fix: defer inlineData parts to end of array for parallel tool calls (#91)

When multiple tool_results contain images, the inlineData parts were
being interleaved between functionResponse parts, breaking Claude's API
requirement that functionResponse parts be consecutive.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Badri Narayanan S
2026-01-14 21:04:36 +05:30
parent 97e1d9c417
commit 772dabe7e4
2 changed files with 9 additions and 4 deletions

View File

@@ -35,6 +35,7 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
} }
const parts = []; const parts = [];
const deferredInlineData = []; // Collect inlineData to add at the end (Issue #91)
for (const block of content) { for (const block of content) {
if (!block) continue; if (!block) continue;
@@ -152,8 +153,9 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
parts.push({ functionResponse }); parts.push({ functionResponse });
// Add any images from the tool result as separate parts // Defer images from the tool result to end of parts array (Issue #91)
parts.push(...imageParts); // This ensures all functionResponse parts are consecutive
deferredInlineData.push(...imageParts);
} else if (block.type === 'thinking') { } else if (block.type === 'thinking') {
// Handle thinking blocks with signature compatibility check // Handle thinking blocks with signature compatibility check
if (block.signature && block.signature.length >= MIN_SIGNATURE_LENGTH) { if (block.signature && block.signature.length >= MIN_SIGNATURE_LENGTH) {
@@ -183,5 +185,9 @@ export function convertContentToParts(content, isClaudeModel = false, isGeminiMo
} }
} }
// Add deferred inlineData at the end (Issue #91)
// This ensures functionResponse parts are consecutive, which Claude's API requires
parts.push(...deferredInlineData);
return parts; return parts;
} }

View File

@@ -95,8 +95,7 @@ export function convertAnthropicToGoogle(anthropicRequest) {
} }
// Convert messages to contents, then filter unsigned thinking blocks // Convert messages to contents, then filter unsigned thinking blocks
for (let i = 0; i < processedMessages.length; i++) { for (const msg of processedMessages) {
const msg = processedMessages[i];
let msgContent = msg.content; let msgContent = msg.content;
// For assistant messages, process thinking blocks and reorder content // For assistant messages, process thinking blocks and reorder content