// Tool call helper functions for translator // Generate unique tool call ID export function generateToolCallId() { return `call_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`; } // Ensure all tool_calls have id field and arguments is string (some providers require it) export function ensureToolCallIds(body) { if (!body.messages || !Array.isArray(body.messages)) return body; for (const msg of body.messages) { if (msg.role === "assistant" && msg.tool_calls && Array.isArray(msg.tool_calls)) { for (const tc of msg.tool_calls) { if (!tc.id) { tc.id = generateToolCallId(); } if (!tc.type) { tc.type = "function"; } // Ensure arguments is JSON string, not object if (tc.function?.arguments && typeof tc.function.arguments !== "string") { tc.function.arguments = JSON.stringify(tc.function.arguments); } } } } return body; } // Get tool_call ids from assistant message (OpenAI format: tool_calls, Claude format: tool_use in content) export function getToolCallIds(msg) { if (msg.role !== "assistant") return []; const ids = []; // OpenAI format: tool_calls array if (msg.tool_calls && Array.isArray(msg.tool_calls)) { for (const tc of msg.tool_calls) { if (tc.id) ids.push(tc.id); } } // Claude format: tool_use blocks in content if (Array.isArray(msg.content)) { for (const block of msg.content) { if (block.type === "tool_use" && block.id) { ids.push(block.id); } } } return ids; } // Check if user message has tool_result for given ids (OpenAI format: role=tool, Claude format: tool_result in content) export function hasToolResults(msg, toolCallIds) { if (!msg || !toolCallIds.length) return false; // OpenAI format: role = "tool" with tool_call_id if (msg.role === "tool" && msg.tool_call_id) { return toolCallIds.includes(msg.tool_call_id); } // Claude format: tool_result blocks in user message content if (msg.role === "user" && Array.isArray(msg.content)) { for (const block of msg.content) { if (block.type === "tool_result" && toolCallIds.includes(block.tool_use_id)) { return true; } } } return false; } // Fix missing tool responses - insert empty tool_result if assistant has tool_use but next message has no tool_result export function fixMissingToolResponses(body) { if (!body.messages || !Array.isArray(body.messages)) return body; const newMessages = []; for (let i = 0; i < body.messages.length; i++) { const msg = body.messages[i]; const nextMsg = body.messages[i + 1]; newMessages.push(msg); // Check if this is assistant with tool_calls/tool_use const toolCallIds = getToolCallIds(msg); if (toolCallIds.length === 0) continue; // Check if next message has tool_result if (nextMsg && !hasToolResults(nextMsg, toolCallIds)) { // Insert tool responses for each tool_call for (const id of toolCallIds) { // OpenAI format: role = "tool" newMessages.push({ role: "tool", tool_call_id: id, content: "" }); } } } body.messages = newMessages; return body; }