Skip to content

Commit be20eeb

Browse files
hclsysclaude
authored andcommitted
fix(ui): resolve model provider from catalog instead of stale session default
When the server returns a bare model name (e.g. "deepseek-chat") with a session-level modelProvider (e.g. "zai"), the UI blindly prepends the provider — producing "zai/deepseek-chat" instead of the correct "deepseek/deepseek-chat". This causes "model not allowed" errors when switching between models from different providers. Root cause: resolveModelOverrideValue() and resolveDefaultModelValue() in app-render.helpers.ts, plus the /model slash command handler in slash-command-executor.ts, all call resolveServerChatModelValue() which trusts the session's default provider. The session provider reflects the PREVIOUS model, not the newly selected one. Fix: for bare model names, create a raw ChatModelOverride and resolve through normalizeChatModelOverrideValue() which looks up the correct provider from the model catalog. Falls back to server-provided provider only if the catalog lookup fails. All 3 call sites are fixed. Closes #53031 Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: HCL <[email protected]>
1 parent 5ab3782 commit be20eeb

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

ui/src/ui/app-render.helpers.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,17 +536,38 @@ function resolveModelOverrideValue(state: AppViewState): string {
536536
return "";
537537
}
538538
// No local override recorded yet — fall back to server data.
539-
// Include provider prefix so the value matches option keys (provider/model).
539+
// Use the bare model name and resolve provider from the catalog rather than
540+
// trusting the session's modelProvider, which may be the session default and
541+
// not the model's actual provider (e.g. "zai" for a "deepseek-chat" model).
540542
const activeRow = resolveActiveSessionRow(state);
541543
if (activeRow && typeof activeRow.model === "string" && activeRow.model.trim()) {
544+
const rawOverride = createChatModelOverride(activeRow.model.trim());
545+
if (rawOverride) {
546+
const normalized = normalizeChatModelOverrideValue(rawOverride, state.chatModelCatalog ?? []);
547+
if (normalized) {
548+
return normalized;
549+
}
550+
}
551+
// Fallback: use server-provided provider if catalog lookup fails.
542552
return resolveServerChatModelValue(activeRow.model, activeRow.modelProvider);
543553
}
544554
return "";
545555
}
546556

547557
function resolveDefaultModelValue(state: AppViewState): string {
548558
const defaults = state.sessionsResult?.defaults;
549-
return resolveServerChatModelValue(defaults?.model, defaults?.modelProvider);
559+
const model = defaults?.model;
560+
if (typeof model !== "string" || !model.trim()) {
561+
return "";
562+
}
563+
const rawOverride = createChatModelOverride(model.trim());
564+
if (rawOverride) {
565+
const normalized = normalizeChatModelOverrideValue(rawOverride, state.chatModelCatalog ?? []);
566+
if (normalized) {
567+
return normalized;
568+
}
569+
}
570+
return resolveServerChatModelValue(model, defaults?.modelProvider);
550571
}
551572

552573
function buildChatModelOptions(

ui/src/ui/chat/slash-command-executor.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
isSubagentSessionKey,
1717
parseAgentSessionKey,
1818
} from "../../../../src/routing/session-key.js";
19-
import { createChatModelOverride, resolveServerChatModelValue } from "../chat-model-ref.ts";
19+
import { createChatModelOverride, normalizeChatModelOverrideValue, resolveServerChatModelValue } from "../chat-model-ref.ts";
2020
import type { GatewayBrowserClient } from "../gateway.ts";
2121
import type {
2222
AgentsListResult,
@@ -155,10 +155,12 @@ async function executeModel(
155155
key: sessionKey,
156156
model: args.trim(),
157157
});
158-
const resolvedValue = resolveServerChatModelValue(
159-
patched.resolved?.model ?? args.trim(),
160-
patched.resolved?.modelProvider,
161-
);
158+
const patchedModel = patched.resolved?.model ?? args.trim();
159+
const rawOverride = createChatModelOverride(patchedModel.trim());
160+
const resolvedValue = rawOverride
161+
? (normalizeChatModelOverrideValue(rawOverride, state.chatModelCatalog ?? []) ||
162+
resolveServerChatModelValue(patchedModel, patched.resolved?.modelProvider))
163+
: resolveServerChatModelValue(patchedModel, patched.resolved?.modelProvider);
162164
return {
163165
content: `Model set to \`${args.trim()}\`.`,
164166
action: "refresh",

0 commit comments

Comments
 (0)