mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 15:22:12 +03:00
Validated in local merge-train T6 (ungrouped batch 1)
This commit is contained in:
@@ -9,6 +9,7 @@ import type { ComboLogger, ResolvedComboTarget } from "./types.ts";
|
||||
|
||||
export interface ContextRequirements {
|
||||
minContextWindow?: number;
|
||||
maxContextWindow?: number;
|
||||
preferLargeContext?: boolean;
|
||||
contextFilterMode?: "strict" | "lenient";
|
||||
}
|
||||
@@ -51,10 +52,15 @@ export function applyContextRequirements(
|
||||
): ResolvedComboTarget[] {
|
||||
if (!requirements || targets.length === 0) return targets;
|
||||
|
||||
const { minContextWindow, preferLargeContext, contextFilterMode = "lenient" } = requirements;
|
||||
const {
|
||||
minContextWindow,
|
||||
maxContextWindow,
|
||||
preferLargeContext,
|
||||
contextFilterMode = "lenient",
|
||||
} = requirements;
|
||||
|
||||
// No requirements specified
|
||||
if (!minContextWindow && !preferLargeContext) return targets;
|
||||
if (!minContextWindow && !maxContextWindow && !preferLargeContext) return targets;
|
||||
|
||||
let filtered = targets;
|
||||
|
||||
@@ -108,6 +114,34 @@ export function applyContextRequirements(
|
||||
}
|
||||
}
|
||||
|
||||
// Apply maxContextWindow filtering
|
||||
if (maxContextWindow && maxContextWindow > 0) {
|
||||
const beforeFilterCount = filtered.length;
|
||||
|
||||
filtered = filtered.filter((target) => {
|
||||
const contextWindow = getTargetContextWindow(target);
|
||||
|
||||
// Unknown context limit handling
|
||||
if (contextWindow === null) {
|
||||
return contextFilterMode === "lenient";
|
||||
}
|
||||
|
||||
// Known context limit - check threshold
|
||||
return contextWindow <= maxContextWindow;
|
||||
});
|
||||
|
||||
if (filtered.length < beforeFilterCount) {
|
||||
log.info(
|
||||
"COMBO",
|
||||
`Context requirements: filtered ${beforeFilterCount} → ${filtered.length} targets (maxContextWindow: ${maxContextWindow}, mode: ${contextFilterMode})`
|
||||
);
|
||||
log.debug?.(
|
||||
"COMBO",
|
||||
`Context requirements: kept models ${filtered.map((t) => t.modelStr).join(", ")}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply preferLargeContext sorting
|
||||
if (preferLargeContext && filtered.length > 1) {
|
||||
filtered = [...filtered].sort((a, b) => {
|
||||
|
||||
@@ -179,6 +179,7 @@ const DEFAULT_COMBO_CONFIG = {
|
||||
contextRequirements: undefined as
|
||||
| {
|
||||
minContextWindow?: number;
|
||||
maxContextWindow?: number;
|
||||
preferLargeContext?: boolean;
|
||||
contextFilterMode?: "strict" | "lenient";
|
||||
}
|
||||
|
||||
@@ -239,11 +239,13 @@ export const comboRuntimeConfigSchema = z
|
||||
.optional(),
|
||||
// Context window requirements for combo target filtering and sorting.
|
||||
// minContextWindow: filters out models with context windows below this threshold.
|
||||
// maxContextWindow: filters out models with context windows above this threshold.
|
||||
// preferLargeContext: sorts remaining targets by context size (descending).
|
||||
// contextFilterMode: "strict" excludes unknown-context models, "lenient" includes them.
|
||||
contextRequirements: z
|
||||
.object({
|
||||
minContextWindow: z.coerce.number().int().min(0).max(10_000_000).optional(),
|
||||
maxContextWindow: z.coerce.number().int().min(0).max(10_000_000).optional(),
|
||||
preferLargeContext: z.boolean().optional(),
|
||||
contextFilterMode: z.enum(["strict", "lenient"]).optional(),
|
||||
})
|
||||
|
||||
@@ -7,11 +7,13 @@ import { comboRuntimeConfigSchema } from "@/shared/validation/schemas/combo";
|
||||
// production schema changes shape.
|
||||
describe("Combo Context Requirements", () => {
|
||||
describe("Schema Validation", () => {
|
||||
it("should accept valid minContextWindow", () => {
|
||||
it("should accept valid minContextWindow and maxContextWindow", () => {
|
||||
const schema = comboRuntimeConfigSchema;
|
||||
|
||||
const valid = [
|
||||
{ contextRequirements: { minContextWindow: 8192 } },
|
||||
{ contextRequirements: { maxContextWindow: 32000 } },
|
||||
{ contextRequirements: { minContextWindow: 8192, maxContextWindow: 128000 } },
|
||||
{ contextRequirements: { minContextWindow: 32000 } },
|
||||
{ contextRequirements: { minContextWindow: 128000 } },
|
||||
{ contextRequirements: { minContextWindow: 1000000 } },
|
||||
@@ -25,13 +27,16 @@ describe("Combo Context Requirements", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("should reject invalid minContextWindow", () => {
|
||||
it("should reject invalid minContextWindow or maxContextWindow", () => {
|
||||
const schema = comboRuntimeConfigSchema;
|
||||
|
||||
const invalid = [
|
||||
{ contextRequirements: { minContextWindow: -1 } },
|
||||
{ contextRequirements: { minContextWindow: 20_000_000 } },
|
||||
{ contextRequirements: { minContextWindow: "invalid" } },
|
||||
{ contextRequirements: { maxContextWindow: -1 } },
|
||||
{ contextRequirements: { maxContextWindow: 20_000_000 } },
|
||||
{ contextRequirements: { maxContextWindow: "invalid" } },
|
||||
];
|
||||
|
||||
for (const input of invalid) {
|
||||
|
||||
@@ -146,3 +146,43 @@ test("request exceeding every known context window returns a 400 earlyResponse",
|
||||
assert.equal(body.diagnostics?.terminalReason, "context_length_exceeded");
|
||||
assert.equal(body.diagnostics?.attempted, 0);
|
||||
});
|
||||
|
||||
// #8790: maxContextWindow rejects every target whose known context window
|
||||
// exceeds the configured ceiling. When that empties the pool, the
|
||||
// context-requirements guard (applyContinuityFilters → #8786's
|
||||
// buildEmptyComboTargetsPayload) must surface a 404 context_requirements_exhausted
|
||||
// early response instead of letting an empty orderedTargets[] fall through to the
|
||||
// attempt loop.
|
||||
test("maxContextWindow rejecting every target returns a 404 context_requirements_exhausted earlyResponse", async () => {
|
||||
saveModelsDevCapabilities({
|
||||
"unit-target-resolution-max": {
|
||||
big1: capabilityEntry(500_000),
|
||||
big2: capabilityEntry(1_000_000),
|
||||
},
|
||||
});
|
||||
|
||||
const result = await resolveComboTargetPipeline(
|
||||
deps({
|
||||
combo: {
|
||||
id: "c3",
|
||||
name: "max-context-window-exhausted",
|
||||
models: ["unit-target-resolution-max/big1", "unit-target-resolution-max/big2"],
|
||||
config: {},
|
||||
},
|
||||
config: {
|
||||
contextRequirements: { maxContextWindow: 128_000, contextFilterMode: "strict" },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.ok("earlyResponse" in result, "expected a context-requirements-exhausted early response");
|
||||
if (!("earlyResponse" in result)) return;
|
||||
assert.equal(result.earlyResponse.status, 404);
|
||||
const body = (await result.earlyResponse.json()) as {
|
||||
error?: { code?: string };
|
||||
diagnostics?: { terminalReason?: string; excluded?: unknown[] };
|
||||
};
|
||||
assert.equal(body.error?.code, "model_not_found");
|
||||
assert.equal(body.diagnostics?.terminalReason, "context_requirements_exhausted");
|
||||
assert.equal(body.diagnostics?.excluded?.length, 2);
|
||||
});
|
||||
|
||||
@@ -65,6 +65,29 @@ describe("Context Requirements Integration", () => {
|
||||
assert.equal(result.length, 0);
|
||||
});
|
||||
|
||||
it("should filter targets above maxContextWindow in strict mode", () => {
|
||||
const targets = [
|
||||
{ modelStr: "claude-opus-4-5", provider: "anthropic", weight: 1 }, // 200k
|
||||
{ modelStr: "claude-sonnet-4-5", provider: "anthropic", weight: 1 }, // 200k
|
||||
{ modelStr: "claude-sonnet-4-6", provider: "anthropic", weight: 1 }, // 1000k (1M)
|
||||
{ modelStr: "claude-opus-4-6", provider: "anthropic", weight: 1 }, // 1000k (1M)
|
||||
];
|
||||
|
||||
const requirements = {
|
||||
maxContextWindow: 500000,
|
||||
contextFilterMode: "strict" as const,
|
||||
};
|
||||
|
||||
const result = applyContextRequirements(targets, requirements, mockLog);
|
||||
assert.equal(result.length, 2);
|
||||
assert.ok(
|
||||
result.every(
|
||||
(target) => target.modelStr === "claude-opus-4-5" || target.modelStr === "claude-sonnet-4-5"
|
||||
),
|
||||
"Should only include targets with context window <= 500000"
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle lenient mode with unknown context models", () => {
|
||||
const targets = [
|
||||
{ modelStr: "gpt-4o", provider: "openai", weight: 1 },
|
||||
|
||||
Reference in New Issue
Block a user