fix: preserve reasoning_content for DeepSeek thinking mode on multi-turn tool calls (#10366)
Co-authored-by: pcace <{ID}+{username}@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0b98172aca
commit
1e03bbb561
@@ -2313,27 +2313,94 @@ impl Agent {
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
// When thinking arrived in an earlier stream chunk it was stored as
|
||||
// a standalone thinking-only message; reuse that thinking on the
|
||||
// tool-call messages and drop the standalone so it isn't duplicated.
|
||||
let response_thinking = if direct_thinking.is_empty() {
|
||||
let prior = messages_to_add.messages().iter().rposition(|m| {
|
||||
m.role == response.role
|
||||
&& !m.content.is_empty()
|
||||
&& m.content.iter().all(|c| {
|
||||
matches!(
|
||||
// When thinking arrived in earlier stream chunks it was stored as
|
||||
// standalone thinking-only messages; reuse that thinking on the
|
||||
// tool-call messages and drop the standalone messages so the
|
||||
// thinking isn't duplicated.
|
||||
// Always accumulate ALL prior thinking — even when
|
||||
// direct_thinking is non-empty (reasoning arrived on the same
|
||||
// chunk as tool_calls) — because otherwise only the last chunk's
|
||||
// reasoning ends up on split tool-call messages.
|
||||
// Also extract thinking from mixed (thinking+text) messages,
|
||||
// not just pure-thinking-only ones.
|
||||
let mut accumulated_prior: Vec<MessageContent> = Vec::new();
|
||||
let mut indices_to_remove: Vec<usize> = Vec::new();
|
||||
for (idx, m) in messages_to_add.messages_mut().iter_mut().enumerate()
|
||||
{
|
||||
if m.role != response.role || m.content.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let thinking_only = m.content.iter().all(|c| {
|
||||
matches!(
|
||||
c,
|
||||
MessageContent::Thinking(_)
|
||||
| MessageContent::RedactedThinking(_)
|
||||
)
|
||||
});
|
||||
let has_thinking = m.content.iter().any(|c| {
|
||||
matches!(
|
||||
c,
|
||||
MessageContent::Thinking(_)
|
||||
| MessageContent::RedactedThinking(_)
|
||||
)
|
||||
});
|
||||
if has_thinking {
|
||||
// Only accumulate thinking from messages that
|
||||
// have not already been split into tool-call
|
||||
// request_msg items — prior-split messages
|
||||
// already carry their own thinking copy.
|
||||
if !m.content.iter().any(|c| {
|
||||
matches!(c, MessageContent::ToolRequest(_))
|
||||
}) {
|
||||
for c in &m.content {
|
||||
if matches!(
|
||||
c,
|
||||
MessageContent::Thinking(_)
|
||||
| MessageContent::RedactedThinking(_)
|
||||
)
|
||||
})
|
||||
});
|
||||
match prior {
|
||||
Some(idx) => messages_to_add.remove(idx).content,
|
||||
None => Vec::new(),
|
||||
) {
|
||||
accumulated_prior.push(c.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if thinking_only {
|
||||
indices_to_remove.push(idx);
|
||||
} else if has_thinking
|
||||
&& !m.content.iter().any(|c| {
|
||||
matches!(c, MessageContent::ToolRequest(_))
|
||||
})
|
||||
{
|
||||
// Strip thinking blocks from mixed text+thinking
|
||||
// messages so the same signed/unsigned thinking is not
|
||||
// duplicated when carried onto the tool-call request
|
||||
// messages below. Messages that already contain tool
|
||||
// requests are prior-split request_msg items whose
|
||||
// thinking was already attached — stripping their
|
||||
// thinking would leave only the last split message
|
||||
// with reasoning, violating the signed-thinking
|
||||
// dedup expectation that the first split message
|
||||
// retains it.
|
||||
m.content.retain(|c| {
|
||||
!matches!(
|
||||
c,
|
||||
MessageContent::Thinking(_)
|
||||
| MessageContent::RedactedThinking(_)
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
// Remove in reverse order to preserve indices
|
||||
for idx in indices_to_remove.into_iter().rev() {
|
||||
messages_to_add.remove(idx);
|
||||
}
|
||||
let response_thinking = if direct_thinking.is_empty() {
|
||||
accumulated_prior
|
||||
} else if accumulated_prior.is_empty() {
|
||||
direct_thinking
|
||||
} else {
|
||||
let mut merged = accumulated_prior;
|
||||
merged.extend(direct_thinking);
|
||||
merged
|
||||
};
|
||||
|
||||
for request in frontend_requests.iter().chain(remaining_requests.iter()) {
|
||||
|
||||
Reference in New Issue
Block a user