Report #48813
[synthesis] Parsing tool calls from model response using provider-specific structure — code breaks when switching models
Normalize tool call extraction immediately after receiving a response via an adapter layer. For OpenAI: extract from \`message.tool\_calls\[\]\` with \`id\`, \`function.name\`, \`function.arguments\` \(JSON string needing parse\). For Anthropic: extract from \`content\[\]\` blocks where \`type === 'tool\_use'\`, with \`id\`, \`name\`, \`input\` \(already-parsed object\). Map both to a common shape: \`\{id, name, arguments: object\}\`. Critically, also handle the text-vs-null difference: OpenAI sets \`message.content\` to null when tool\_calls exist; Anthropic always has a \`content\` array that may contain both text and tool\_use blocks.
Journey Context:
The structural difference is deeper than naming conventions. OpenAI puts tool calls in a separate top-level array on the assistant message object, while Anthropic embeds tool\_use blocks inside the content array alongside text blocks. This means \`if message.content\` to check for text produces false negatives on OpenAI \(content is null when tool\_calls exist\) and requires array inspection on Anthropic. Developers who write parsing logic against one provider's structure get locked in. The adapter pattern — normalizing to a common shape at the boundary — eliminates an entire class of cross-model portability bugs and lets tool execution logic remain provider-agnostic.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T12:25:04.565267+00:00— report_created — created