Fix Mistral 400 errors after aborted assistant messages

- Skip empty assistant messages (no content, no tool calls) to avoid
  Mistral's 'Assistant message must have either content or tool_calls'
  error
- Remove synthetic assistant bridge message after tool results (Mistral
  no longer requires this as of Dec 2024)
- Add test for empty assistant message handling

Follow-up to #165
This commit is contained in:
Mario Zechner 2025-12-10 21:13:33 +01:00
parent 99b4b1aca0
commit 76312ea7e8
2 changed files with 138 additions and 11 deletions

View file

@ -369,15 +369,6 @@ function convertMessages(
let lastRole: string | null = null;
for (const msg of transformedMessages) {
// Some providers (e.g. Mistral) don't allow user messages directly after tool results
// Insert a synthetic assistant message to bridge the gap
if (compat.requiresAssistantAfterToolResult && lastRole === "toolResult" && msg.role === "user") {
params.push({
role: "assistant",
content: "I have processed the tool results.",
});
}
if (msg.role === "user") {
if (typeof msg.content === "string") {
params.push({
@ -455,7 +446,16 @@ function convertMessages(
},
}));
}
if (assistantMsg.content === null && !assistantMsg.tool_calls) {
// Skip assistant messages that have no content and no tool calls.
// Mistral explicitly requires "either content or tool_calls, but not none".
// Other providers also don't accept empty assistant messages.
// This handles aborted assistant responses that got no content.
const content = assistantMsg.content;
const hasContent =
content !== null &&
content !== undefined &&
(typeof content === "string" ? content.length > 0 : content.length > 0);
if (!hasContent && !assistantMsg.tool_calls) {
continue;
}
params.push(assistantMsg);
@ -570,7 +570,7 @@ function detectCompatFromUrl(baseUrl: string): Required<OpenAICompat> {
supportsReasoningEffort: !isGrok,
maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
requiresToolResultName: isMistral,
requiresAssistantAfterToolResult: isMistral,
requiresAssistantAfterToolResult: false, // Mistral no longer requires this as of Dec 2024
requiresThinkingAsText: isMistral,
requiresMistralToolIds: isMistral,
};