fix: Normalize tool-call IDs for Anthropic compatibility#153
Open
danny-avila wants to merge 1 commit intodevfrom
Open
fix: Normalize tool-call IDs for Anthropic compatibility#153danny-avila wants to merge 1 commit intodevfrom
danny-avila wants to merge 1 commit intodevfrom
Conversation
Cross-provider conversations carrying OpenAI Responses-style tool-call IDs (e.g. \`fc_...|call_...\`) hit a 400 from Anthropic on replay because the IDs contain \`|\` and can exceed 64 chars, violating Anthropic's \`^[a-zA-Z0-9_-]+$\` and length constraints. Adds \`normalizeAnthropicToolCallId\` and applies it at the four sites that emit IDs to the wire — \`_convertLangChainToolCallToAnthropic\` and the three \`tool_result.tool_use_id\` constructions in \`_ensureMessageContents\`. Server tool IDs (\`srvtoolu_\` prefix) are left untouched. Non-compliant inputs are sanitized then suffixed with a 10-hex-char SHA-256 prefix of the *original* ID, so two long IDs that share a 64-char prefix (or two short IDs differing only by an invalid char like \`|\` vs \`.\`) still produce distinct outputs — Anthropic's "tool_use ids must be unique" check stays satisfied. The function is pure and deterministic so paired \`tool_use.id\` and \`tool_result.tool_use_id\` remain matched without a session map. Verified live against the Anthropic API: previously-rejected payloads now succeed, including the colliding-prefix case.
Owner
Author
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a cross-provider 400 from Anthropic when tool-call IDs originating from OpenAI's Responses API (or any other ID source that violates Anthropic's regex) flow into an Anthropic call as part of replayed history.
The bug
OpenAI Responses API generates tool-call IDs that can:
|and other characters outside Anthropic's^[a-zA-Z0-9_-]+$constraintWhen this library replays such history to Anthropic (e.g. for an OpenAI → Anthropic handoff), the IDs flow through verbatim and Anthropic 400s with:
The fix
normalizeAnthropicToolCallIdis applied at the four wire-bound sites insrc/llm/anthropic/utils/message_inputs.ts:_convertLangChainToolCallToAnthropic— assistanttool_use.idtool_result.tool_use_idconstructions in_ensureMessageContentsCompliant IDs pass through unchanged. Non-compliant IDs are sanitized (
[^a-zA-Z0-9_-]→_), then truncated to 53 chars and suffixed with_<10-hex-char SHA-256>of the original input. This ensures:a|bvsa.b) also produce distinct outputstool_use.idandtool_result.tool_use_idstay matched without any session mapServer tool IDs (
srvtoolu_prefix) are explicitly excluded — they're Anthropic-internal and always compliant.Test plan
src/llm/anthropic/utils/tool-id-normalization.test.ts:_convertMessagesToAnthropicPayloadconfirmstool_use.idandtool_result.tool_use_idreceive the same normalized valuesrc/llm/anthropicsuite still passes (1 pre-existing flake in abort-signal test, reproduces on baseline)fc_67abc1234def567|call_abc123def456ghi789jkl0mnopqrsnow succeeds. Collision case with two 96-char IDs sharing an 80-char prefix also accepted.Scope
Intentionally narrow. Related cross-provider concerns (Anthropic thinking-block flatten for OpenAI, wrapper coverage) are tracked separately on #140.