Skip to content

Commit 7a9980e

Browse files
committed
fix(ui): use sessions row with patched.resolved fallback for model reconciliation
When a session is absent from the sessions list (new session, test scenarios), reading only from the sessions row blanks out the model override. Prefer the refreshed sessions row for existing sessions (authoritative post-lifecycle model), fall back to patched.resolved from the RPC for new sessions, and finally to nextModel as the ultimate safe default. Fixes the test regression: keeps the selected model visible when the active session is absent from sessions.list
1 parent fc4367f commit 7a9980e

1 file changed

Lines changed: 16 additions & 18 deletions

File tree

ui/src/ui/chat/session-controls.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {
4545
normalizeThinkLevel,
4646
resolveThinkingDefaultForModel,
4747
} from "../thinking.ts";
48-
import type { FastMode, GatewayThinkingLevelOption, SessionsListResult } from "../types.ts";
48+
import type { FastMode, GatewayThinkingLevelOption, SessionsListResult, SessionsPatchResult } from "../types.ts";
4949

5050
type ChatSessionSwitchHandler = (state: AppViewState, nextSessionKey: string) => void;
5151
type ChatSessionSelectSurface = "desktop" | "mobile" | "sidebar";
@@ -1391,36 +1391,34 @@ async function switchChatModel(state: AppViewState, nextModel: string): Promise<
13911391
};
13921392
const switchPromise: Promise<boolean> = (async () => {
13931393
try {
1394-
await client.request("sessions.patch", {
1394+
const patched = await client.request<SessionsPatchResult>("sessions.patch", {
13951395
key: targetSessionKey,
13961396
...scopedAgentParamsForSession(state, targetSessionKey),
13971397
model: nextModel || null,
13981398
});
1399-
// Refresh the sessions list first so the model-fallback reconciliation
1400-
// below reads the authoritative server-confirmed model from the session
1401-
// row. The server can silently fall back to a different provider (e.g.
1402-
// when the requested provider is unreachable), and the refreshed row
1403-
// carries the final effective model after all fallback lifecycle hooks
1404-
// have run — matching the contract already used by the /model command.
14051399
await refreshSessionOptions(state);
1406-
// Reconcile the cached override for explicit (non-empty) selections.
1407-
// When the user picked Default the cache was already set to null above
1408-
// and must stay null so the selector keeps following the current
1409-
// default model.
1400+
// Reconcile the cached override when the user made an explicit (non-empty)
1401+
// model selection. `patched.resolved` from the RPC carries the request-time
1402+
// resolution even before the sessions list is populated. When the sessions
1403+
// list already has a row (existing session), prefer the refreshed row
1404+
// because it carries the final effective model after server-side fallback
1405+
// lifecycle hooks. When the row is absent (new session, test scenarios),
1406+
// fall back to `patched.resolved` so the dropdown still shows the resolved
1407+
// model rather than getting blanked out.
14101408
if (nextModel) {
14111409
const row = state.sessionsResult?.sessions?.find(
14121410
(s) => s.key === targetSessionKey,
14131411
);
1412+
const resolvedModel =
1413+
row?.model ?? patched.resolved?.model ?? nextModel;
1414+
const resolvedProvider =
1415+
row?.modelProvider ?? patched.resolved?.modelProvider ?? "";
14141416
const resolvedValue = resolvePreferredServerChatModelValue(
1415-
row?.model,
1416-
row?.modelProvider,
1417+
resolvedModel,
1418+
resolvedProvider,
14171419
state.chatModelCatalog ?? [],
14181420
);
14191421
const resolvedOverride = createChatModelOverride(resolvedValue);
1420-
// Only update when the resolved override differs. This avoids
1421-
// unnecessary re-renders and preserves the cached override kind
1422-
// (raw vs qualified) when the server confirms the same model the
1423-
// user requested — no provider fallback occurred.
14241422
if (
14251423
JSON.stringify(resolvedOverride) !==
14261424
JSON.stringify(state.chatModelOverrides[targetSessionKey])

0 commit comments

Comments
 (0)