fix(core): replay DeepSeek reasoning_content on all assistant turns (#3747)

Extend the DeepSeek reasoning_content normalization (introduced in
#3729) to assistant turns without tool_calls. The DeepSeek API rejects
follow-up requests in thinking mode whenever any prior assistant turn
omits reasoning_content, not just turns that carried tool_calls.
This commit is contained in:
tanzhenxin 2026-04-30 07:43:16 +08:00 committed by GitHub
parent 9861114ff3
commit da2936336b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 9 additions and 12 deletions

View file

@ -180,9 +180,9 @@ describe('DeepSeekOpenAICompatibleProvider', () => {
});
// https://github.com/QwenLM/qwen-code/issues/3695 — DeepSeek's thinking
// mode rejects subsequent requests when a prior tool-calling assistant
// turn omits reasoning_content, even if the model itself returned no
// reasoning text. The provider must always send the field.
// mode rejects subsequent requests when any prior assistant turn omits
// reasoning_content, even if the model itself returned no reasoning text.
// The provider must always send the field.
it('injects empty reasoning_content on tool-calling assistant turns missing it', () => {
const originalRequest: OpenAI.Chat.ChatCompletionCreateParams = {
model: 'deepseek-v4-flash',
@ -245,7 +245,7 @@ describe('DeepSeekOpenAICompatibleProvider', () => {
expect(assistant.reasoning_content).toBe('Let me glob first.');
});
it('does not add reasoning_content to assistant turns without tool_calls', () => {
it('injects empty reasoning_content on assistant turns without tool_calls', () => {
const originalRequest: OpenAI.Chat.ChatCompletionCreateParams = {
model: 'deepseek-v4-flash',
messages: [
@ -259,7 +259,7 @@ describe('DeepSeekOpenAICompatibleProvider', () => {
const assistant = result.messages?.[1] as {
reasoning_content?: string;
};
expect(assistant.reasoning_content).toBeUndefined();
expect(assistant.reasoning_content).toBe('');
});
});

View file

@ -108,19 +108,16 @@ function flattenContentParts(
}
// DeepSeek's thinking mode requires reasoning_content to be replayed on every
// prior assistant turn that carried tool_calls. The model may legitimately
// return a tool round without reasoning text, so the field can be missing
// when we rebuild the request. Send an empty string in that case so the API
// contract is satisfied. https://github.com/QwenLM/qwen-code/issues/3695
// prior assistant turn, including ones without tool_calls. The model may
// legitimately return a turn without reasoning text, so the field can be
// missing when we rebuild the request. Send an empty string in that case so
// the API contract is satisfied. https://github.com/QwenLM/qwen-code/issues/3695
function ensureReasoningContentOnToolCalls(
message: OpenAI.Chat.ChatCompletionMessageParam,
): OpenAI.Chat.ChatCompletionMessageParam {
if (message.role !== 'assistant') {
return message;
}
if (!Array.isArray(message.tool_calls) || message.tool_calls.length === 0) {
return message;
}
const extended = message as ExtendedChatCompletionAssistantMessageParam;
if (
typeof extended.reasoning_content === 'string' &&