Skip to content

fix(ai): preserve caller-referenced tool calls in pruneMessages#12602

Open
doganarif wants to merge 1 commit intovercel:mainfrom
doganarif:fix-prune-messages-caller-refs
Open

fix(ai): preserve caller-referenced tool calls in pruneMessages#12602
doganarif wants to merge 1 commit intovercel:mainfrom
doganarif:fix-prune-messages-caller-refs

Conversation

@doganarif
Copy link

Background

When using Anthropic's code_execution_20250825 with programmatic tool calling (allowedCallers), convertToModelMessages splits a multi-step assistant UIMessage into separate ModelMessages at step boundaries. A server_tool_use block ends up in one message, and the dependent tool_use (with caller.toolId pointing back to it) ends up in another.

pruneMessages builds keptToolCallIds by scanning the last N messages, then strips tool-call/result parts from older messages if their ID is not in the set. It did not follow providerOptions.anthropic.caller.toolId references, so the server_tool_use message was removed while the tool_use that references it was kept. Anthropic then rejects the request with "source tool not found".

Fix

After the initial keptToolCallIds scan, do a transitive pass over all messages: if a kept tool-call part has a caller.toolId in its providerOptions, add that referenced ID to the kept set. The loop repeats until no new IDs are added, handling multi-level dependencies.

Test

Added a test with a code_execution server tool use and a programmatic lookup tool use that references it via caller.toolId. Verifies both tool-call IDs and their results survive pruning.

Fixes #12504

pruneMessages builds a set of kept tool call IDs by scanning the
last N messages, then strips tool-call/result parts with unknown IDs
from older messages. It did not follow providerOptions caller
references (e.g. Anthropic code_execution caller.toolId), causing
the referenced server_tool_use to be removed while the dependent
tool_use was kept. This made Anthropic reject the request with
"source tool not found".

Add a transitive pass after the initial scan: if a kept tool-call
has a caller.toolId reference, that ID is added to the kept set.

Fixes vercel#12504

const result = pruneMessages({
messages,
toolCalls: 'before-last-5-messages',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
toolCalls: 'before-last-5-messages',
toolCalls: 'before-last-3-messages',

Test for "caller tool id dependencies" uses a window size (before-last-5-messages) that already includes the referenced caller tool's result in the initial scan, so the transitive closure code is never exercised and the test passes even without the new feature code.

Fix on Vercel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant