Files
OmniRoute/tests/unit/vps-runner-variable-scope.test.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

83 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Guard for what `USE_VPS_RUNNER` is allowed to govern (gap 19).
*
* One variable used to switch the build AND the test jobs, which want OPPOSITE machines. The
* build needs the .113's RAM — the production `next build` wants 1820 GB and a hosted runner
* has 16, which killed the same step nine times in the v3.8.49 cycle. The test jobs need the
* hosted runner's link:
*
* actions/setup-node on .113, 4 concurrent runners ..... 20m06s
* actions/setup-node hosted ............................ 16s
* the tests themselves .................. 2m54 vs 2m31 — a tie
*
* So there was no setting that served both, and flipping the variable for a release traded a
* working build for twenty-minute test jobs.
*
* The fix is not a second variable. Self-hosted is strictly worse for anything whose cost is
* dominated by `setup-node` + `npm ci`, so those jobs are pinned to hosted and the variable now
* means exactly one thing: "this job needs the .113's memory".
*
* `fast-gates` was the last holdout and the evidence for pinning it is unusually clean: across
* 160 quality.yml runs it NEVER landed on a self-hosted runner — every non-skipped sample is
* `GitHub Actions NNNN`. That was dead configuration, not a working escape hatch, and the
* classifier is not at fault: ci.yml's Build ran on omniroute-113-7 and -6 in the same window.
*/
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const workflowDir = path.join(repoRoot, ".github/workflows");
/** Map every `runs-on:` line mentioning USE_VPS_RUNNER back to the job that owns it. */
function jobsSwitchedByVpsVar(): Array<{ file: string; job: string }> {
const out: Array<{ file: string; job: string }> = [];
for (const name of fs.readdirSync(workflowDir)) {
if (!name.endsWith(".yml") && !name.endsWith(".yaml")) continue;
const lines = fs.readFileSync(path.join(workflowDir, name), "utf-8").split("\n");
let job = "";
for (const line of lines) {
const m = /^ {2}([A-Za-z0-9_-]+):\s*$/.exec(line);
if (m) job = m[1];
if (line.includes("USE_VPS_RUNNER") && line.includes("runs-on")) {
out.push({ file: name, job });
}
}
}
return out;
}
/** Jobs whose cost is dominated by setup-node + npm ci, never by memory. */
const TEST_LIKE = /(^|-)(test|tests|vitest|unit|gates)($|-)/i;
test("USE_VPS_RUNNER never governs a test-like job", () => {
const offenders = jobsSwitchedByVpsVar().filter((j) => TEST_LIKE.test(j.job));
assert.deepEqual(
offenders.map((j) => `${j.file}:${j.job}`),
[],
"self-hosted is strictly worse for these — setup-node measured 20m06s there vs 16s hosted, " +
"while the tests themselves tie. Pin the job to ubuntu-latest instead of switching it."
);
});
test("it still governs the build-like jobs — the ones that actually need the RAM", () => {
const jobs = jobsSwitchedByVpsVar().map((j) => j.job);
assert.ok(
jobs.includes("build"),
"the build is the whole reason the variable exists: 18-20 GB working set vs a 16 GB hosted runner"
);
assert.ok(jobs.includes("publish"), "npm-publish falls back to a full build when the artifact is missing");
});
test("fast-gates specifically stays hosted", () => {
// The holdout. 160 runs, zero self-hosted samples — dead configuration, and it would have
// inherited the setup-node penalty the day it fired.
const wf = fs.readFileSync(path.join(workflowDir, "quality.yml"), "utf-8");
const block = wf.split(/^ {2}fast-gates:\s*$/m)[1] ?? "";
const runsOn = /^ {4}runs-on:\s*(.+)$/m.exec(block);
assert.ok(runsOn, "fast-gates must declare runs-on");
assert.match(runsOn[1].trim(), /^ubuntu-latest$/, "fast-gates must be pinned, not switched");
});