From 5dd8561b06c8af4ee46f3aa8bcf839f208b8c7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20K=C3=B6nig?= Date: Tue, 9 Dec 2025 20:54:16 +0100 Subject: [PATCH] Fix DeepSeek Reasoner tool-call handling and add reasoning_content support (#44301) ## Closes #43887 ## Release Notes: ### Problem DeepSeek's reasoning mode API requires `reasoning_content` to be included in assistant messages that precede tool calls. Without it, the API returns a 400 error: ``` Missing `reasoning_content` field in the assistant message at message index 2 ``` ### Added/Fixed/Improved - Add `reasoning_content` field to `RequestMessage::Assistant` in `crates/deepseek/src/deepseek.rs` - Accumulate thinking content from `MessageContent::Thinking` and attach it to the next assistant/tool-call message - Wire reasoning content through the language model provider in `crates/language_models/src/provider/deepseek.rs` ### Testing - Verified with DeepSeek Reasoner model using tool calls - Confirmed reasoning content is properly included in API requests Fixes tool-call errors when using DeepSeek's reasoning mode. --------- Co-authored-by: Ben Brandt --- crates/deepseek/src/deepseek.rs | 2 ++ crates/language_models/src/provider/deepseek.rs | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/deepseek/src/deepseek.rs b/crates/deepseek/src/deepseek.rs index 64a1cbe5d96354260c2bf84a43ed70be7336aa7a..e978aa08048bfa4c7b7b203ce6b405ba8a0a7d0c 100644 --- a/crates/deepseek/src/deepseek.rs +++ b/crates/deepseek/src/deepseek.rs @@ -155,6 +155,8 @@ pub enum RequestMessage { content: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] tool_calls: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + reasoning_content: Option, }, User { content: String, diff --git a/crates/language_models/src/provider/deepseek.rs b/crates/language_models/src/provider/deepseek.rs index 4bc7164f421bfbaa075c72faff7f731c0defcdba..91b83bb9f1d0f08fe70f5e750ff8ce993a7afd7f 100644 --- a/crates/language_models/src/provider/deepseek.rs +++ b/crates/language_models/src/provider/deepseek.rs @@ -332,9 +332,11 @@ pub fn into_deepseek( model: &deepseek::Model, max_output_tokens: Option, ) -> deepseek::Request { - let is_reasoner = *model == deepseek::Model::Reasoner; + let is_reasoner = model == &deepseek::Model::Reasoner; let mut messages = Vec::new(); + let mut current_reasoning: Option = None; + for message in request.messages { for content in message.content { match content { @@ -343,10 +345,14 @@ pub fn into_deepseek( Role::Assistant => deepseek::RequestMessage::Assistant { content: Some(text), tool_calls: Vec::new(), + reasoning_content: current_reasoning.take(), }, Role::System => deepseek::RequestMessage::System { content: text }, }), - MessageContent::Thinking { .. } => {} + MessageContent::Thinking { text, .. } => { + // Accumulate reasoning content for next assistant message + current_reasoning.get_or_insert_default().push_str(&text); + } MessageContent::RedactedThinking(_) => {} MessageContent::Image(_) => {} MessageContent::ToolUse(tool_use) => { @@ -369,6 +375,7 @@ pub fn into_deepseek( messages.push(deepseek::RequestMessage::Assistant { content: None, tool_calls: vec![tool_call], + reasoning_content: current_reasoning.take(), }); } }