diff --git a/changelog.d/fixes/8866-auto-family-classification.md b/changelog.d/fixes/8866-auto-family-classification.md new file mode 100644 index 0000000000..53ed6d5512 --- /dev/null +++ b/changelog.d/fixes/8866-auto-family-classification.md @@ -0,0 +1 @@ +- **Auto routing**: `auto/` combos (`auto/glm`, `auto/gemini`, `auto/llama`, …) are recognized as built-in auto models again — the family suffix failed the category/tier parser and fell through as an unknown model instead of routing to that family diff --git a/src/sse/handlers/autoRouting.ts b/src/sse/handlers/autoRouting.ts index 49987dcd08..f898f1094d 100644 --- a/src/sse/handlers/autoRouting.ts +++ b/src/sse/handlers/autoRouting.ts @@ -10,13 +10,17 @@ import { type AutoCategory, type AutoTier, } from "@omniroute/open-sse/services/autoCombo/suffixComposition.ts"; +import { + isValidModelFamily, + type ModelFamily, +} from "@omniroute/open-sse/services/autoCombo/modelFamily.ts"; import { getCachedSettings } from "@/lib/localDb"; import * as log from "../utils/logger"; export type AutoRoutingState = { model: string; variant?: AutoVariant; - spec?: { category?: AutoCategory; tier?: AutoTier }; + spec?: { category?: AutoCategory; tier?: AutoTier; family?: ModelFamily }; isAutoRouting: boolean; recognizedBuiltInAuto: boolean; response: Response | null; @@ -37,12 +41,19 @@ function classifyAutoModel( return { recognizedBuiltInAuto: true }; } const parsedSuffix = parseAutoSuffix(suffix); - return parsedSuffix.valid - ? { - recognizedBuiltInAuto: true, - spec: { category: parsedSuffix.category, tier: parsedSuffix.tier }, - } - : { recognizedBuiltInAuto }; + if (parsedSuffix.valid) { + return { + recognizedBuiltInAuto: true, + spec: { category: parsedSuffix.category, tier: parsedSuffix.tier }, + }; + } + if (isValidModelFamily(suffix)) { + return { + recognizedBuiltInAuto: true, + spec: { family: suffix as ModelFamily }, + }; + } + return { recognizedBuiltInAuto }; } async function applyAutoPrefix( diff --git a/tests/unit/auto-family-classification-8866.test.ts b/tests/unit/auto-family-classification-8866.test.ts new file mode 100644 index 0000000000..f32ecd651f --- /dev/null +++ b/tests/unit/auto-family-classification-8866.test.ts @@ -0,0 +1,50 @@ +/** + * #8866 — `auto/` must be recognized as a built-in auto model. + * + * classifyAutoModel() is module-private, so this exercises it through the public + * resolveAutoRoutingState(). Before the fix, a family suffix failed parseAutoSuffix() + * and fell through to `recognizedBuiltInAuto: false` with no spec, so `auto/glm` was + * treated as an unknown model instead of a family-scoped auto combo. + */ +import test from "node:test"; +import assert from "node:assert/strict"; +import { MODEL_FAMILIES } from "@omniroute/open-sse/services/autoCombo/modelFamily.ts"; +import { resolveAutoRoutingState } from "../../src/sse/handlers/autoRouting.ts"; + +test("#8866: every model family is recognized as auto/", async () => { + assert.ok(MODEL_FAMILIES.length > 0, "expected a non-empty family list"); + for (const family of MODEL_FAMILIES) { + const state = await resolveAutoRoutingState(`auto/${family}`); + assert.equal( + state.recognizedBuiltInAuto, + true, + `auto/${family} should be a recognized built-in auto model` + ); + assert.equal(state.spec?.family, family, `auto/${family} should carry spec.family`); + assert.equal(state.isAutoRouting, true); + } +}); + +test("#8866: a family suffix does not hijack the category/tier spec", async () => { + // A valid category/tier suffix keeps its own shape — the family branch is only a + // fallback for suffixes parseAutoSuffix rejects. + const state = await resolveAutoRoutingState("auto/coding"); + assert.equal(state.recognizedBuiltInAuto, true); + assert.equal(state.spec?.family, undefined, "auto/coding is a category, not a family"); +}); + +test("#8866: an unknown suffix stays unrecognized", async () => { + const state = await resolveAutoRoutingState("auto/not-a-real-family-xyz"); + assert.equal( + state.recognizedBuiltInAuto, + false, + "an unknown suffix must not be treated as a built-in auto model" + ); + assert.equal(state.spec?.family, undefined); +}); + +test("#8866: a plain model is untouched by the family branch", async () => { + const state = await resolveAutoRoutingState("gpt-5.6-sol"); + assert.equal(state.isAutoRouting, false); + assert.equal(state.spec?.family, undefined); +});