Files
OmniRoute/tests/unit/combo/context-requirements-integration.test.ts
Paijo 10cb2447b1 [trim] feat(combo): add context requirements config for target filtering (#6907)
* feat(combo): add context requirements config for target filtering

Add contextRequirements config field to combo runtime config:
- minContextWindow: filter models below threshold (0-10M tokens)
- preferLargeContext: sort targets by context size descending
- contextFilterMode: 'strict' excludes unknown limits, 'lenient' includes them

Implementation:
- Added Zod schema validation in combo.ts
- Created contextRequirements.ts module with applyContextRequirements()
- Integrated filtering after filterTargetsByRequestCompatibility()
- Full test coverage with unit + integration tests

Tests: 17/17 pass (combo-context-requirements.test.ts + integration)

* feat(combo): add ContextRequirementsEditor UI component

Add standalone React component for editing context requirements config:
- Slider for minContextWindow (0 to 1M tokens) with presets
- Toggle for preferLargeContext sorting
- Radio group for contextFilterMode (strict/lenient)
- Tooltips explaining each option
- Active filters summary display

Component features:
- Shadcn UI components (Card, Slider, Switch, RadioGroup)
- Preset buttons for common context sizes (8K, 32K, 128K, 1M)
- Conditional display of filter mode when minContextWindow > 0
- Clear visual feedback of active filters

Integration:
Import and use in combo config form where other config fields
like fusionTuning and judgeModel are edited. Pass combo.config.contextRequirements
as value prop and update on onChange.

Example usage:
<ContextRequirementsEditor
  value={config.contextRequirements}
  onChange={(val) => updateConfig({ contextRequirements: val })}
/>

UI matches existing combo config editor patterns.

* feat(combo): wire ContextRequirementsEditor into combo config form

Adds context requirements section to combo edit page (strategy section),
matching existing ResponseValidation pattern. Placed after response
validation block, before agent features.

* docs(combo): add context requirements feature documentation

Covers: config schema, behavior, use cases, UI integration,
troubleshooting, and test instructions.

* fix(combo): pass provider+modelStr to getModelContextLimit for accurate context resolution

Per gemini-code-assist review feedback: model names are not globally
unique across providers. Passing both provider and modelStr ensures
correct context limit resolution in applyContextRequirements().

* fix(combo): repair broken doc links and restore test:unit:fast flag

- Point docs/combo-context-requirements.md 'Related' links at real docs
  (routing/AUTO-COMBO.md, architecture/RESILIENCE_GUIDE.md) — the three
  placeholder links (strategies.md/model-capabilities.md/fusion-tuning.md)
  did not exist and failed check:doc-links (Docs Gates fast-path).
- Revert an out-of-scope package.json change to test:unit:fast that dropped
  --test-isolation=none; restore to match release/v3.8.47.

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>

* test(combo): validate context requirements against the real Zod schema

Point tests/unit/combo-context-requirements.test.ts at the real
comboRuntimeConfigSchema export (src/shared/validation/schemas/combo.ts)
instead of hand-duplicating the Zod schema inline, so the test catches
schema drift.

Also declare contextRequirements on DEFAULT_COMBO_CONFIG so
resolveComboSetupConfig's inferred return type includes the key —
combo.ts reads config.contextRequirements but the property was missing
from the object typecheck:core infers types from, causing a build error.

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>

* fix(combos): remove dead ContextRequirementsEditor scaffolding (broken ui/card+label imports)

The editor imported @/components/ui/card and @/components/ui/label which do not
exist in the repo, breaking the Turbopack build. Removed the editor + its page.tsx
usage + doc mention; the real fix (comboConfig contextRequirements default + test)
is preserved.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
2026-07-12 03:41:50 -03:00

102 lines
3.4 KiB
TypeScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { applyContextRequirements } from "../../../open-sse/services/combo/contextRequirements";
import type { ResolvedComboTarget } from "../../../open-sse/services/combo/types";
// Mock logger
const mockLog = {
info: () => {},
debug: () => {},
warn: () => {},
error: () => {},
};
describe("Context Requirements Integration", () => {
it("should filter and sort targets with full context requirements", () => {
const targets = [
{ modelStr: "gpt-3.5-turbo", provider: "openai", weight: 1 },
{ modelStr: "gpt-4", provider: "openai", weight: 1 },
{ modelStr: "gpt-4-turbo", provider: "openai", weight: 1 },
{ modelStr: "claude-3-opus-20240229", provider: "anthropic", weight: 1 },
{ modelStr: "gemini-1.5-pro", provider: "google", weight: 1 },
];
const requirements = {
minContextWindow: 100000,
preferLargeContext: true,
contextFilterMode: "strict" as const,
};
const result = applyContextRequirements(targets, requirements, mockLog);
// Should filter out models with <100k context and, in strict mode, also drop
// models whose context window is unknown in the current catalog snapshot.
assert.ok(result.length < targets.length);
assert.ok(
result.every((target) => targets.some((original) => original.modelStr === target.modelStr)),
"Filtered targets should be a subset of the original list"
);
});
it("should not filter when no requirements specified", () => {
const targets = [
{ modelStr: "gpt-3.5-turbo", provider: "openai", weight: 1 },
{ modelStr: "gpt-4", provider: "openai", weight: 1 },
];
const result = applyContextRequirements(targets, undefined, mockLog);
assert.equal(result.length, targets.length);
assert.equal(result, targets); // Same reference
});
it("should handle empty targets array", () => {
const targets: ResolvedComboTarget[] = [];
const requirements = {
minContextWindow: 32000,
preferLargeContext: true,
};
const result = applyContextRequirements(targets, requirements, mockLog);
assert.equal(result.length, 0);
});
it("should handle lenient mode with unknown context models", () => {
const targets = [
{ modelStr: "gpt-4", provider: "openai", weight: 1 },
{ modelStr: "unknown-model", provider: "custom", weight: 1 },
];
const requirements = {
minContextWindow: 32000,
contextFilterMode: "lenient" as const,
};
const result = applyContextRequirements(targets, requirements, mockLog);
// Should include unknown-model in lenient mode
assert.ok(
result.some((t) => t.modelStr === "unknown-model"),
"Should include unknown model in lenient mode"
);
});
it("should handle strict mode with unknown context models", () => {
const targets = [
{ modelStr: "claude-3-opus-20240229", provider: "anthropic", weight: 1 },
{ modelStr: "unknown-model", provider: "custom", weight: 1 },
];
const requirements = {
minContextWindow: 32000,
contextFilterMode: "strict" as const,
};
const result = applyContextRequirements(targets, requirements, mockLog);
// Should exclude unknown-model in strict mode
assert.ok(
!result.some((t) => t.modelStr === "unknown-model"),
"Should exclude unknown model in strict mode"
);
});
});