fix(core): strip thinking blocks from history on model switch (#3304) (#3315)

When switching models mid-session, reasoning_content fields from
thinking-capable models leaked into API requests sent to the new
provider, causing 422 errors on strict OpenAI-compatible endpoints.

Call stripThoughtsFromHistory() in handleModelChange() so thought parts
are removed before the next request is built for the new model.
This commit is contained in:
tanzhenxin 2026-04-17 15:28:58 +08:00 committed by GitHub
parent 0f8e8db9ae
commit f7733cfc7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 0 deletions

View file

@ -474,6 +474,45 @@ describe('Server Config (config.ts)', () => {
).toHaveBeenCalledTimes(2); ).toHaveBeenCalledTimes(2);
expect(vi.mocked(createContentGenerator)).toHaveBeenCalledTimes(1); expect(vi.mocked(createContentGenerator)).toHaveBeenCalledTimes(1);
}); });
it('should strip thoughts from history on model switch (#3304)', async () => {
const config = new Config(baseParams);
const mockContentConfig: ContentGeneratorConfig = {
authType: AuthType.QWEN_OAUTH,
model: 'coder-model',
apiKey: 'QWEN_OAUTH_DYNAMIC_TOKEN',
baseUrl: DEFAULT_DASHSCOPE_BASE_URL,
timeout: 60000,
maxRetries: 3,
} as ContentGeneratorConfig;
vi.mocked(resolveContentGeneratorConfigWithSources).mockImplementation(
(_config, authType, generationConfig) => ({
config: {
...mockContentConfig,
authType,
model: generationConfig?.model ?? mockContentConfig.model,
} as ContentGeneratorConfig,
sources: {},
}),
);
vi.mocked(createContentGenerator).mockResolvedValue({
generateContent: vi.fn(),
generateContentStream: vi.fn(),
countTokens: vi.fn(),
embedContent: vi.fn(),
} as unknown as ContentGenerator);
await config.refreshAuth(AuthType.QWEN_OAUTH);
const stripSpy = config.getGeminiClient().stripThoughtsFromHistory;
vi.mocked(stripSpy).mockClear();
await config.switchModel(AuthType.QWEN_OAUTH, 'coder-model');
expect(stripSpy).toHaveBeenCalledTimes(1);
});
}); });
describe('model switching with different credentials (OpenAI)', () => { describe('model switching with different credentials (OpenAI)', () => {

View file

@ -1334,6 +1334,12 @@ export class Config {
return; 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();
// Hot update path: only supported for qwen-oauth. // Hot update path: only supported for qwen-oauth.
// For other auth types we always refresh to recreate the ContentGenerator. // For other auth types we always refresh to recreate the ContentGenerator.
// //