From 27cb060f709baf1a964d4f07a82e09c00636b0e3 Mon Sep 17 00:00:00 2001 From: Gorre Surya Date: Wed, 27 May 2026 12:43:09 -0400 Subject: [PATCH] fix: pass reasoning_content back to DeepSeek API in multi-turn conversations When DeepSeek reasoning models (e.g. deepseek-reasoner) return reasoning_content, the API requires that field to be echoed back in subsequent request messages. The previous createRequest() always passed null as the last argument of ChatCompletionMessage for ASSISTANT turns, silently dropping the stored reasoning_content and causing HTTP 400 errors on second and later turns. Fix: when mapping an AssistantMessage back to a ChatCompletionMessage, check whether it is a DeepSeekAssistantMessage and, if so, forward its getReasoningContent() value. Plain AssistantMessage instances (non-reasoning models) are unaffected and still emit null reasoning_content. Fixes: #6026 Signed-off-by: Gorre Surya --- .../ai/deepseek/DeepSeekChatModel.java | 11 ++++-- .../DeepSeekChatCompletionRequestTests.java | 38 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java index fad9af7f8f..adf7502d76 100644 --- a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java +++ b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java @@ -430,14 +430,17 @@ else if (message.getMessageType() == MessageType.ASSISTANT) { }).toList(); } Boolean isPrefixAssistantMessage = null; - if (message instanceof DeepSeekAssistantMessage - && Boolean.TRUE.equals(((DeepSeekAssistantMessage) message).getPrefix())) { - isPrefixAssistantMessage = true; + String reasoningContent = null; + if (message instanceof DeepSeekAssistantMessage deepSeekMsg) { + if (Boolean.TRUE.equals(deepSeekMsg.getPrefix())) { + isPrefixAssistantMessage = true; + } + reasoningContent = deepSeekMsg.getReasoningContent(); } String text = assistantMessage.getText(); Assert.state(text != null, "text must not be null"); return List.of(new ChatCompletionMessage(text, ChatCompletionMessage.Role.ASSISTANT, null, null, - toolCalls, isPrefixAssistantMessage, null)); + toolCalls, isPrefixAssistantMessage, reasoningContent)); } else if (message.getMessageType() == MessageType.TOOL) { ToolResponseMessage toolMessage = (ToolResponseMessage) message; diff --git a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekChatCompletionRequestTests.java b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekChatCompletionRequestTests.java index 56338f2657..b62d628a93 100644 --- a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekChatCompletionRequestTests.java +++ b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/DeepSeekChatCompletionRequestTests.java @@ -16,8 +16,11 @@ package org.springframework.ai.deepseek; +import java.util.List; + import org.junit.jupiter.api.Test; +import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.deepseek.api.DeepSeekApi; @@ -54,4 +57,39 @@ public void createRequestWithChatOptions() { assertThat(request.temperature()).isEqualTo(99.9D); } + @Test + public void reasoningContentIsPassedBackForDeepSeekAssistantMessage() { + var client = DeepSeekChatModel.builder().deepSeekApi(DeepSeekApi.builder().apiKey("TEST").build()).build(); + + DeepSeekAssistantMessage assistantMessage = new DeepSeekAssistantMessage.Builder().content("The answer is 42.") + .reasoningContent("I reasoned through the problem step by step.") + .build(); + + var prompt = new Prompt( + List.of(new UserMessage("What is the answer?"), assistantMessage, new UserMessage("Are you sure?")), + DeepSeekChatOptions.builder().model("deepseek-reasoner").build()); + + var request = client.createRequest(prompt, false); + + assertThat(request.messages()).hasSize(3); + var assistantMsg = request.messages().get(1); + assertThat(assistantMsg.role()).isEqualTo(DeepSeekApi.ChatCompletionMessage.Role.ASSISTANT); + assertThat(assistantMsg.reasoningContent()).isEqualTo("I reasoned through the problem step by step."); + } + + @Test + public void nullReasoningContentIsOmittedForPlainAssistantMessage() { + var client = DeepSeekChatModel.builder().deepSeekApi(DeepSeekApi.builder().apiKey("TEST").build()).build(); + + var prompt = new Prompt(List.of(new UserMessage("Hello"), + new org.springframework.ai.chat.messages.AssistantMessage("Hi!"), new UserMessage("How are you?")), + DeepSeekChatOptions.builder().model("deepseek-chat").build()); + + var request = client.createRequest(prompt, false); + + assertThat(request.messages()).hasSize(3); + var assistantMsg = request.messages().get(1); + assertThat(assistantMsg.reasoningContent()).isNull(); + } + }