fix(desktop): default reasoning effort to low for new sessions

The global effort default moves from max to low on both the server
(/api/effort fallback) and the desktop store. Users who already saved an
effort keep it.

ChatGPT Official and Grok Official are not in the saved provider list, so
their first draft selection carried no effort: the server ran the model
default while the selector showed the global value. Resolve the effort
against the model catalog for those providers too.
This commit is contained in:
程序员阿江(Relakkes)
2026-10-04 13:41:13 +08:00
parent 195b406e91
commit 6ab7ec0c0d
8 changed files with 53 additions and 9 deletions
@@ -1069,6 +1069,7 @@ describe('ModelSelector', () => {
})
useSettingsStore.setState({
locale: 'en',
effortLevel: 'max',
availableModels: openAIModels,
currentModel: openAIModels[0],
activeProviderName: 'ChatGPT Official',
+26 -2
View File
@@ -865,6 +865,30 @@ describe('EmptySession', () => {
})
})
it('carries the global effort into the first draft selection for ChatGPT Official', async () => {
useSettingsStore.setState({ effortLevel: 'low' })
useProviderStore.setState({
providers: [],
activeId: 'openai-official',
providerOrder: ['claude-official', 'openai-official', 'grok-official'],
hasLoadedProviders: true,
})
render(<EmptySession />)
setComposerText('draft question', 14)
fireEvent.click(screen.getByRole('button', { name: /Run/i }))
await waitFor(() => {
expect(mocks.createSession).toHaveBeenCalledWith({ permissionMode: 'default' })
})
expect(useSessionRuntimeStore.getState().selections['draft-session']).toMatchObject({
providerId: 'openai-official',
effortLevel: 'low',
})
})
it.each([true, false])('materializes raw provider models with 1M=%s before the first draft message', async (enabled) => {
useProviderStore.setState({
providers: [{
@@ -902,7 +926,7 @@ describe('EmptySession', () => {
expect(useSessionRuntimeStore.getState().selections['draft-session']).toEqual({
providerId: 'provider-minimax',
modelId: enabled ? 'MiniMax-M3[1m]' : 'MiniMax-M3',
effortLevel: 'max',
effortLevel: 'low',
})
expect(mocks.wsSend.mock.calls.slice(0, 3)).toEqual([
[
@@ -911,7 +935,7 @@ describe('EmptySession', () => {
type: 'set_runtime_config',
providerId: 'provider-minimax',
modelId: enabled ? 'MiniMax-M3[1m]' : 'MiniMax-M3',
effortLevel: 'max',
effortLevel: 'low',
},
],
['draft-session', { type: 'prewarm_session' }],
+11 -1
View File
@@ -34,6 +34,8 @@ import {
SlashCommandMenu,
} from '../components/chat/SlashCommandMenu'
import { useMobileViewport } from '../hooks/useMobileViewport'
import { GROK_OFFICIAL_PROVIDER_ID } from '../constants/grokOfficialProvider'
import { OPENAI_OFFICIAL_PROVIDER_ID } from '../constants/openaiOfficialProvider'
import { isDesktopRuntime } from '../lib/desktopRuntime'
import {
normalizeRuntimeSelection,
@@ -400,7 +402,15 @@ export function EmptySession() {
activeCustomProvider.apiFormat,
getBundledPresetReasoningProviderKind(activeCustomProvider.presetId),
)
: defaultActiveProviderSelection
: defaultActiveProviderSelection && (
defaultActiveProviderSelection.providerId === OPENAI_OFFICIAL_PROVIDER_ID ||
defaultActiveProviderSelection.providerId === GROK_OFFICIAL_PROVIDER_ID
)
// Built-in providers are not in the saved list. Without an explicit
// effort the server runs the model default while the selector shows
// the global value, so resolve it against the model's catalog here.
? normalizeRuntimeSelection({ ...defaultActiveProviderSelection, effortLevel })
: defaultActiveProviderSelection
const claudeOAuthRuntimeSelection = !explicitDraftSelection &&
authStatus.source === 'claude-oauth' &&
activeProviderId === null &&
+9
View File
@@ -160,6 +160,15 @@ describe('settingsStore locale defaults', () => {
})
})
describe('settingsStore effort default', () => {
it('starts at low before the server value loads', async () => {
vi.resetModules()
const { useSettingsStore } = await import('./settingsStore')
expect(useSettingsStore.getState().effortLevel).toBe('low')
})
})
describe('settingsStore UI zoom', () => {
beforeEach(() => {
vi.resetModules()
+1 -1
View File
@@ -204,7 +204,7 @@ let autoQuestionUpdateQueue: Promise<unknown> = Promise.resolve()
export const useSettingsStore = create<SettingsStore>((set, get) => ({
permissionMode: 'default',
currentModel: null,
effortLevel: 'max',
effortLevel: 'low',
thinkingEnabled: true,
workflowKeywordTriggerEnabled: true,
agentTeamsEnabled: true,
@@ -457,9 +457,9 @@ describe('Business Flow: Models & Effort', () => {
expect(status).toBe(400)
})
it('should default effort to max', async () => {
it('should default effort to low', async () => {
const { data } = await api('GET', '/api/effort')
expect(data.level).toBe('max')
expect(data.level).toBe('low')
expect(data.available).toEqual(['low', 'medium', 'high', 'xhigh', 'max'])
})
+2 -2
View File
@@ -1548,7 +1548,7 @@ describe('Models API', () => {
expect(res.status).toBe(200)
const body = await res.json()
expect(body.level).toBe('max')
expect(body.level).toBe('low')
expect(body.available).toEqual(['low', 'medium', 'high', 'xhigh', 'max'])
})
@@ -1561,7 +1561,7 @@ describe('Models API', () => {
expect(res.status).toBe(200)
const body = await res.json()
expect(body.level).toBe('max')
expect(body.level).toBe('low')
expect(body.available).toEqual(['low', 'medium', 'high', 'xhigh', 'max'])
})
+1 -1
View File
@@ -119,7 +119,7 @@ const EFFORT_LEVELS = MODEL_REASONING_EFFORTS
// CLI's own default runs. Keep this in step with what the `opus` alias resolves to there
// (getDefaultOpusModel) so the UI names the model that actually runs.
const DEFAULT_MODEL = 'claude-opus-5-5'
const DEFAULT_EFFORT = 'max'
const DEFAULT_EFFORT = 'low'
const settingsService = new SettingsService()
const providerService = new ProviderService()