From 714a315a1a6d8af2a5406161a118d1357d8621bb Mon Sep 17 00:00:00 2001 From: Jan Leon Date: Thu, 6 Aug 2026 16:07:56 +0200 Subject: [PATCH] Treat context metadata as a routing hint (#8944) Validated in post-merge-train sweep (boards clean on release/v3.8.50 tip) --- open-sse/services/combo/comboStructure.ts | 79 ++++++------------- .../unit/combo-context-window-filter.test.ts | 14 ++-- 2 files changed, 32 insertions(+), 61 deletions(-) diff --git a/open-sse/services/combo/comboStructure.ts b/open-sse/services/combo/comboStructure.ts index fa711c6b8c..58a8b99538 100644 --- a/open-sse/services/combo/comboStructure.ts +++ b/open-sse/services/combo/comboStructure.ts @@ -8,6 +8,8 @@ * getComboModelsFromData, validateComboDAG, resolveNestedComboModels, * filterTargetsByRequestCompatibility) are re-exported from combo.ts for the * ~20 external consumers (chatCore.ts, the /api/combos routes, embeddings, etc.). + * Context-window metadata is advisory: known-fitting targets are ordered first, + * while catalog-too-small targets remain available for runtime fallback. * No barrel import — depends only on sibling leaves. */ @@ -531,9 +533,7 @@ function hasKnownCompatibleContextLimit( return evaluateContextLimit(capabilities, requirements, target.modelStr) === true; } -function hasOnlyContextWindowFailures(reasons: string[]): boolean { - return reasons.length > 0 && reasons.every((reason) => reason === "context_window"); -} +const HARD_COMPAT_REASONS = new Set(["tools", "vision", "structured_output", "output_tokens"]); /** * #8332: vision is a hard requirement, not a soft preference — a target whose vision @@ -694,67 +694,34 @@ export function filterTargetsByRequestCompatibility( if (!needsFiltering) return targets; const rejected: Array<{ target: ResolvedComboTarget; reasons: string[] }> = []; - const compatible = targets.filter((target) => { + const targetReasons = new Map(); + for (const target of targets) { const reasons = getTargetCompatibilityFailures(target, requirements); - if (reasons.length === 0) return true; - rejected.push({ target, reasons }); - return false; - }); + targetReasons.set(target, reasons); + if (reasons.length > 0) rejected.push({ target, reasons }); + } - // Unknown context limits are safe only as a fallback. If this request already - // filtered at least one known-too-small target and known-good targets remain, - // prefer the known-good set over unknown metadata gaps. If no known-good - // context target remains, fall back to the strategy order for context-only - // candidates instead of letting unknown metadata be the only survivors. - const rejectedForContextWindow = rejected.some((entry) => + // Context metadata is advisory. Keep every target that has no hard capability + // mismatch, but prefer targets whose known limit fits. A stale catalog entry must + // never remove the only target that could accept the request at runtime. + const compatible = targets.filter((target) => { + const reasons = targetReasons.get(target) || []; + return !reasons.some((reason) => HARD_COMPAT_REASONS.has(reason)); + }); + const hadKnownTooSmallContextTarget = rejected.some((entry) => entry.reasons.includes("context_window") ); - if (requirements.requiredContextTokens > 0 && rejectedForContextWindow) { + if ( + requirements.requiredContextTokens > 0 && + hadKnownTooSmallContextTarget && + compatible.length > 1 + ) { const knownContextCompatible = compatible.filter((target) => hasKnownCompatibleContextLimit(target, requirements) ); - if (knownContextCompatible.length > 0 && knownContextCompatible.length < compatible.length) { - const knownContextCompatibleTargets = new Set(knownContextCompatible); - for (const target of compatible) { - if (!knownContextCompatibleTargets.has(target)) { - rejected.push({ target, reasons: ["context_window_unknown"] }); - } - } - - log.info( - "COMBO", - `${label}: kept ${knownContextCompatible.length}/${targets.length} targets for request requirements` - ); - log.debug?.( - "COMBO", - `${label}: rejected targets ${rejected - .map((entry) => `${entry.target.modelStr}(${entry.reasons.join("+")})`) - .join(", ")}` - ); - return knownContextCompatible; - } - - if (knownContextCompatible.length === 0 && compatible.length > 0) { - const rejectedByTarget = new Map(rejected.map((entry) => [entry.target, entry.reasons])); - const contextOnlyFallback = targets.filter((target) => { - const reasons = rejectedByTarget.get(target); - return !reasons || hasOnlyContextWindowFailures(reasons); - }); - - if (contextOnlyFallback.length > compatible.length) { - log.warn( - "COMBO", - `${label}: no known-compatible context target remains; preserving strategy order for context-only candidates` - ); - log.debug?.( - "COMBO", - `${label}: rejected targets ${rejected - .map((entry) => `${entry.target.modelStr}(${entry.reasons.join("+")})`) - .join(", ")}` - ); - return contextOnlyFallback; - } + const knownSet = new Set(knownContextCompatible); + return [...knownContextCompatible, ...compatible.filter((target) => !knownSet.has(target))]; } } diff --git a/tests/unit/combo-context-window-filter.test.ts b/tests/unit/combo-context-window-filter.test.ts index 12214ce1ba..f20ce67522 100644 --- a/tests/unit/combo-context-window-filter.test.ts +++ b/tests/unit/combo-context-window-filter.test.ts @@ -5,9 +5,8 @@ import os from "node:os"; import path from "node:path"; // Regression tests for the context-aware combo compatibility filter. -// Unknown context metadata is only safe as a fallback. Once the context filter -// has rejected known-too-small targets and a known-capacity target remains, -// unknown-context targets must not survive over it. +// Context metadata is advisory: known-fitting targets are preferred, while +// unknown and catalog-too-small targets remain available for runtime fallback. const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-combo-context-filter-")); const ORIGINAL_DATA_DIR = process.env.DATA_DIR; @@ -97,7 +96,7 @@ function bigContextBody(tokens: number) { const noopLog = { info() {}, warn() {}, error() {}, debug() {} }; -test("known compatible context target wins over unknown-context targets", () => { +test("known compatible context target is preferred while unknown targets remain fallback", () => { saveModelsDevCapabilities({ "unit-known-context": { tiny: capabilityEntry(8_000), @@ -118,7 +117,12 @@ test("known compatible context target wins over unknown-context targets", () => assert.deepEqual( out.map((entry) => entry.modelStr), - ["unit-known-context/million"] + [ + "unit-known-context/million", + "unit-unknown-context/mystery-a", + "unit-known-context/tiny", + "unit-unknown-context/mystery-b", + ] ); });