From b8c1b54f9eaf78d3bcf073f6495cfaeb8b3bf2b4 Mon Sep 17 00:00:00 2001 From: Umesh Yadav <23421535+imumesh18@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:05:22 +0530 Subject: [PATCH] language_models: Fix Mistral tool->user message sequence handling (#31736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #31491 ### Problem Mistral API enforces strict conversation flow requirements that other providers don't. Specifically, after a `tool` message, the next message **must** be from the `assistant` role, not `user`. This causes the error: ``` "Unexpected role 'user' after role 'tool'" ``` This can also occur in normal conversation flow where mistral doesn't return the assistant message but that is something which can't be reproduce reliably. ### Root Cause When users interrupt an ongoing tool call sequence by sending a new message, we insert a `user` message directly after a `tool` message, violating Mistral's protocol. **Expected Mistral flow:** ``` user → assistant (with tool_calls) → tool (results) → assistant (processes results) → user (next input) ``` **What we were doing:** ``` user → assistant (with tool_calls) → tool (results) → user (interruption) ❌ ``` ### Solution Insert an empty `assistant` message between any `tool` → `user` sequence in the Mistral provider's request construction. This satisfies Mistral's API requirements without affecting other providers or requiring UX changes. ### Testing To reproduce the original error: 1. Start agent chat with `codestral-latest` 2. Send: "Describe this project using tool call only" 3. Once tool calls begin, send: "stop this" 4. Main branch: API error 5. This fix: Works correctly Release Notes: - Fixed Mistral tool calling in some cases --- .../language_models/src/provider/mistral.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/language_models/src/provider/mistral.rs b/crates/language_models/src/provider/mistral.rs index e739ccd99d..87c23e49e7 100644 --- a/crates/language_models/src/provider/mistral.rs +++ b/crates/language_models/src/provider/mistral.rs @@ -444,6 +444,35 @@ pub fn into_mistral( } } + // The Mistral API requires that tool messages be followed by assistant messages, + // not user messages. When we have a tool->user sequence in the conversation, + // we need to insert a placeholder assistant message to maintain proper conversation + // flow and prevent API errors. This is a Mistral-specific requirement that differs + // from other language model APIs. + let messages = { + let mut fixed_messages = Vec::with_capacity(messages.len()); + let mut messages_iter = messages.into_iter().peekable(); + + while let Some(message) = messages_iter.next() { + let is_tool_message = matches!(message, mistral::RequestMessage::Tool { .. }); + fixed_messages.push(message); + + // Insert assistant message between tool and user messages + if is_tool_message { + if let Some(next_msg) = messages_iter.peek() { + if matches!(next_msg, mistral::RequestMessage::User { .. }) { + fixed_messages.push(mistral::RequestMessage::Assistant { + content: Some(" ".to_string()), + tool_calls: Vec::new(), + }); + } + } + } + } + + fixed_messages + }; + mistral::Request { model, messages,