From d09c19c0c5a25be6f0dce68e9fe57ef8e61b2e16 Mon Sep 17 00:00:00 2001 From: Fu Yuchen Date: Tue, 28 Apr 2026 09:22:17 +0800 Subject: [PATCH] fix(core,cli): stop stripping reasoning on switch and resume paths (#3682) --- packages/cli/src/ui/hooks/slashCommandProcessor.test.ts | 4 ++-- packages/cli/src/ui/hooks/slashCommandProcessor.ts | 1 - packages/core/src/config/config.test.ts | 4 ++-- packages/core/src/config/config.ts | 8 +++----- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts index 0893c8e28..7b963cfd2 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts @@ -501,7 +501,7 @@ describe('useSlashCommandProcessor', () => { ); }); - it('should strip thoughts when handling "load_history" action', async () => { + it('should preserve thoughts when handling "load_history" action', async () => { const mockClient = { setHistory: vi.fn(), stripThoughtsFromHistory: vi.fn(), @@ -531,7 +531,7 @@ describe('useSlashCommandProcessor', () => { }); expect(mockClient.setHistory).toHaveBeenCalledTimes(1); - expect(mockClient.stripThoughtsFromHistory).toHaveBeenCalledWith(); + expect(mockClient.stripThoughtsFromHistory).not.toHaveBeenCalled(); }); it('should handle a "quit" action', async () => { diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index fb1281f16..f9d685b78 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -647,7 +647,6 @@ export const useSlashCommandProcessor = ( } case 'load_history': { config?.getGeminiClient()?.setHistory(result.clientHistory); - config?.getGeminiClient()?.stripThoughtsFromHistory(); fullCommandContext.ui.clear(); result.history.forEach((item, index) => { fullCommandContext.ui.addItem(item, index); diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts index c3ce32493..873c403d8 100644 --- a/packages/core/src/config/config.test.ts +++ b/packages/core/src/config/config.test.ts @@ -531,7 +531,7 @@ describe('Server Config (config.ts)', () => { expect(vi.mocked(createContentGenerator)).toHaveBeenCalledTimes(1); }); - it('should strip thoughts from history on model switch (#3304)', async () => { + it('should preserve thoughts from history on model switch', async () => { const config = new Config(baseParams); const mockContentConfig: ContentGeneratorConfig = { @@ -567,7 +567,7 @@ describe('Server Config (config.ts)', () => { await config.switchModel(AuthType.QWEN_OAUTH, 'coder-model'); - expect(stripSpy).toHaveBeenCalledTimes(1); + expect(stripSpy).not.toHaveBeenCalled(); }); }); diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 27a97d205..a3b498b99 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -1423,11 +1423,9 @@ export class Config { return; } - // Strip thinking blocks from conversation history on model switch. - // reasoning_content is a non-standard field that causes strict - // OpenAI-compatible providers to reject requests with 422 errors - // when thought parts from a previous model leak into the payload (#3304). - this.geminiClient.stripThoughtsFromHistory(); + // Keep full history (including thought parts) on model switch. + // Some OpenAI-compatible reasoning models (e.g. DeepSeek) require + // reasoning_content to be preserved across turns. // Hot update path: only supported for qwen-oauth. // For other auth types we always refresh to recreate the ContentGenerator.