From b8fe8a6f2d92b31ffa8d3d4fc32eeb5c6d3139a4 Mon Sep 17 00:00:00 2001 From: mastertyko <11311479+mastertyko@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:31:10 +0100 Subject: [PATCH] fix: honor explicit model config when model is not in known tier map (#2643) When a user configures a phase-specific model that is not in MODEL_CAPABILITY_TIER (e.g. gpt-5.4, custom-provider/my-model), getModelTier() defaults to "heavy". This causes resolveModelForComplexity to downgrade every standard/light unit to tier_models, silently ignoring the user's explicit configuration. Add an isKnownModel() check before the downgrade logic. If the configured primary model is not in the known tier map, skip downgrading entirely and honor the user's choice. Known models continue to be routed normally. Closes #2192 --- src/resources/extensions/gsd/model-router.ts | 25 ++++++++++++ .../extensions/gsd/tests/model-router.test.ts | 40 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/resources/extensions/gsd/model-router.ts b/src/resources/extensions/gsd/model-router.ts index fd76d53ca..fe8bdf0a5 100644 --- a/src/resources/extensions/gsd/model-router.ts +++ b/src/resources/extensions/gsd/model-router.ts @@ -114,6 +114,21 @@ export function resolveModelForComplexity( const configuredTier = getModelTier(configuredPrimary); const requestedTier = classification.tier; + // If the configured model is unknown (not in MODEL_CAPABILITY_TIER), + // honor the user's explicit choice — don't downgrade based on a guess. + // Unknown models default to "heavy" in getModelTier, which makes every + // standard/light unit get downgraded to tier_models, silently ignoring + // the user's configuration. (#2192) + if (!isKnownModel(configuredPrimary)) { + return { + modelId: configuredPrimary, + fallbacks: phaseConfig.fallbacks, + tier: requestedTier, + wasDowngraded: false, + reason: `configured model "${configuredPrimary}" is not in the known tier map — honoring explicit config`, + }; + } + // Downgrade-only: if requested tier >= configured tier, no change if (tierOrdinal(requestedTier) >= tierOrdinal(configuredTier)) { return { @@ -202,6 +217,16 @@ function getModelTier(modelId: string): ComplexityTier { return "heavy"; } +/** Check if a model ID has a known capability tier mapping. (#2192) */ +function isKnownModel(modelId: string): boolean { + const bareId = modelId.includes("/") ? modelId.split("/").pop()! : modelId; + if (MODEL_CAPABILITY_TIER[bareId]) return true; + for (const knownId of Object.keys(MODEL_CAPABILITY_TIER)) { + if (bareId.includes(knownId) || knownId.includes(bareId)) return true; + } + return false; +} + function findModelForTier( tier: ComplexityTier, config: DynamicRoutingConfig, diff --git a/src/resources/extensions/gsd/tests/model-router.test.ts b/src/resources/extensions/gsd/tests/model-router.test.ts index c7af7fcca..b22fce7fd 100644 --- a/src/resources/extensions/gsd/tests/model-router.test.ts +++ b/src/resources/extensions/gsd/tests/model-router.test.ts @@ -165,3 +165,43 @@ test("falls back to configured model when no light-tier model available", () => assert.equal(result.modelId, "claude-opus-4-6"); assert.equal(result.wasDowngraded, false); }); + +// ─── #2192: Unknown models honor explicit config ───────────────────────────── + +test("#2192: unknown model is not downgraded — respects user config", () => { + const config = { ...defaultRoutingConfig(), enabled: true }; + const result = resolveModelForComplexity( + makeClassification("light"), + { primary: "gpt-5.4", fallbacks: [] }, + config, + ["gpt-5.4", ...AVAILABLE_MODELS], + ); + assert.equal(result.modelId, "gpt-5.4", "unknown model should be used as-is"); + assert.equal(result.wasDowngraded, false, "should not be downgraded"); + assert.ok(result.reason.includes("not in the known tier map"), "reason should explain why"); +}); + +test("#2192: unknown model with provider prefix is not downgraded", () => { + const config = { ...defaultRoutingConfig(), enabled: true }; + const result = resolveModelForComplexity( + makeClassification("standard"), + { primary: "custom-provider/my-model-v3", fallbacks: [] }, + config, + ["custom-provider/my-model-v3", ...AVAILABLE_MODELS], + ); + assert.equal(result.modelId, "custom-provider/my-model-v3"); + assert.equal(result.wasDowngraded, false); +}); + +test("#2192: known model is still downgraded normally", () => { + const config = { ...defaultRoutingConfig(), enabled: true }; + // claude-opus-4-6 is known as "heavy" — a light request should downgrade + const result = resolveModelForComplexity( + makeClassification("light"), + { primary: "claude-opus-4-6", fallbacks: [] }, + config, + AVAILABLE_MODELS, + ); + assert.equal(result.wasDowngraded, true, "known heavy model should still be downgraded for light tasks"); + assert.notEqual(result.modelId, "claude-opus-4-6"); +});