Skip to content

fix(langchain): record Anthropic cache-write tokens from TTL breakdown - #937

Open
zeningc wants to merge 4 commits into
open-telemetry:mainfrom
zeningc:fix/langchain-anthropic-cache-write-tokens
Open

zeningc wants to merge 4 commits into
open-telemetry:mainfrom
zeningc:fix/langchain-anthropic-cache-write-tokens

Conversation

@zeningc

@zeningc zeningc commented Oct 21, 2026 •

Copy link
Copy Markdown

Description

Anthropic reports usage.cache_creation broken down by cache TTL. When it does, langchain-anthropic sets input_token_details.cache_creation to 0 and moves the count into ephemeral_5m_input_tokens / ephemeral_1h_input_tokens, so gen_ai.usage.cache_write.input_tokens was never recorded for ChatAnthropic. This change sums those keys when neither cache_write nor cache_creation is set.

The existing test_chat_anthropic_claude_sonnet_cache_token_details cassette was recorded before Anthropic added the TTL breakdown, so it still passes on main.

Repro: with langchain-anthropic 1.7.6, _create_usage_metadata(Usage(input_tokens=13, output_tokens=8, cache_read_input_tokens=4, cache_creation_input_tokens=5, cache_creation={"ephemeral_5m_input_tokens": 5, "ephemeral_1h_input_tokens": 0})) gives {"cache_read": 4, "cache_creation": 0, "ephemeral_5m_input_tokens": 5, ...}. On main, extract_token_details returns only the cache-read count.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How has this been tested?

  • tox -e py312-test-instrumentation-genai-langchain-latest: 943 passed, 34 skipped, 2 xfailed
  • tox -e py310-test-instrumentation-genai-langchain-oldest: pass
  • tox -e precommit and tox -e typecheck: pass
  • The 5 new tests fail on main and pass with this change.

Checklist

See CONTRIBUTING.md
for the style guide, changelog guidance, and more.

  • Followed the style guidelines of this project
  • Changelog updated if the change requires an entry
  • Unit tests added

AI-assisted (Claude Code), disclosed with an Assisted-by: commit trailer. I have reviewed the change and can answer questions about it.

Copilot AI balanced review requested due to automatic review settings October 21, 2026 00:15
@zeningc
zeningc requested a review from a team as a code owner October 21, 2026 00:15
@linux-foundation-easycla

linux-foundation-easycla Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: zeningc / name: Zening Chen (8307c9d)

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.

🟢 Approval recommended

The narrow fallback preserves existing behavior and has focused regression coverage, with no blocking issues identified.

No open issues 🎉

What changed in this PR

Fixes LangChain instrumentation to record Anthropic cache-write tokens from per-TTL counts when aggregate counts are unavailable.

Changes:

  • Adds a TTL-count fallback while preserving existing aggregate-count precedence.
  • Adds regression coverage and a changelog entry.
File Description
instrumentation/​opentelemetry-instrumentation-genai-langchain/​tests/​test_callback_handler.py Tests TTL-based token extraction and emitted attributes.
instrumentation/​opentelemetry-instrumentation-genai-langchain/​src/​opentelemetry/​instrumentation/​genai/​langchain/​utils.py Sums TTL counts as a fallback.
instrumentation/​opentelemetry-instrumentation-genai-langchain/​.changelog/​PR_NUMBER.fixed Documents the fix.

Review effort: Balanced

@zeningc
zeningc force-pushed the fix/langchain-anthropic-cache-write-tokens branch from c32b18e to b011d4e Compare October 21, 2026 00:18
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Pull request dashboard status

Waiting on reviewers · refreshed 2026-10-21 07:29 UTC

Review the latest changes.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

@zeningc
zeningc force-pushed the fix/langchain-anthropic-cache-write-tokens branch from b011d4e to a238a35 Compare October 21, 2026 02:20
langchain-anthropic zeroes `cache_creation` in `input_token_details` when
Anthropic reports `usage.cache_creation` per TTL, moving the count into
`ephemeral_5m_input_tokens` / `ephemeral_1h_input_tokens`. Fall back to
summing those keys so `gen_ai.usage.cache_write.input_tokens` is recorded.

Assisted-by: Claude Code
@zeningc
zeningc force-pushed the fix/langchain-anthropic-cache-write-tokens branch from 0ab762b to 8307c9d Compare October 21, 2026 03:29
Comment on lines +2001 to +2019
def test_extract_token_details_anthropic_ttl_breakdown():
# Shape produced by langchain-anthropic when Anthropic reports
# ``usage.cache_creation`` per cache TTL.
usage = {
"input_tokens": 22,
"output_tokens": 8,
"total_tokens": 30,
"input_token_details": {
"cache_read": 4,
"cache_creation": 0,
"ephemeral_5m_input_tokens": 5,
"ephemeral_1h_input_tokens": 0,
},
}
details = extract_token_details(usage)
assert details == {
"cache_write_input_tokens": 5,
"cache_read_input_tokens": 4,
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we cover this through ChatAnthropic instead? The existing test_chat_anthropic_claude_sonnet_cache_token_details cassette predates the TTL breakdown. Re-recording it (or adding "cache_creation": {"ephemeral_5m_input_tokens": 5, "ephemeral_1h_input_tokens": 0} to its usage) makes that test fail on main and pass here, with the existing assertions. It also keeps passing on the oldest langchain-anthropic. Then the handler-level cases here wouldn't be needed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sure, I also recorded new cassette, thanks! The test now makes two calls (cache write, then read), since the old prompt was too short to trigger caching.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants