Skip to content

AnthropicLlm never populates LlmResponse.model_version #6847

Description

@omar-infinitusai

🔴 Required Information

Describe the Bug:
AnthropicLlm (google/adk/models/anthropic_llm.py) never sets LlmResponse.model_version,
on either the streaming or non-streaming path. Every other first-party model wrapper populates
it from the underlying API response:

  • google_llm.py sets model_version=model from the Gemini response.
  • lite_llm.py sets model_version=response.model / model_version=part.model (this covers
    Claude-via-LiteLLM too, so that path does not have this bug).
  • apigee_llm.py sets model_version=response.get('model').

AnthropicLlm has the same data available and does not read it:

  • Non-streaming: message_to_generate_content_response() (anthropic_llm.py:672) takes
    message: anthropic_types.Message and builds the LlmResponse at line 686 without ever
    touching message.model — the Anthropic SDK's Message object exposes the actual resolved
    model id there.
  • Streaming: the message_start event handler (anthropic_llm.py:998) reads
    event.message.usage for token accounting (lines 999-1002) but never reads
    event.message.model, so the aggregated LlmResponse yielded at the end of the stream
    (anthropic_llm.py:1112) also has no model_version.

This matters because Anthropic model aliases (e.g. claude-sonnet-5) resolve to a dated
snapshot server-side, and model_version is the mechanism ADK already uses elsewhere (event
telemetry, DatabaseSessionService-persisted Event rows, the gen_ai.response.model span
attribute in telemetry/tracing.py) to record which snapshot actually served a given response.
For the Anthropic direct-API path, all of that ends up null.

Steps to Reproduce:

  1. Install google-adk (reproduced on 2.7.0, but present since the module's introduction).
  2. Configure an LlmAgent with model=AnthropicLlm(model="claude-sonnet-5") (ANTHROPIC_API_KEY set).
  3. Run any turn, streaming or non-streaming, and inspect the yielded LlmResponse(s) — or, if
    using DatabaseSessionService/any other persisted SessionService, inspect the stored
    Event row for that turn.
  4. Compare LlmResponse.model_version against the same check with Gemini or LiteLlm(model="claude-...").

Expected Behavior:
LlmResponse.model_version is populated with the model id/snapshot Anthropic actually served
(message.model non-streaming, event.message.model streaming), matching the behavior of
google_llm.py, lite_llm.py, and apigee_llm.py.

Observed Behavior:
LlmResponse.model_version is always None for every response AnthropicLlm produces, on
both the streaming and non-streaming paths.

Environment Details:

  • ADK Library Version (pip show google-adk): 2.7.0
  • Desktop OS: macOS
  • Python Version (python -V): 3.13.7

Model Information:

  • Are you using LiteLLM: No — using AnthropicLlm directly (AsyncAnthropic, keyed by ANTHROPIC_API_KEY), not the LiteLLM path.
  • Which model is being used: claude-sonnet-5 (also reproduces on other Claude ids)

🟡 Optional Information

Regression:
Not a regression — model_version appears to have never been wired into AnthropicLlm since
the class was introduced.

Additional Context:
This looks like the same class of gap as #5394 ("AnthropicLlm never populates
LlmResponse.finish_reason"), which was part of a batch of AnthropicLlm parity issues
(#5392#5397) filed against this file. That issue's linked PR (#5512) showed the finish_reason
line had originally been commented out with # TODO: Deal with these later, and although both
community PRs for it (#5441, #5512) were closed unmerged, the fix did land internally by 2.7.0
(anthropic_llm.py:1121 now sets finish_reason=to_google_genai_finish_reason(stop_reason)).
model_version doesn't appear to be tracked anywhere in that batch or elsewhere in the tracker —
filing this separately so it doesn't fall through the same gap.

Minimal Reproduction Code:

from google.adk.models.anthropic_llm import AnthropicLlm
from google.adk.models.llm_request import LlmRequest
from google.genai import types

llm = AnthropicLlm(model="claude-sonnet-5")
request = LlmRequest(
    contents=[types.Content(role="user", parts=[types.Part.from_text(text="hi")])],
)

async def main():
    async for response in llm.generate_content_async(request):
        print("model_version:", response.model_version)  # always None

import asyncio
asyncio.run(main())

How often has this issue occurred?:

  • Always (100%)

Metadata

Metadata

Assignees

Labels

models[Component] This issue is related to model support

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions