From 7e6b3fa2cf77f0f748cf194bf05fa0ab73c3d672 Mon Sep 17 00:00:00 2001 From: Guillaume Fradet Date: Fri, 26 Sep 2025 10:35:16 +0200 Subject: [PATCH 1/2] Count read/write cache tokens with anthropic streaming --- .../instrumentation/anthropic/_stream.py | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/python/instrumentation/openinference-instrumentation-anthropic/src/openinference/instrumentation/anthropic/_stream.py b/python/instrumentation/openinference-instrumentation-anthropic/src/openinference/instrumentation/anthropic/_stream.py index 2e450d04..b5e19476 100644 --- a/python/instrumentation/openinference-instrumentation-anthropic/src/openinference/instrumentation/anthropic/_stream.py +++ b/python/instrumentation/openinference-instrumentation-anthropic/src/openinference/instrumentation/anthropic/_stream.py @@ -249,6 +249,8 @@ def __init__(self) -> None: ), stop_reason=_SimpleStringReplace(), input_tokens=_SimpleStringReplace(), + cache_creation_input_tokens=_SimpleStringReplace(), + cache_read_input_tokens=_SimpleStringReplace(), output_tokens=_SimpleStringReplace(), ), ), @@ -272,6 +274,13 @@ def process_chunk(self, chunk: "RawContentBlockDeltaEvent") -> None: "input_tokens": str(chunk.message.usage.input_tokens), } } + # Capture Anthropic prompt cache usage details when present + cache_write = getattr(chunk.message.usage, "cache_creation_input_tokens", None) + cache_read = getattr(chunk.message.usage, "cache_read_input_tokens", None) + if cache_write is not None: + value["messages"]["cache_creation_input_tokens"] = str(cache_write) + if cache_read is not None: + value["messages"]["cache_read_input_tokens"] = str(cache_read) self._values += value elif isinstance(chunk, RawContentBlockStartEvent): self._current_content_block_type = chunk.content_block @@ -341,6 +350,8 @@ def get_extra_attributes(self) -> Iterator[Tuple[str, AttributeValue]]: idx = 0 total_completion_token_count = 0 total_prompt_token_count = 0 + total_cache_read_token_count = 0 + total_cache_write_token_count = 0 # TODO(harrison): figure out if we should always assume messages is 1. # The current non streaming implementation assumes the same for message in messages: @@ -353,6 +364,10 @@ def get_extra_attributes(self) -> Iterator[Tuple[str, AttributeValue]]: total_completion_token_count += int(output_tokens) if input_tokens := message.get("input_tokens"): total_prompt_token_count += int(input_tokens) + if cache_read_tokens := message.get("cache_read_input_tokens"): + total_cache_read_token_count += int(cache_read_tokens) + if cache_write_tokens := message.get("cache_creation_input_tokens"): + total_cache_write_token_count += int(cache_write_tokens) # TODO(harrison): figure out if we should always assume the first message # will always be a message output generally this block feels really @@ -377,10 +392,23 @@ def get_extra_attributes(self) -> Iterator[Tuple[str, AttributeValue]]: tool_idx += 1 idx += 1 yield SpanAttributes.LLM_TOKEN_COUNT_COMPLETION, total_completion_token_count - yield SpanAttributes.LLM_TOKEN_COUNT_PROMPT, total_prompt_token_count + total_prompt_with_cache = ( + total_prompt_token_count + total_cache_read_token_count + total_cache_write_token_count + ) + yield SpanAttributes.LLM_TOKEN_COUNT_PROMPT, total_prompt_with_cache + if total_cache_read_token_count: + yield ( + SpanAttributes.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ, + total_cache_read_token_count, + ) + if total_cache_write_token_count: + yield ( + SpanAttributes.LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE, + total_cache_write_token_count, + ) yield ( SpanAttributes.LLM_TOKEN_COUNT_TOTAL, - total_completion_token_count + total_prompt_token_count, + total_completion_token_count + total_prompt_with_cache, ) From ce3f1bbc8cdf0c12f633edcb3c5fd723de3c34ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 02:31:07 +0000 Subject: [PATCH 2/2] chore(deps): update anthropic requirement Updates the requirements on [anthropic](https://github.com/anthropics/anthropic-sdk-python) to permit the latest version. - [Release notes](https://github.com/anthropics/anthropic-sdk-python/releases) - [Changelog](https://github.com/anthropics/anthropic-sdk-python/blob/main/CHANGELOG.md) - [Commits](https://github.com/anthropics/anthropic-sdk-python/compare/v0.2.1...v0.79.0) --- updated-dependencies: - dependency-name: anthropic dependency-version: 0.79.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .../test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/instrumentation/openinference-instrumentation-llama-index/test-requirements.txt b/python/instrumentation/openinference-instrumentation-llama-index/test-requirements.txt index 50e45c43..6ca7287b 100644 --- a/python/instrumentation/openinference-instrumentation-llama-index/test-requirements.txt +++ b/python/instrumentation/openinference-instrumentation-llama-index/test-requirements.txt @@ -1,4 +1,4 @@ -anthropic<0.67 +anthropic<0.80 llama-index-core==0.12.51 llama-index-llms-groq llama-index-llms-openai