-
Notifications
You must be signed in to change notification settings - Fork 909
fix(web): submit thinking level verbatim and drop the hardcoded default #1673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| web: Align thinking-level handling with the CLI: submit the selected level verbatim instead of silently downgrading it, pin the model's catalog default when nothing was chosen, pre-select the target model's default on model switches, and persist explicit picks as the daemon-wide default so new sessions inherit them. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,11 @@ import type { | |
| ThinkingLevel, | ||
| } from '../../api/types'; | ||
| import { safeGetString, safeSetString, STORAGE_KEYS } from '../../lib/storage'; | ||
| import { coerceThinkingForModel, thinkingLevelForModelSwitch } from '../../lib/modelThinking'; | ||
| import { | ||
| defaultThinkingLevelFor, | ||
| thinkingLevelForModelSwitch, | ||
| thinkingLevelToConfig, | ||
| } from '../../lib/modelThinking'; | ||
| import { beginLocalTurn, settleLocalTurn } from './useWorkspaceState'; | ||
| import type { ActivityState } from '../../types'; | ||
| import type { ExtendedState } from '../useKimiWebClient'; | ||
|
|
@@ -129,15 +133,24 @@ export function useModelProviderState( | |
| return modelById(rawModel)?.id ?? rawModel ?? undefined; | ||
| } | ||
|
|
||
| function activeThinkingModel(): AppModel | undefined { | ||
| return modelById(currentModelId()); | ||
| function applyThinkingLevel(level: ThinkingLevel | undefined): ThinkingLevel | undefined { | ||
| // Stored verbatim — whatever the user picked is what gets submitted to the | ||
| // daemon (same as the TUI); no coercion against the active model. Only | ||
| // concrete levels are persisted; "no preference" stays in-memory. | ||
| rawState.thinking = level; | ||
| if (level !== undefined) saveThinkingToStorage(level); | ||
| return level; | ||
| } | ||
|
|
||
| function applyThinkingLevel(level: ThinkingLevel): ThinkingLevel { | ||
| const next = coerceThinkingForModel(activeThinkingModel(), level); | ||
| rawState.thinking = next; | ||
| saveThinkingToStorage(next); | ||
| return next; | ||
| /** Persist an explicit thinking pick as the daemon-wide default ([thinking] | ||
| * in config.toml), mirroring the TUI's persistModelSelection, so sessions | ||
| * created by other clients inherit it. Fire-and-forget: the session-level | ||
| * and local values have already been applied. Never called for derived | ||
| * values (e.g. the loadModels default pin) — only for user actions. */ | ||
| function persistGlobalThinking(level: ThinkingLevel): void { | ||
| void getKimiWebApi() | ||
| .setConfig({ thinking: thinkingLevelToConfig(level) }) | ||
| .catch((error: unknown) => pushOperationFailure('setConfig', error)); | ||
| } | ||
|
|
||
| async function loadSkillsForSession(sessionId: string): Promise<void> { | ||
|
|
@@ -167,7 +180,15 @@ export function useModelProviderState( | |
| try { | ||
| const api = getKimiWebApi(); | ||
| models.value = await api.listModels(); | ||
| applyThinkingLevel(rawState.thinking); | ||
| // No explicit preference: pin the active model's default level (from the | ||
| // server catalog) as a concrete value, so what the UI shows, what gets | ||
| // submitted, and what the session runs are always the same. In-memory | ||
| // only — localStorage stays reserved for levels the user actually | ||
| // picked, and a reload re-derives from the then-current model. | ||
| if (rawState.thinking === undefined) { | ||
| const active = modelById(currentModelId()); | ||
| if (active !== undefined) rawState.thinking = defaultThinkingLevelFor(active); | ||
| } | ||
| } catch (err) { | ||
| pushOperationFailure('loadModels', err); | ||
| } | ||
|
|
@@ -221,14 +242,16 @@ export function useModelProviderState( | |
| // Remember the pick — startSessionAndSendPrompt applies it at create time. | ||
| draftModel.value = modelId; | ||
| applyThinkingLevel(nextThinking); | ||
| if (nextThinking !== prevThinking && nextThinking !== undefined) { | ||
| persistGlobalThinking(nextThinking); | ||
| } | ||
| return true; | ||
| } | ||
| // Optimistic: show the chosen model immediately, but remember the previous | ||
| // one so we can roll back if the switch never reaches the daemon. | ||
| updateSession(sid, (s) => ({ ...s, model: modelId })); | ||
| if (nextThinking !== prevThinking) { | ||
| rawState.thinking = nextThinking; | ||
| saveThinkingToStorage(nextThinking); | ||
| applyThinkingLevel(nextThinking); | ||
| } | ||
| try { | ||
| await getKimiWebApi().updateSession(sid, { | ||
|
|
@@ -242,12 +265,16 @@ export function useModelProviderState( | |
| // new one as if the switch succeeded, then surface the failure. | ||
| updateSession(sid, (s) => ({ ...s, model: prevSessionModel ?? s.model })); | ||
| if (nextThinking !== prevThinking) { | ||
| rawState.thinking = prevThinking; | ||
| saveThinkingToStorage(prevThinking); | ||
| applyThinkingLevel(prevThinking); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a model switch fails after Useful? React with 👍 / 👎. |
||
| } | ||
| pushOperationFailure('setModel', err, { sessionId: sid }); | ||
| return false; | ||
| } | ||
| // The switch reached the daemon: also persist the thinking pick as the | ||
| // daemon-wide default (mirrors the TUI). Skipped on rollback above. | ||
| if (nextThinking !== prevThinking && nextThinking !== undefined) { | ||
| persistGlobalThinking(nextThinking); | ||
| } | ||
| // refreshSessionStatus folds the authoritative current model from /status | ||
| // back into the session (the profile echo can return ''). Best-effort: a | ||
| // failure here does not mean the switch failed, so it must not roll back. | ||
|
|
@@ -419,6 +446,7 @@ export function useModelProviderState( | |
| function setThinking(level: ThinkingLevel): void { | ||
| const next = applyThinkingLevel(level); | ||
| void persistSessionProfile({ thinking: next }); | ||
| if (next !== undefined) persistGlobalThinking(next); | ||
| } | ||
|
|
||
| return { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there is no stored thinking preference, this pins
rawState.thinkingduringloadModels(), but the first-load flow callsmodelProvider.loadModels()before sessions are loaded and before a deep-linked or auto-selected session is chosen. In that pathcurrentModelId()only sees the global default model, so opening an existing session whose model differs from the default can store e.g.offand latersubmitPromptsends that explicit value instead of letting the daemon resolve the session/model default.Useful? React with 👍 / 👎.