From a2ffa2e643e0e48e98b3129f2c86d5b5852e7181 Mon Sep 17 00:00:00 2001 From: pragnyanramtha Date: Sun, 17 May 2026 05:41:33 +0000 Subject: [PATCH] fix streaming server tool input events --- src/anthropic/lib/streaming/_beta_messages.py | 2 +- src/anthropic/lib/streaming/_messages.py | 2 +- tests/lib/streaming/test_beta_messages.py | 45 ++++++++++++++++++- tests/lib/streaming/test_messages.py | 40 ++++++++++++++++- 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/anthropic/lib/streaming/_beta_messages.py b/src/anthropic/lib/streaming/_beta_messages.py index 5a5a562a0..3865d5251 100644 --- a/src/anthropic/lib/streaming/_beta_messages.py +++ b/src/anthropic/lib/streaming/_beta_messages.py @@ -365,7 +365,7 @@ def build_events( ) ) elif event.delta.type == "input_json_delta": - if content_block.type == "tool_use" or content_block.type == "mcp_tool_use": + if isinstance(content_block, TRACKS_TOOL_INPUT): events_to_fire.append( build( BetaInputJsonEvent, diff --git a/src/anthropic/lib/streaming/_messages.py b/src/anthropic/lib/streaming/_messages.py index 5c0da9992..5a5d993bd 100644 --- a/src/anthropic/lib/streaming/_messages.py +++ b/src/anthropic/lib/streaming/_messages.py @@ -360,7 +360,7 @@ def build_events( ) ) elif event.delta.type == "input_json_delta": - if content_block.type == "tool_use": + if isinstance(content_block, TRACKS_TOOL_INPUT): events_to_fire.append( build( InputJsonEvent, diff --git a/tests/lib/streaming/test_beta_messages.py b/tests/lib/streaming/test_beta_messages.py index 8203864b4..6646ff532 100644 --- a/tests/lib/streaming/test_beta_messages.py +++ b/tests/lib/streaming/test_beta_messages.py @@ -12,10 +12,20 @@ from anthropic import Anthropic, AsyncAnthropic from anthropic._utils import assert_overloads_in_sync, assert_signatures_in_sync from anthropic._compat import PYDANTIC_V1 +from anthropic.types.beta.beta_usage import BetaUsage from anthropic.types.beta.beta_message import BetaMessage from anthropic.lib.streaming._beta_types import ParsedBetaMessageStreamEvent from anthropic.resources.messages.messages import DEPRECATED_MODELS -from anthropic.lib.streaming._beta_messages import TRACKS_TOOL_INPUT, BetaMessageStream, BetaAsyncMessageStream +from anthropic.lib.streaming._beta_messages import ( + TRACKS_TOOL_INPUT, + BetaMessageStream, + BetaAsyncMessageStream, + build_events, +) +from anthropic.types.beta.parsed_beta_message import ParsedBetaMessage +from anthropic.types.beta.beta_input_json_delta import BetaInputJSONDelta +from anthropic.types.beta.beta_server_tool_use_block import BetaServerToolUseBlock +from anthropic.types.beta.beta_raw_content_block_delta_event import BetaRawContentBlockDeltaEvent from .helpers import get_response, to_async_iter @@ -473,3 +483,36 @@ def test_tracks_tool_input_type_alias_is_up_to_date() -> None: f"ContentBlock type {block_type.__name__} has an input property, " f"but is not included in TRACKS_TOOL_INPUT. You probably need to update the TRACKS_TOOL_INPUT type alias." ) + + +def test_server_tool_use_emits_input_json_event() -> None: + message = ParsedBetaMessage( + id="msg_123", + type="message", + role="assistant", + content=[ + BetaServerToolUseBlock( + id="srvtoolu_123", + type="server_tool_use", + name="web_search", + input={"query": "Paris weather"}, + ) + ], + model="claude-sonnet-4-5", + stop_reason=None, + stop_sequence=None, + usage=BetaUsage(input_tokens=10, output_tokens=1), + ) + event = BetaRawContentBlockDeltaEvent( + type="content_block_delta", + index=0, + delta=BetaInputJSONDelta(type="input_json_delta", partial_json='{"query":"Paris weather"}'), + ) + + events = build_events(event=event, message_snapshot=message) + + assert [stream_event.type for stream_event in events] == ["content_block_delta", "input_json"] + input_json_event = events[1] + assert input_json_event.type == "input_json" + assert input_json_event.partial_json == '{"query":"Paris weather"}' + assert input_json_event.snapshot == {"query": "Paris weather"} diff --git a/tests/lib/streaming/test_messages.py b/tests/lib/streaming/test_messages.py index b86a39063..1b7966c0b 100644 --- a/tests/lib/streaming/test_messages.py +++ b/tests/lib/streaming/test_messages.py @@ -10,10 +10,15 @@ from anthropic import Stream, Anthropic, AsyncStream, AsyncAnthropic from anthropic._utils import assert_signatures_in_sync from anthropic._compat import PYDANTIC_V1 +from anthropic.types.usage import Usage from anthropic.lib.streaming import ParsedMessageStreamEvent from anthropic.types.message import Message from anthropic.resources.messages import DEPRECATED_MODELS -from anthropic.lib.streaming._messages import TRACKS_TOOL_INPUT +from anthropic.types.parsed_message import ParsedMessage +from anthropic.types.input_json_delta import InputJSONDelta +from anthropic.lib.streaming._messages import TRACKS_TOOL_INPUT, build_events +from anthropic.types.server_tool_use_block import ServerToolUseBlock +from anthropic.types.raw_content_block_delta_event import RawContentBlockDeltaEvent from .helpers import get_response, to_async_iter @@ -349,3 +354,36 @@ def test_tracks_tool_input_type_alias_is_up_to_date() -> None: f"ContentBlock type {block_type.__name__} has an input property, " f"but is not included in TRACKS_TOOL_INPUT. You probably need to update the TRACKS_TOOL_INPUT type alias." ) + + +def test_server_tool_use_emits_input_json_event() -> None: + message = ParsedMessage( + id="msg_123", + type="message", + role="assistant", + content=[ + ServerToolUseBlock( + id="srvtoolu_123", + type="server_tool_use", + name="web_search", + input={"query": "Paris weather"}, + ) + ], + model="claude-sonnet-4-5", + stop_reason=None, + stop_sequence=None, + usage=Usage(input_tokens=10, output_tokens=1), + ) + event = RawContentBlockDeltaEvent( + type="content_block_delta", + index=0, + delta=InputJSONDelta(type="input_json_delta", partial_json='{"query":"Paris weather"}'), + ) + + events = build_events(event=event, message_snapshot=message) + + assert [stream_event.type for stream_event in events] == ["content_block_delta", "input_json"] + input_json_event = events[1] + assert input_json_event.type == "input_json" + assert input_json_event.partial_json == '{"query":"Paris weather"}' + assert input_json_event.snapshot == {"query": "Paris weather"}