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

Labels

waiting on reporterWaiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions