Skip to content

Claude (Anthropic) model wrapper drops token usage — Event.usageMetadata() is always empty #1259

@rogerzxu

Description

@rogerzxu

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:

  1. Create an LlmAgent backed by new Claude(modelName, anthropicClient) (Anthropic API or Vertex backend).
  2. Run it via Runner.runAsync(...) (default RunConfig, streaming mode NONE).
  3. In an afterModelCallback (or by inspecting the streamed Events), read llmResponse.usageMetadata() / event.usageMetadata().
  4. 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());
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions