fix(combo): restore routing module load

This commit is contained in:
alexey.nazarov@softmg.ru
2026-08-06 18:02:41 +03:00
parent 7f36b192f0
commit b106fe7ac4
5 changed files with 53 additions and 28 deletions

View File

@@ -56,7 +56,11 @@ function capabilityEntry(limitContext: number | null) {
};
}
function capabilityEntryWithLimits(limitInput: number | null, limitContext: number | null, limitOutput = 4096) {
function capabilityEntryWithLimits(
limitInput: number | null,
limitContext: number | null,
limitOutput = 4096
) {
return {
...capabilityEntry(limitContext),
limit_input: limitInput,
@@ -188,6 +192,26 @@ test("all known-too-small context targets still fall back to strategy order", ()
);
});
test("output-token limits remain a hard compatibility requirement", () => {
saveModelsDevCapabilities({
"unit-output-limit": {
insufficient: capabilityEntryWithLimits(128_000, 128_000, 128),
sufficient: capabilityEntryWithLimits(128_000, 128_000, 4_096),
},
});
const out = filterTargetsByRequestCompatibility(
[target("unit-output-limit/insufficient"), target("unit-output-limit/sufficient")],
{ messages: [{ role: "user", content: "hello" }], max_tokens: 512 },
noopLog
);
assert.deepEqual(
out.map((entry) => entry.modelStr),
["unit-output-limit/sufficient"]
);
});
test("known context overflow reports the largest target limit", () => {
saveModelsDevCapabilities({
"unit-known-context": {
@@ -325,10 +349,10 @@ test("small input-only maxInputTokens keeps a target whose input fits even thoug
);
});
test("input-only maxInputTokens still rejects when the input itself exceeds the cap", () => {
// The fix must not let a genuinely-too-small input cap pass. `too-small` has
// maxInputTokens = 1, which cannot even hold the ~11-token input, so it must
// still be dropped while the compatible target survives.
test("input-only maxInputTokens keeps an oversized target as runtime fallback", () => {
// Context metadata is advisory. `too-small` has maxInputTokens = 1, so the
// known-compatible target is preferred while the catalog-too-small target
// remains available for runtime fallback.
saveModelsDevCapabilities({
"unit-7039-too-small": {
"too-small": capabilityEntryWithLimits(1, 1_000_000, 500),
@@ -344,14 +368,14 @@ test("input-only maxInputTokens still rejects when the input itself exceeds the
assert.deepEqual(
out.map((entry) => entry.modelStr),
["unit-7039-too-small/huge"]
["unit-7039-too-small/huge", "unit-7039-too-small/too-small"]
);
});
test("maxInputTokens defaulting to contextWindow still rejects when input + output exceeds the total window (#7039 follow-up)", () => {
test("shared-window overflow keeps the target as runtime fallback (#7039 follow-up)", () => {
// Shared-window model where maxInputTokens equals the total window size.
// The input alone fits the input cap, but input + output overflows the
// window, so the target must be rejected instead of passing on the input cap.
// The input alone fits the input cap, but input + output exceeds the catalog
// window. Prefer the known-compatible target without removing the fallback.
saveModelsDevCapabilities({
"unit-7039-window": {
"shared-window": capabilityEntryWithLimits(400_000, 400_000, 200_000),
@@ -367,7 +391,7 @@ test("maxInputTokens defaulting to contextWindow still rejects when input + outp
assert.deepEqual(
out.map((entry) => entry.modelStr),
["unit-7039-window/huge"]
["unit-7039-window/huge", "unit-7039-window/shared-window"]
);
});
@@ -391,24 +415,24 @@ test("model_context_override lets a small-catalog target survive a large-context
largeContextBody(),
noopLog
);
assert.deepEqual(
out.map((entry) => entry.modelStr).sort(),
["unit-override/big", "unit-override/capped"]
);
assert.deepEqual(out.map((entry) => entry.modelStr).sort(), [
"unit-override/big",
"unit-override/capped",
]);
} finally {
removeModelContextOverride("unit-override", "capped");
}
});
test("without an override the small-catalog target is still dropped for the large request", () => {
test("without an override the small-catalog target remains ordered behind a compatible target", () => {
saveModelsDevCapabilities({
"unit-override": {
big: capabilityEntry(1_000_000),
capped: capabilityEntry(8_000),
},
});
// No override: capped (8K) is genuinely too small and must be filtered out,
// guarding the override read-path from masking a real too-small target.
// No override: capped (8K) is catalog-too-small, so it stays behind the
// known-compatible target while remaining available as a runtime fallback.
const out = filterTargetsByRequestCompatibility(
[target("unit-override/capped"), target("unit-override/big")],
largeContextBody(),
@@ -417,6 +441,6 @@ test("without an override the small-catalog target is still dropped for the larg
assert.deepEqual(
out.map((entry) => entry.modelStr),
["unit-override/big"]
["unit-override/big", "unit-override/capped"]
);
});

View File

@@ -0,0 +1,9 @@
import test from "node:test";
import assert from "node:assert/strict";
test("combo routing module loads without unresolved symbols", async () => {
const combo = await import("../../open-sse/services/combo.ts");
assert.equal(typeof combo.filterTargetsByRequestCompatibility, "function");
assert.equal(typeof combo.handleComboChat, "function");
});