From 8abbccea54515a92311c95472b1793153e8d0865 Mon Sep 17 00:00:00 2001 From: Flux Labs Date: Sat, 14 Mar 2026 15:00:03 -0500 Subject: [PATCH] fix: resolve OpenRouter model IDs in auto-mode and show active model per phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenRouter models use slash-separated IDs (e.g. "moonshotai/kimi-k2.5") where the full string is the model ID on the "openrouter" provider. The auto-mode model switcher incorrectly split on the first slash and treated the prefix as a provider name, causing all OpenRouter preference models to fail resolution and fall back to the default model for every phase. Now the resolver first checks whether the slash-prefix is a known provider, and if not (or if no match is found), falls back to matching the full string as a model ID — consistent with model-resolver.ts. Also improves the progress widget and notifications to show [PHASE] and provider/model so users can confirm the correct model is active. Closes #402 --- src/resources/extensions/gsd/auto.ts | 45 +++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/resources/extensions/gsd/auto.ts b/src/resources/extensions/gsd/auto.ts index c00a203f0..cd8924ac0 100644 --- a/src/resources/extensions/gsd/auto.ts +++ b/src/resources/extensions/gsd/auto.ts @@ -1162,7 +1162,14 @@ function updateProgressWidget( .join(theme.fg("dim", " ")); const modelId = cmdCtx?.model?.id ?? ""; - const sRight = modelId ? theme.fg("dim", modelId) : ""; + const modelProvider = cmdCtx?.model?.provider ?? ""; + const modelPhase = phaseLabel ? theme.fg("dim", `[${phaseLabel}] `) : ""; + const modelDisplay = modelProvider && modelId + ? `${modelProvider}/${modelId}` + : modelId; + const sRight = modelDisplay + ? `${modelPhase}${theme.fg("dim", modelDisplay)}` + : ""; lines.push(rightAlign(`${pad}${sLeft}`, sRight, width)); } @@ -1940,16 +1947,37 @@ async function dispatchNextUnit( let modelSet = false; for (const modelId of modelsToTry) { - // Support "provider/model" format for explicit provider targeting + // Resolve model from available models. + // Handles multiple formats: + // "provider/model" → explicit provider targeting (e.g. "anthropic/claude-opus-4-6") + // "bare-id" → match by ID across providers + // "org/model-name" → OpenRouter-style IDs where the full string is the model ID + // "openrouter/org/model" → explicit provider + OpenRouter model ID const slashIdx = modelId.indexOf("/"); let model; if (slashIdx !== -1) { - const provider = modelId.substring(0, slashIdx); + const maybeProvider = modelId.substring(0, slashIdx); const id = modelId.substring(slashIdx + 1); - model = availableModels.find( - m => m.provider.toLowerCase() === provider.toLowerCase() - && m.id.toLowerCase() === id.toLowerCase(), - ); + + // Check if the prefix before the first slash is a known provider + const knownProviders = new Set(availableModels.map(m => m.provider.toLowerCase())); + if (knownProviders.has(maybeProvider.toLowerCase())) { + // Explicit "provider/model" format (handles "openrouter/org/model" too) + model = availableModels.find( + m => m.provider.toLowerCase() === maybeProvider.toLowerCase() + && m.id.toLowerCase() === id.toLowerCase(), + ); + } + + // If the prefix wasn't a known provider, or no match was found within that provider, + // try matching the full string as a model ID (OpenRouter-style IDs like "org/model-name") + if (!model) { + const lower = modelId.toLowerCase(); + model = availableModels.find( + m => m.id.toLowerCase() === lower + || `${m.provider}/${m.id}`.toLowerCase() === lower, + ); + } } else { // For bare IDs, prefer the current session's provider, then first available match const currentProvider = ctx.model?.provider; @@ -1983,7 +2011,8 @@ async function dispatchNextUnit( const fallbackNote = modelId === modelConfig.primary ? "" : ` (fallback from ${modelConfig.primary})`; - ctx.ui.notify(`Model: ${modelId}${fallbackNote}`, "info"); + const phase = unitPhaseLabel(unitType); + ctx.ui.notify(`Model [${phase}]: ${model.provider}/${model.id}${fallbackNote}`, "info"); modelSet = true; break; } else {