diff --git a/ui/src/ui/chat-model-ref.test.ts b/ui/src/ui/chat-model-ref.test.ts index 86b46f3fe7f..544bea8333c 100644 --- a/ui/src/ui/chat-model-ref.test.ts +++ b/ui/src/ui/chat-model-ref.test.ts @@ -47,4 +47,16 @@ describe("chat-model-ref helpers", () => { expect(resolveServerChatModelValue("gpt-5-mini", "openai")).toBe("openai/gpt-5-mini"); expect(resolveServerChatModelValue("alias-only", null)).toBe("alias-only"); }); + + it("does not double-qualify already-qualified model values", () => { + expect(resolveServerChatModelValue("ollama/gpt-oss:120b-cloud", "anthropic")).toBe( + "ollama/gpt-oss:120b-cloud", + ); + }); + + it("preserves nested vendor/model identifiers", () => { + expect( + resolveServerChatModelValue("openrouter/anthropic/claude-sonnet-4-6", "openrouter"), + ).toBe("openrouter/anthropic/claude-sonnet-4-6"); + }); }); diff --git a/ui/src/ui/chat-model-ref.ts b/ui/src/ui/chat-model-ref.ts index 351b8544bad..f4c4c8f4ad5 100644 --- a/ui/src/ui/chat-model-ref.ts +++ b/ui/src/ui/chat-model-ref.ts @@ -15,6 +15,11 @@ export function buildQualifiedChatModelValue(model: string, provider?: string | if (!trimmedModel) { return ""; } + // If the model string already contains "/" it is already provider-qualified + // (e.g. "ollama/gpt-oss:120b-cloud"); return as-is to avoid double-qualifying. + if (trimmedModel.includes("/")) { + return trimmedModel; + } const trimmedProvider = provider?.trim(); return trimmedProvider ? `${trimmedProvider}/${trimmedModel}` : trimmedModel; } diff --git a/vitest.config.ts b/vitest.config.ts index 568f5dd03e6..762c9ad6708 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -41,6 +41,7 @@ export default defineConfig({ "src/**/*.test.ts", "extensions/**/*.test.ts", "test/**/*.test.ts", + "ui/src/ui/chat-model-ref.test.ts", "ui/src/ui/app-chat.test.ts", "ui/src/ui/views/agents-utils.test.ts", "ui/src/ui/views/chat.test.ts",