Describe the Bug:
com.google.adk.models.Claude does not map Anthropic token usage into the LlmResponse. Its convertAnthropicResponseToLlmResponse(Message) reads only message.content() and never calls responseBuilder.usageMetadata(...), so for any Claude-backed agent LlmResponse.usageMetadata() — and therefore Event.usageMetadata() — is always Optional.empty(). This makes token accounting / cost tracking impossible for Claude through ADK (afterModelCallback, plugins, and per-session token counting all see empty usage).
The Anthropic SDK response already carries the data: Message.usage() exposes inputTokens() / outputTokens() (plus cache token fields). It's simply not copied across in the conversion.
This is the same class of bug as #1159 (spring-ai MessageConverter.toLlmResponse dropping ChatResponse usage), which was fixed — the native Claude wrapper has the identical gap. Related: #622, #777.
Steps to Reproduce:
- Create an
LlmAgent backed by new Claude(modelName, anthropicClient) (Anthropic API or Vertex backend).
- Run it via
Runner.runAsync(...) (default RunConfig, streaming mode NONE).
- In an
afterModelCallback (or by inspecting the streamed Events), read llmResponse.usageMetadata() / event.usageMetadata().
- It is
Optional.empty() on every event, despite the Anthropic API returning usage.
Expected Behavior:
LlmResponse.usageMetadata() should be populated from Message.usage() (prompt/candidates/total token counts), consistent with the Gemini path and the spring-ai fix in #1159.
Observed Behavior:
usageMetadata is empty for all Claude responses; token counts are unavailable.
Environment Details:
- ADK Library Version: 1.4.0 (confirmed still present on
main)
- File:
core/src/main/java/com/google/adk/models/Claude.java, method convertAnthropicResponseToLlmResponse(Message)
Model Information:
- Claude (Anthropic) via both the direct Anthropic API and the Vertex AI backend.
Suggested fix:
Map usage in convertAnthropicResponseToLlmResponse, e.g.:
if (message.usage() != null) {
responseBuilder.usageMetadata(
GenerateContentResponseUsageMetadata.builder()
.promptTokenCount((int) message.usage().inputTokens())
.candidatesTokenCount((int) message.usage().outputTokens())
.totalTokenCount(
(int) (message.usage().inputTokens() + message.usage().outputTokens()))
.build());
}
Describe the Bug:
com.google.adk.models.Claudedoes not map Anthropic token usage into theLlmResponse. ItsconvertAnthropicResponseToLlmResponse(Message)reads onlymessage.content()and never callsresponseBuilder.usageMetadata(...), so for any Claude-backed agentLlmResponse.usageMetadata()— and thereforeEvent.usageMetadata()— is alwaysOptional.empty(). This makes token accounting / cost tracking impossible for Claude through ADK (afterModelCallback, plugins, and per-session token counting all see empty usage).The Anthropic SDK response already carries the data:
Message.usage()exposesinputTokens()/outputTokens()(plus cache token fields). It's simply not copied across in the conversion.This is the same class of bug as #1159 (spring-ai
MessageConverter.toLlmResponsedroppingChatResponseusage), which was fixed — the native Claude wrapper has the identical gap. Related: #622, #777.Steps to Reproduce:
LlmAgentbacked bynew Claude(modelName, anthropicClient)(Anthropic API or Vertex backend).Runner.runAsync(...)(defaultRunConfig, streaming mode NONE).afterModelCallback(or by inspecting the streamedEvents), readllmResponse.usageMetadata()/event.usageMetadata().Optional.empty()on every event, despite the Anthropic API returning usage.Expected Behavior:
LlmResponse.usageMetadata()should be populated fromMessage.usage()(prompt/candidates/total token counts), consistent with the Gemini path and the spring-ai fix in #1159.Observed Behavior:
usageMetadatais empty for all Claude responses; token counts are unavailable.Environment Details:
main)core/src/main/java/com/google/adk/models/Claude.java, methodconvertAnthropicResponseToLlmResponse(Message)Model Information:
Suggested fix:
Map usage in
convertAnthropicResponseToLlmResponse, e.g.: