diff --git a/src/google/adk/models/anthropic_llm.py b/src/google/adk/models/anthropic_llm.py index f6622628d6..189e5b41e3 100644 --- a/src/google/adk/models/anthropic_llm.py +++ b/src/google/adk/models/anthropic_llm.py @@ -705,6 +705,7 @@ def message_to_generate_content_response( ) return LlmResponse( + model_version=message.model, content=types.Content( role="model", parts=parts, @@ -1011,10 +1012,14 @@ async def _generate_content_streaming( thinking_tokens: int | None = None cached_input_tokens: int | None = None cache_creation_tokens: int | None = None + model_version: str | None = None stop_reason: Optional[anthropic_types.StopReason] = None async for event in raw_stream: if event.type == "message_start": + event_model = getattr(event.message, "model", None) + if isinstance(event_model, str): + model_version = event_model input_tokens = _extract_prompt_token_count(event.message.usage) output_tokens = event.message.usage.output_tokens thinking_tokens = _extract_thinking_token_count(event.message.usage) @@ -1148,6 +1153,7 @@ async def _generate_content_streaming( ) yield LlmResponse( + model_version=model_version, content=types.Content(role="model", parts=all_parts), usage_metadata=usage_metadata, finish_reason=to_google_genai_finish_reason(stop_reason), diff --git a/tests/unittests/models/test_anthropic_llm.py b/tests/unittests/models/test_anthropic_llm.py index 1f25a21700..6a77c34507 100644 --- a/tests/unittests/models/test_anthropic_llm.py +++ b/tests/unittests/models/test_anthropic_llm.py @@ -3457,3 +3457,71 @@ def test_anthropic_config_allows_thinking_budget_without_thinking_level(): assert config.effort == "high" assert config.thinking_config.thinking_budget == 2048 assert config.thinking_config.thinking_level is None + + +def test_message_to_generate_content_response_sets_model_version( + generate_content_response, +): + """The non-streaming response preserves the resolved Anthropic model.""" + response = message_to_generate_content_response(generate_content_response) + + assert response.model_version == generate_content_response.model + + +@pytest.mark.asyncio +async def test_streaming_sets_model_version_from_message_start(): + """The final streaming response preserves the resolved Anthropic model.""" + requested_model = "claude-sonnet-4" + resolved_model = "claude-sonnet-4-20250514" + llm = AnthropicLlm(model=requested_model) + + events = [ + MagicMock( + type="message_start", + message=MagicMock( + model=resolved_model, + usage=MagicMock(input_tokens=5, output_tokens=0), + ), + ), + MagicMock( + type="content_block_start", + index=0, + content_block=anthropic_types.TextBlock(text="", type="text"), + ), + MagicMock( + type="content_block_delta", + index=0, + delta=anthropic_types.TextDelta( + text="Hello", + type="text_delta", + ), + ), + MagicMock( + type="message_delta", + delta=MagicMock(stop_reason="end_turn"), + usage=MagicMock(output_tokens=1), + ), + MagicMock(type="message_stop"), + ] + + mock_client = MagicMock() + mock_client.messages.create = AsyncMock( + return_value=_make_mock_stream_events(events) + ) + + llm_request = LlmRequest( + model=requested_model, + contents=[Content(role="user", parts=[Part.from_text(text="Hi")])], + config=types.GenerateContentConfig(system_instruction="Test"), + ) + + with mock.patch.object(llm, "_anthropic_client", mock_client): + responses = [ + response + async for response in llm.generate_content_async( + llm_request, + stream=True, + ) + ] + + assert responses[-1].model_version == resolved_model