Skip to content

fix(translator): merge input/cache token usage reported on Anthropic message_delta - #2292

Merged
nacx merged 3 commits into
envoyproxy:mainfrom
pjdurden:fix/2290-anthropic-message-delta-usage
Jul 13, 2026
Merged

fix(translator): merge input/cache token usage reported on Anthropic message_delta#2292
nacx merged 3 commits into
envoyproxy:mainfrom
pjdurden:fix/2290-anthropic-message-delta-usage

Conversation

@pjdurden

Copy link
Copy Markdown
Contributor

Description

Anthropic Messages streaming usage extraction reads input and cache token counts from message_start, and on message_delta it only updates output_tokens. Some Anthropic-compatable backends report the final input_tokens, cache_creation_input_tokens and cache_read_input_tokens on the message_delta event instead. In that case the input and cache counts get dropped and recorded as zero, which corrupts usage metrics and any downstream cost or quota accounting that depends on them.

This runs the message_delta usage through the same ExtractTokenUsageFromExplicitCaching path already used for message_start and merges it with Override. The fields are only merged when non-zero, so a standard delta that carries output_tokens alone doesnt overwrite the message_start baseline with zeros.

Related Issues/PRs (if applicable)

Fixes #2290

Special notes for reviewers (if applicable)

Added two streaming subtests (cache-creation and cache-read variants) using the values from the issue: input_tokens 4522 + cache 4511 gives 9033 input total and 9038 total. They fail on main and pass with this change. The existing streaming and non-streaming usage tests are untouched.

@pjdurden
pjdurden requested a review from a team as a code owner June 25, 2026 03:07
Copilot AI review requested due to automatic review settings June 25, 2026 03:07
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jun 25, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@CodePrometheus

Copy link
Copy Markdown
Contributor

@pjdurden Hi, thanks for working on this, I originally planned to open a PR for this after filing the issue, but you got to it first. This addresses the main case. One thing I think we should be careful about is merging usage at field level rather than replacing the whole usage snapshot. In real environments, message_delta.usage may only include the input/cache fields that apply, so field presence matters when merging usage across events.

@pjdurden

Copy link
Copy Markdown
Contributor Author

Thanks, that makes sense and you are right that field presence matters here.

Pushed an update that merges per field instead of replacing the whole usage snapshot. It now keeps the latest known value for raw input, cache_read and cache_creation, and only overwrites the ones the message_delta actualy reports (non-zero), then recomputes the unified input_tokens sum. So a delta that carries only cache_read no longer zeros out the input_tokens already set on message_start. output_tokens is taken unconditionally from message_delta since that is always the final value there.

I cant distinguish an omitted field from an explicit zero, since the upstream usage fields are plain ints rather than pointers, so non-zero is the best proxy for presence. If you would rather gate this behind a backend flag instead of always-on merging, happy to switch to that.

Added a regression test where message_start sets input=1000 and the delta reports only cache_read=500, asserting the merged total stays 1500 instead of collapsing to 500.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Jun 25, 2026
@nacx

nacx commented Jul 12, 2026

Copy link
Copy Markdown
Member

Thanks! @pjdurden can you fix the DCO?

@nacx
nacx enabled auto-merge (squash) July 12, 2026 07:35
@codecov-commenter

codecov-commenter commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.87%. Comparing base (6749acb) to head (4f57ce6).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2292      +/-   ##
==========================================
+ Coverage   84.86%   84.87%   +0.01%     
==========================================
  Files         145      145              
  Lines       21518    21534      +16     
==========================================
+ Hits        18261    18277      +16     
  Misses       2161     2161              
  Partials     1096     1096              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nacx
nacx disabled auto-merge July 12, 2026 07:35
pjdurden added 2 commits July 13, 2026 00:23
…message_delta

Anthropic Messages streaming usage extraction read input and cache token
counts only from message_start, and on message_delta it updated output
tokens alone. Some Anthropic-compatible backends instead report the final
input_tokens / cache_creation_input_tokens / cache_read_input_tokens on the
message_delta event, so those counts were dropped and recorded as zero,
corrupting usage metrics and any downstream cost/quota accounting.

Merge the input and cache fields when message_delta carries them, reusing
the same ExtractTokenUsageFromExplicitCaching path as message_start. The
fields are only merged when non-zero so a standard delta carrying output
tokens only does not clobber the message_start baseline.

Fixes envoyproxy#2290

Signed-off-by: pjdurden <prajjwalchittori1@gmail.com>
Signed-off-by: pjdurden <prajjwalchittori1@gmail.com>
@pjdurden
pjdurden force-pushed the fix/2290-anthropic-message-delta-usage branch from ee0ef8d to 527375a Compare July 13, 2026 05:27
@pjdurden

Copy link
Copy Markdown
Contributor Author

Thanks! @pjdurden can you fix the DCO?

DCO fixed now. Thanks for looking into this - @nacx

@nacx
nacx merged commit 21ae837 into envoyproxy:main Jul 13, 2026
34 checks passed
DanielChrn pushed a commit to DanielChrn/ai-gateway that referenced this pull request Jul 16, 2026
…message_delta (envoyproxy#2292)

**Description**

Anthropic Messages streaming usage extraction reads input and cache
token counts from `message_start`, and on `message_delta` it only
updates `output_tokens`. Some Anthropic-compatable backends report the
final `input_tokens`, `cache_creation_input_tokens` and
`cache_read_input_tokens` on the `message_delta` event instead. In that
case the input and cache counts get dropped and recorded as zero, which
corrupts usage metrics and any downstream cost or quota accounting that
depends on them.

This runs the `message_delta` usage through the same
`ExtractTokenUsageFromExplicitCaching` path already used for
`message_start` and merges it with `Override`. The fields are only
merged when non-zero, so a standard delta that carries `output_tokens`
alone doesnt overwrite the `message_start` baseline with zeros.

**Related Issues/PRs (if applicable)**

Fixes envoyproxy#2290

**Special notes for reviewers (if applicable)**

Added two streaming subtests (cache-creation and cache-read variants)
using the values from the issue: `input_tokens` 4522 + cache 4511 gives
9033 input total and 9038 total. They fail on main and pass with this
change. The existing streaming and non-streaming usage tests are
untouched.

---------

Signed-off-by: pjdurden <prajjwalchittori1@gmail.com>
Co-authored-by: Ignasi Barrera <ignasi@tetrate.io>
Signed-off-by: Daniel Chernovsky <daniel.chernovsky@doubleverify.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Anthropic streaming usage drops input/cache tokens from message_delta

5 participants