From a5e18c45854fb35ed94663da6b4caef4db6911cd Mon Sep 17 00:00:00 2001 From: Pablo Pardo Garcia Date: Mon, 7 Sep 2026 18:22:20 +0200 Subject: [PATCH] fix: emit gen_ai.usage.cache_write.input_tokens per the semconv rename The GenAI conventions renamed cache_creation to cache_write (open-telemetry/semantic-conventions-genai#440) before the cache fields first shipped here; the old name is in no released version, so the parameter and attribute rename land together with no alias. --- src/rius/generation.py | 13 +++++++------ src/rius/semconv.py | 4 +++- tests/test_generation.py | 10 +++++----- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/rius/generation.py b/src/rius/generation.py index be1bdc6..7e9d250 100644 --- a/src/rius/generation.py +++ b/src/rius/generation.py @@ -27,8 +27,8 @@ GEN_AI_REQUEST_PREFIX, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_RESPONSE_MODEL, - GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, + GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, TRACER_NAME, @@ -158,7 +158,7 @@ def set_usage( input_tokens: int | None = None, output_tokens: int | None = None, cache_read_input_tokens: int | None = None, - cache_creation_input_tokens: int | None = None, + cache_write_input_tokens: int | None = None, ) -> None: """Record token usage (``gen_ai.usage.*`` attributes). @@ -179,9 +179,10 @@ def set_usage( cache_read_input_tokens: Input tokens served from a provider-managed prompt cache (``gen_ai.usage.cache_read.input_tokens``). - cache_creation_input_tokens: Input tokens written to a + cache_write_input_tokens: Input tokens written to a provider-managed prompt cache - (``gen_ai.usage.cache_creation.input_tokens``). + (``gen_ai.usage.cache_write.input_tokens``, called + "cache creation" by Anthropic). """ if input_tokens is not None: self._span.set_attribute(GEN_AI_USAGE_INPUT_TOKENS, input_tokens) @@ -189,9 +190,9 @@ def set_usage( self._span.set_attribute(GEN_AI_USAGE_OUTPUT_TOKENS, output_tokens) if cache_read_input_tokens is not None: self._span.set_attribute(GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, cache_read_input_tokens) - if cache_creation_input_tokens is not None: + if cache_write_input_tokens is not None: self._span.set_attribute( - GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, cache_creation_input_tokens + GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS, cache_write_input_tokens ) def record_first_token(self) -> None: diff --git a/src/rius/semconv.py b/src/rius/semconv.py index bf75374..f4235b4 100644 --- a/src/rius/semconv.py +++ b/src/rius/semconv.py @@ -47,7 +47,9 @@ GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens" GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens" GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens" -GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS = "gen_ai.usage.cache_creation.input_tokens" +# semconv-genai renamed cache_creation -> cache_write (PR #440) before our +# cache fields first shipped; SDKs 0.12.x emitted the pre-rename name. +GEN_AI_USAGE_CACHE_WRITE_INPUT_TOKENS = "gen_ai.usage.cache_write.input_tokens" GEN_AI_INPUT_MESSAGES = "gen_ai.input.messages" GEN_AI_OUTPUT_MESSAGES = "gen_ai.output.messages" GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons" diff --git a/tests/test_generation.py b/tests/test_generation.py index 8ed0dab..ca3abf8 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -58,11 +58,11 @@ def test_cm_usage_cache_tokens(exported_spans: InMemorySpanExporter) -> None: input_tokens=10, output_tokens=202, cache_read_input_tokens=11579, - cache_creation_input_tokens=12694, + cache_write_input_tokens=12694, ) attrs = exported_spans.get_finished_spans()[0].attributes assert attrs["gen_ai.usage.cache_read.input_tokens"] == 11579 - assert attrs["gen_ai.usage.cache_creation.input_tokens"] == 12694 + assert attrs["gen_ai.usage.cache_write.input_tokens"] == 12694 def test_cm_usage_cache_tokens_omitted_are_absent(exported_spans: InMemorySpanExporter) -> None: @@ -70,15 +70,15 @@ def test_cm_usage_cache_tokens_omitted_are_absent(exported_spans: InMemorySpanEx gen.set_usage(input_tokens=10, output_tokens=5) attrs = exported_spans.get_finished_spans()[0].attributes assert "gen_ai.usage.cache_read.input_tokens" not in attrs - assert "gen_ai.usage.cache_creation.input_tokens" not in attrs + assert "gen_ai.usage.cache_write.input_tokens" not in attrs def test_cm_usage_cache_tokens_zero_recorded(exported_spans: InMemorySpanExporter) -> None: with start_as_current_generation("chat") as gen: - gen.set_usage(cache_read_input_tokens=0, cache_creation_input_tokens=0) + gen.set_usage(cache_read_input_tokens=0, cache_write_input_tokens=0) attrs = exported_spans.get_finished_spans()[0].attributes assert attrs["gen_ai.usage.cache_read.input_tokens"] == 0 - assert attrs["gen_ai.usage.cache_creation.input_tokens"] == 0 + assert attrs["gen_ai.usage.cache_write.input_tokens"] == 0 def test_cm_finish_reasons_list(exported_spans: InMemorySpanExporter) -> None: