From b4aa40760141d3befdb10a7daabea4c246302573 Mon Sep 17 00:00:00 2001 From: cj Date: Sat, 2 May 2026 03:28:19 +0000 Subject: [PATCH] fix(openai_compat): preserve reasoning_content for all DeepSeek assistant turns Remove the hasToolInteraction guard in filterDeepSeekReasoningTurn that was stripping reasoning_content from non-tool interaction assistant messages. DeepSeek V3/V4+ thinking mode rejects requests where any assistant message is missing reasoning_content, returning 400: "The reasoning_content in the thinking mode must be passed back to the API." This is a follow-up to the streaming fix that captured reasoning_content from SSE deltas. That fix alone wasn't sufficient because reasoning_content was still being stripped on replay for plain assistant turns. --- pkg/providers/openai_compat/provider.go | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index 0dbeba0b6..ce6e56b37 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -255,14 +255,6 @@ func filterDeepSeekReasoningMessages(messages []Message) []Message { } func filterDeepSeekReasoningTurn(messages []Message) []Message { - hasToolInteraction := false - for _, msg := range messages { - if msg.Role == "tool" || (msg.Role == "assistant" && len(msg.ToolCalls) > 0) { - hasToolInteraction = true - break - } - } - out := make([]Message, 0, len(messages)) for _, msg := range messages { if messageutil.IsTransientAssistantThoughtMessage(msg) { @@ -270,13 +262,10 @@ func filterDeepSeekReasoningTurn(messages []Message) []Message { } cloned := msg - // DeepSeek thinking-mode replay only requires reasoning_content for - // turns that participate in a tool interaction round. For plain - // assistant turns between two user messages, the docs say the API will - // ignore reasoning_content on replay, so we strip it here. - if cloned.Role == "assistant" && strings.TrimSpace(cloned.ReasoningContent) != "" && !hasToolInteraction { - cloned.ReasoningContent = "" - } + // DeepSeek thinking-mode requires reasoning_content to be echoed + // back for ALL assistant turns. If missing the API returns 400: + // "The reasoning_content in the thinking mode must be passed back + // to the API." if assistantMessageEmpty(cloned) { continue }