Skip to content

fix(ai): keep Anthropic stream tool arguments after server tool blocks - #984

Open
breken-ai wants to merge 1 commit into
PostHog:mainfrom
breken-ai:fix/anthropic-stream-block-index
Open

breken-ai wants to merge 1 commit into
PostHog:mainfrom
breken-ai:fix/anthropic-stream-block-index

Conversation

@breken-ai

Copy link
Copy Markdown

💡 Motivation and Context

Anthropic stream events address content blocks by their index in the message. _AnthropicStreamAccumulator passed content_blocks to handle_anthropic_tool_delta and finalize_anthropic_tool_input, which look the block up with content_blocks[event.index]. But content_blocks only holds the block types the wrapper formats (text, thinking, redacted_thinking, tool_use). A server_tool_use or web_search_tool_result block still takes an index in the stream and is skipped in the list.

So once a response uses a server tool (web search, web fetch, code execution) before a client tool call, the list and the stream index no longer line up:

  • the tool call's input_json_delta events point past the end of the list, and $ai_output_choices records the call with arguments: {};
  • if a tool call came before the server tool, the server tool's input deltas land on the next tool block and its own arguments are replaced.

The fix keeps a second list that lines up with the stream index (an empty placeholder for block types that are not formatted) and uses it only for tool deltas and finalization. content_blocks and the captured output are unchanged.

💚 How did you test it?

  • New test test_streaming_tool_call_after_server_tool_blocks builds the stream from the real anthropic.types events: server_tool_use (0), web_search_tool_result (1), text (2), tool_use with two input_json_delta chunks (3). It goes through the wrapped Anthropic client.
    • On main (4a138e6) it fails: the tool call is captured with 'arguments': {}.
    • With the fix it passes with {'city': 'SF'}.
  • pytest posthog 4144 passed, 16 skipped. ruff format --check, ruff check, mypy through mypy-baseline filter and python -W error -c "import posthog" are clean.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file

🤖 Agent context

Autonomy: Fully autonomous

  • Found and fixed by an autonomous agent (Claude Code, Claude Opus) while reviewing the AI wrappers' streaming accumulators. The failing test was run against main before the fix.
  • I kept the fix inside the accumulator instead of changing the handle_anthropic_* helper signatures, so the non-streaming path and the formatted output stay as they are.

Commits here are unsigned; this repo squash-merges, so GitHub signs the merged commit.

Stream delta and stop events address content blocks by their index in the
message. The accumulator looked them up in content_blocks, which skips block
types it does not format (server_tool_use, web_search_tool_result, ...). After
a web search, a client tool call's input_json_delta events pointed past the
end of the list (arguments captured as {}) or at a different tool block.

Keep a list that lines up with the stream index and use it for tool deltas
and finalization. content_blocks and the captured output are unchanged.
@breken-ai
breken-ai requested a review from a team as a code owner September 26, 2026 04:25

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant