mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
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.
274 lines
9.2 KiB
TypeScript
274 lines
9.2 KiB
TypeScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { comboRuntimeConfigSchema } from "@/shared/validation/schemas/combo";
|
|
|
|
// Test the context requirements schema extension against the real
|
|
// comboRuntimeConfigSchema export, so this test catches drift if the
|
|
// production schema changes shape.
|
|
describe("Combo Context Requirements", () => {
|
|
describe("Schema Validation", () => {
|
|
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 } },
|
|
{ contextRequirements: {} },
|
|
{},
|
|
];
|
|
|
|
for (const input of valid) {
|
|
const result = schema.safeParse(input);
|
|
assert.ok(result.success, `Should accept ${JSON.stringify(input)}`);
|
|
}
|
|
});
|
|
|
|
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) {
|
|
const result = schema.safeParse(input);
|
|
assert.ok(!result.success, `Should reject ${JSON.stringify(input)}`);
|
|
}
|
|
});
|
|
|
|
it("should accept valid preferLargeContext boolean", () => {
|
|
const schema = comboRuntimeConfigSchema;
|
|
|
|
const valid = [
|
|
{ contextRequirements: { preferLargeContext: true } },
|
|
{ contextRequirements: { preferLargeContext: false } },
|
|
{ contextRequirements: {} },
|
|
];
|
|
|
|
for (const input of valid) {
|
|
const result = schema.safeParse(input);
|
|
assert.ok(result.success, `Should accept ${JSON.stringify(input)}`);
|
|
}
|
|
});
|
|
|
|
it("should accept valid contextFilterMode", () => {
|
|
const schema = comboRuntimeConfigSchema;
|
|
|
|
const valid = [
|
|
{ contextRequirements: { contextFilterMode: "strict" } },
|
|
{ contextRequirements: { contextFilterMode: "lenient" } },
|
|
{ contextRequirements: {} },
|
|
];
|
|
|
|
for (const input of valid) {
|
|
const result = schema.safeParse(input);
|
|
assert.ok(result.success, `Should accept ${JSON.stringify(input)}`);
|
|
}
|
|
});
|
|
|
|
it("should reject invalid contextFilterMode", () => {
|
|
const schema = comboRuntimeConfigSchema;
|
|
|
|
const invalid = [
|
|
{ contextRequirements: { contextFilterMode: "invalid" } },
|
|
{ contextRequirements: { contextFilterMode: "permissive" } },
|
|
];
|
|
|
|
for (const input of invalid) {
|
|
const result = schema.safeParse(input);
|
|
assert.ok(!result.success, `Should reject ${JSON.stringify(input)}`);
|
|
}
|
|
});
|
|
|
|
it("should accept combined context requirements", () => {
|
|
const schema = comboRuntimeConfigSchema;
|
|
|
|
const input = {
|
|
contextRequirements: {
|
|
minContextWindow: 32000,
|
|
preferLargeContext: true,
|
|
contextFilterMode: "strict" as const,
|
|
},
|
|
};
|
|
|
|
const result = schema.safeParse(input);
|
|
assert.ok(result.success);
|
|
if (result.success) {
|
|
assert.deepEqual(result.data, input);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Context Filtering Logic", () => {
|
|
it("should filter targets below minContextWindow in strict mode", () => {
|
|
const targets = [
|
|
{ model: "gpt-3.5-turbo", contextWindow: 4096 },
|
|
{ model: "gpt-4", contextWindow: 8192 },
|
|
{ model: "gpt-4-turbo", contextWindow: 128000 },
|
|
{ model: "claude-3-opus", contextWindow: 200000 },
|
|
];
|
|
|
|
const minContextWindow = 32000;
|
|
const contextFilterMode = "strict";
|
|
|
|
const filtered = targets.filter((t) => {
|
|
const limit = t.contextWindow ?? null;
|
|
if (limit === null) {
|
|
// Unknown limits fail in strict mode
|
|
return contextFilterMode === "lenient";
|
|
}
|
|
return limit >= minContextWindow;
|
|
});
|
|
|
|
assert.equal(filtered.length, 2);
|
|
assert.equal(filtered[0].model, "gpt-4-turbo");
|
|
assert.equal(filtered[1].model, "claude-3-opus");
|
|
});
|
|
|
|
it("should include unknown context limits in lenient mode", () => {
|
|
const targets = [
|
|
{ model: "gpt-4", contextWindow: 8192 },
|
|
{ model: "custom-model", contextWindow: null },
|
|
{ model: "claude-3-opus", contextWindow: 200000 },
|
|
];
|
|
|
|
const minContextWindow = 32000;
|
|
const contextFilterMode = "lenient";
|
|
|
|
const filtered = targets.filter((t) => {
|
|
const limit = t.contextWindow ?? null;
|
|
if (limit === null) {
|
|
return contextFilterMode === "lenient";
|
|
}
|
|
return limit >= minContextWindow;
|
|
});
|
|
|
|
assert.equal(filtered.length, 2);
|
|
assert.equal(filtered[0].model, "custom-model");
|
|
assert.equal(filtered[1].model, "claude-3-opus");
|
|
});
|
|
|
|
it("should exclude unknown context limits in strict mode", () => {
|
|
const targets = [
|
|
{ model: "gpt-4", contextWindow: 8192 },
|
|
{ model: "custom-model", contextWindow: null },
|
|
{ model: "claude-3-opus", contextWindow: 200000 },
|
|
];
|
|
|
|
const minContextWindow = 32000;
|
|
const contextFilterMode = "strict";
|
|
|
|
const filtered = targets.filter((t) => {
|
|
const limit = t.contextWindow ?? null;
|
|
if (limit === null) {
|
|
return contextFilterMode === "lenient";
|
|
}
|
|
return limit >= minContextWindow;
|
|
});
|
|
|
|
assert.equal(filtered.length, 1);
|
|
assert.equal(filtered[0].model, "claude-3-opus");
|
|
});
|
|
|
|
it("should sort by context size when preferLargeContext is enabled", () => {
|
|
const targets = [
|
|
{ model: "gpt-4", contextWindow: 8192 },
|
|
{ model: "claude-3-opus", contextWindow: 200000 },
|
|
{ model: "gpt-4-turbo", contextWindow: 128000 },
|
|
{ model: "gemini-pro", contextWindow: 1000000 },
|
|
];
|
|
|
|
const preferLargeContext = true;
|
|
|
|
const sorted = preferLargeContext
|
|
? [...targets].sort((a, b) => {
|
|
const aLimit = a.contextWindow ?? 0;
|
|
const bLimit = b.contextWindow ?? 0;
|
|
return bLimit - aLimit; // Descending
|
|
})
|
|
: targets;
|
|
|
|
assert.equal(sorted[0].model, "gemini-pro");
|
|
assert.equal(sorted[1].model, "claude-3-opus");
|
|
assert.equal(sorted[2].model, "gpt-4-turbo");
|
|
assert.equal(sorted[3].model, "gpt-4");
|
|
});
|
|
|
|
it("should not sort when preferLargeContext is disabled", () => {
|
|
const targets = [
|
|
{ model: "gpt-4", contextWindow: 8192 },
|
|
{ model: "claude-3-opus", contextWindow: 200000 },
|
|
{ model: "gpt-4-turbo", contextWindow: 128000 },
|
|
];
|
|
|
|
const preferLargeContext = false;
|
|
|
|
const sorted = preferLargeContext
|
|
? [...targets].sort((a, b) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0))
|
|
: targets;
|
|
|
|
assert.equal(sorted[0].model, "gpt-4");
|
|
assert.equal(sorted[1].model, "claude-3-opus");
|
|
assert.equal(sorted[2].model, "gpt-4-turbo");
|
|
});
|
|
});
|
|
|
|
describe("Integration with Existing Context Filtering", () => {
|
|
it("should work with existing filterTargetsByRequestCompatibility logic", () => {
|
|
// This test verifies the new config integrates with existing code
|
|
// The actual implementation in comboStructure.ts already has context filtering
|
|
// We just need to ensure our new config fields are respected
|
|
|
|
const config = {
|
|
contextRequirements: {
|
|
minContextWindow: 32000,
|
|
preferLargeContext: true,
|
|
contextFilterMode: "strict" as const,
|
|
},
|
|
};
|
|
|
|
const targets = [
|
|
{ model: "small-model", contextWindow: 4096 },
|
|
{ model: "medium-model", contextWindow: 32000 },
|
|
{ model: "large-model", contextWindow: 200000 },
|
|
{ model: "unknown-model", contextWindow: null },
|
|
];
|
|
|
|
// Step 1: Filter by minContextWindow
|
|
let filtered = targets.filter((t) => {
|
|
const limit = t.contextWindow ?? null;
|
|
if (config.contextRequirements.minContextWindow) {
|
|
if (limit === null) {
|
|
return config.contextRequirements.contextFilterMode === "lenient";
|
|
}
|
|
return limit >= config.contextRequirements.minContextWindow;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
assert.equal(filtered.length, 2);
|
|
|
|
// Step 2: Sort by context size if preferLargeContext
|
|
if (config.contextRequirements.preferLargeContext) {
|
|
filtered.sort((a, b) => {
|
|
const aLimit = a.contextWindow ?? 0;
|
|
const bLimit = b.contextWindow ?? 0;
|
|
return bLimit - aLimit;
|
|
});
|
|
}
|
|
|
|
assert.equal(filtered[0].model, "large-model");
|
|
assert.equal(filtered[1].model, "medium-model");
|
|
});
|
|
});
|
|
});
|