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 <benjamin.j.brandt@gmail.com>
This commit is contained in:
Peter König
2025-12-09 20:54:16 +01:00
committed by GitHub
co-authored by Ben Brandt
parent bfab0b71e0
commit 5dd8561b06
2 changed files with 11 additions and 2 deletions
+2
View File
@@ -155,6 +155,8 @@ pub enum RequestMessage {
content: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
tool_calls: Vec<ToolCall>,
#[serde(default, skip_serializing_if = "Option::is_none")]
reasoning_content: Option<String>,
},
User {
content: String,
@@ -332,9 +332,11 @@ pub fn into_deepseek(
model: &deepseek::Model,
max_output_tokens: Option<u64>,
) -> 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<String> = 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(),
});
}
}