Files
OmniRoute/tests/unit/complexity-router.test.ts
Dizzle d88fc2bce7 fix(routing): table pricing with catalog fallback for off-table models, pooled latency bootstrap, fresh tier cache (#12792)
Um modelo grátis fora da tabela herdando $5/$15 por milhão e afundando no roteamento cost-aware é o defeito mais caro desta onda: silencioso, e inverte exatamente a decisão que o operador quer.

Parar de chutar 1500ms de latência para modelo desconhecido e usar a mediana observada do pool — com contador de quantas vezes o chute dispara — é trocar heurística por medição do jeito certo. O contador é o que permite saber se valeu.

Revalidei após reconstruir a branch sobre o tip: **33/33** nas suítes da PR, typecheck:core limpo, `check-api-typecheck` OK (289).

**Duas integrações:**

1. `computeSnapshotWeights` conflitou com o #12794 (health via breaker + quality), já mergeado. Os dois compõem e ambos ficaram: o seu termo de `reliability` — que era a única chave que o caminho de snapshot ainda ignorava — mais o health observado e o quality do #12794.
2. `scripts/quality/run-all-gates.mjs` conflitou com o `check:provider-order-sync` do #12790. Aditivo, os dois gates coexistem.

**Nota de dívida:** o `virtualFactory.ts` cruzou o teto de 1200 linhas pela primeira vez (1187 → 1207) somando esta onda. Congelei em vez de dividir e registrei os dois candidatos a extração na justificativa — `computeSnapshotWeights` (~85 linhas) e o grupo de elegibilidade de credencial (~70). Qualquer um dos dois volta o arquivo para baixo do cap.
2026-09-10 09:20:18 -03:00

108 lines
3.9 KiB
TypeScript

/**
* tests/unit/complexity-router.test.ts
*
* 2026 strategy: request-complexity classification → recommended tier, with an
* explicit tool-use escalation. Validates the classifier facade over the
* existing specificity detector.
*/
import test from "node:test";
import assert from "node:assert/strict";
import {
classifyRequestComplexity,
escalateTier,
buildComplexityRoutingHint,
} from "../../open-sse/services/autoCombo/complexityRouter.ts";
const NOOP_LOG = { info: () => {} };
function modelTargets(): Parameters<typeof buildComplexityRoutingHint>[0] {
return [
{
kind: "model",
provider: "openai",
model: "gpt-4o-mini",
modelStr: "openai/gpt-4o-mini",
executionKey: "k1",
stepId: "s1",
},
] as unknown as Parameters<typeof buildComplexityRoutingHint>[0];
}
test("escalateTier — raises to the floor, never lowers", () => {
assert.equal(escalateTier("free", "cheap"), "cheap");
assert.equal(escalateTier("free", "premium"), "premium");
assert.equal(escalateTier("premium", "cheap"), "premium");
assert.equal(escalateTier("cheap", "free"), "cheap");
assert.equal(escalateTier("cheap", "cheap"), "cheap");
});
test("classifyRequestComplexity — a trivial prompt stays cheap/free with no tool signal", () => {
const c = classifyRequestComplexity({ messages: [{ role: "user", content: "hi there" }] });
assert.equal(c.hasToolUse, false);
assert.equal(c.recommendedTier, "free");
assert.ok(["trivial", "simple"].includes(c.level), `expected low level, got ${c.level}`);
});
test("classifyRequestComplexity — a hard, multi-step coding+reasoning prompt scores higher", () => {
const trivial = classifyRequestComplexity({ messages: [{ role: "user", content: "hi" }] });
const hard = classifyRequestComplexity({
messages: [
{
role: "user",
content:
"First, analyze this TypeScript module for race conditions:\n" +
"```ts\nasync function f(){ /* ... */ }\n```\n" +
"Then, step by step, prove the time complexity is O(n log n), " +
"derive the recurrence relation, and refactor it to remove the data race. " +
"Finally, explain the trade-offs of each approach in depth.",
},
],
});
assert.ok(hard.score > trivial.score, `hard (${hard.score}) must exceed trivial (${trivial.score})`);
});
test("classifyRequestComplexity — tool schemas escalate the tier above free", () => {
const c = classifyRequestComplexity({
messages: [{ role: "user", content: "weather?" }],
tools: [
{
function: {
name: "get_weather",
description: "Get the weather",
parameters: { type: "object", properties: { city: { type: "string" } } },
},
},
],
});
assert.equal(c.hasToolUse, true);
assert.notEqual(c.recommendedTier, "free", "tool-using requests must not route to the free tier");
});
test("buildComplexityRoutingHint — a tool-using request floors the hint tier above free", async () => {
const hint = await buildComplexityRoutingHint(
modelTargets(),
{
messages: [{ role: "user", content: "weather?" }],
tools: [{ function: { name: "get_weather", description: "Get the weather", parameters: {} } }],
},
NOOP_LOG
);
assert.ok(hint, "expected a non-null hint when complexity routing builds successfully");
if (!hint) return;
assert.notEqual(
hint.recommendedMinTier,
"free",
"tool-use must floor the recommended tier at cheap (escalation applied)"
);
});
test("buildComplexityRoutingHint — a null body is safe and still builds a tier-neutral hint", async () => {
const hint = await buildComplexityRoutingHint(modelTargets(), null, NOOP_LOG);
assert.ok(hint, "a null body must not throw — messages default to [] and a hint is built");
if (!hint) return;
assert.ok(
["free", "cheap", "premium"].includes(hint.recommendedMinTier),
`unexpected tier ${hint.recommendedMinTier}`
);
});