(anthropicMessages: Anthropic.Messages.MessageParam[])
| 46 | } |
| 47 | |
| 48 | export function convertToMistralMessages(anthropicMessages: Anthropic.Messages.MessageParam[]): MistralMessage[] { |
| 49 | const mistralMessages: MistralMessage[] = [] |
| 50 | |
| 51 | for (const anthropicMessage of anthropicMessages) { |
| 52 | if (typeof anthropicMessage.content === "string") { |
| 53 | mistralMessages.push({ |
| 54 | role: anthropicMessage.role, |
| 55 | content: anthropicMessage.content, |
| 56 | }) |
| 57 | } else { |
| 58 | if (anthropicMessage.role === "user") { |
| 59 | const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{ |
| 60 | nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] |
| 61 | toolMessages: Anthropic.ToolResultBlockParam[] |
| 62 | }>( |
| 63 | (acc, part) => { |
| 64 | if (part.type === "tool_result") { |
| 65 | acc.toolMessages.push(part) |
| 66 | } else if (part.type === "text" || part.type === "image") { |
| 67 | acc.nonToolMessages.push(part) |
| 68 | } // user cannot send tool_use messages |
| 69 | return acc |
| 70 | }, |
| 71 | { nonToolMessages: [], toolMessages: [] }, |
| 72 | ) |
| 73 | |
| 74 | // If there are tool results, handle them |
| 75 | // Mistral's message order is strict: user → assistant → tool → assistant |
| 76 | // We CANNOT put user messages after tool messages |
| 77 | if (toolMessages.length > 0) { |
| 78 | // Convert tool_result blocks to Mistral tool messages |
| 79 | for (const toolResult of toolMessages) { |
| 80 | let resultContent: string |
| 81 | if (typeof toolResult.content === "string") { |
| 82 | resultContent = toolResult.content |
| 83 | } else if (Array.isArray(toolResult.content)) { |
| 84 | // Extract text from content blocks |
| 85 | resultContent = toolResult.content |
| 86 | .filter((block): block is Anthropic.TextBlockParam => block.type === "text") |
| 87 | .map((block) => block.text) |
| 88 | .join("\n") |
| 89 | } else { |
| 90 | resultContent = "" |
| 91 | } |
| 92 | |
| 93 | mistralMessages.push({ |
| 94 | role: "tool", |
| 95 | toolCallId: normalizeMistralToolCallId(toolResult.tool_use_id), |
| 96 | content: resultContent, |
| 97 | } as ToolMessage & { role: "tool" }) |
| 98 | } |
| 99 | // Note: We intentionally skip any non-tool user content when there are tool results |
| 100 | // because Mistral doesn't allow user messages after tool messages |
| 101 | } else if (nonToolMessages.length > 0) { |
| 102 | // Only add user content if there are NO tool results |
| 103 | mistralMessages.push({ |
| 104 | role: "user", |
| 105 | content: nonToolMessages.map((part) => { |
no test coverage detected