From e5ed6b833ff231ac6ef2e4102bf0c6f9038a5080 Mon Sep 17 00:00:00 2001 From: YuanHanzhong Date: Mon, 18 May 2026 22:02:37 +0800 Subject: [PATCH] fix(core): mirror Qwen reasoning history field --- .../provider/default.test.ts | 38 +++++++++++++++ .../provider/default.ts | 46 ++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/packages/core/src/core/openaiContentGenerator/provider/default.test.ts b/packages/core/src/core/openaiContentGenerator/provider/default.test.ts index 12c04c115c..5b1b1c542d 100644 --- a/packages/core/src/core/openaiContentGenerator/provider/default.test.ts +++ b/packages/core/src/core/openaiContentGenerator/provider/default.test.ts @@ -409,6 +409,44 @@ describe('DefaultOpenAICompatibleProvider', () => { }); }); + it('should mirror reasoning_content to reasoning for Qwen model history', () => { + const qwenProvider = new DefaultOpenAICompatibleProvider( + { + ...mockContentGeneratorConfig, + model: 'Qwen/Qwen3.6-35B-A3B', + } as ContentGeneratorConfig, + mockCliConfig, + ); + const originalRequest: OpenAI.Chat.ChatCompletionCreateParams = { + model: 'Qwen/Qwen3.6-35B-A3B', + messages: [ + { + role: 'assistant', + content: 'The answer.', + reasoning_content: 'Prior reasoning.', + } as OpenAI.Chat.ChatCompletionAssistantMessageParam & { + reasoning_content: string; + }, + ], + }; + + const result = qwenProvider.buildRequest(originalRequest, 'prompt-id'); + const assistant = result.messages[0] as + | OpenAI.Chat.ChatCompletionAssistantMessageParam + | (OpenAI.Chat.ChatCompletionAssistantMessageParam & { + reasoning_content?: string; + reasoning?: string; + }); + + expect(assistant).toEqual( + expect.objectContaining({ + reasoning_content: 'Prior reasoning.', + reasoning: 'Prior reasoning.', + }), + ); + expect(originalRequest.messages[0]).not.toHaveProperty('reasoning'); + }); + it('should not include extra_body when not configured', () => { const originalRequest: OpenAI.Chat.ChatCompletionCreateParams = { model: 'gpt-4', diff --git a/packages/core/src/core/openaiContentGenerator/provider/default.ts b/packages/core/src/core/openaiContentGenerator/provider/default.ts index 891188dcd1..ace2fae093 100644 --- a/packages/core/src/core/openaiContentGenerator/provider/default.ts +++ b/packages/core/src/core/openaiContentGenerator/provider/default.ts @@ -11,6 +11,8 @@ import { hasExplicitOutputLimit, } from '../../tokenLimits.js'; +const QWEN_MODEL_MARKER = 'qwen'; + /** * Default provider for standard OpenAI-compatible APIs */ @@ -76,10 +78,11 @@ export class DefaultOpenAICompatibleProvider // This prevents occupying too much context window with output reservation const requestWithTokenLimits = this.applyOutputTokenLimit(request); - return { + const builtRequest = { ...requestWithTokenLimits, ...(extraBody ? extraBody : {}), }; + return this.mirrorQwenReasoningContent(builtRequest); } getDefaultGenerationConfig(): GenerateContentConfig { @@ -167,4 +170,45 @@ export class DefaultOpenAICompatibleProvider max_tokens: effectiveMaxTokens, }; } + + private mirrorQwenReasoningContent( + request: OpenAI.Chat.ChatCompletionCreateParams, + ): OpenAI.Chat.ChatCompletionCreateParams { + const model = request.model.toLowerCase(); + if (!model.includes(QWEN_MODEL_MARKER)) { + return request; + } + + let changed = false; + const messages = request.messages.map((message) => { + if (message.role !== 'assistant') { + return message; + } + + const extended = message as unknown as Record; + const reasoningContent = extended['reasoning_content']; + if ( + typeof reasoningContent !== 'string' || + reasoningContent.length === 0 || + typeof extended['reasoning'] === 'string' + ) { + return message; + } + + changed = true; + return { + ...extended, + reasoning: reasoningContent, + } as unknown as OpenAI.Chat.ChatCompletionMessageParam; + }); + + if (!changed) { + return request; + } + + return { + ...request, + messages, + }; + } }