fix(semconv): dual-emit OTel GenAI input/output token usage names - #1453
ZxlDragonDoctor wants to merge 3 commits into
Conversation
roy-tong
left a comment
There was a problem hiding this comment.
Reviewed — this implements the plan from #1447 cleanly, and the test asserting both families (and the negative case) is exactly right. Approving with three non-blocking notes:
-
Double-count hazard should be stated where a future reader will meet it. The code comment says collectors "can sum either family" — the sharper warning is that a consumer summing both families counts twice. One sentence in the
span_attributes.pycomment block ("consumers must prefer the new names; legacy names are compatibility aliases — summing both double-counts") would prevent the predictable dashboard bug. -
Internal consumers worth a quick check. If anything on the ingestion side iterates
gen_ai.usage.*attributes and sums matches, dual-emit doubles it internally the same way. The unit tests coverto_attributes()'s output shape, which suggests no such iteration exists — just worth confirming once. -
Removal horizon, even soft. "During the transition (#1447)" is good; if there's a version where the legacy names are planned to drop, naming it here (or in a tracking issue linked from this comment) gives dashboard owners a date to migrate by. The failure mode is silent: dashboards keyed on
prompt_tokensbreak quietly the day the alias is removed.
One forward-looking note, not for this PR: cache_creation_input_tokens / cache_read_input_tokens above are the same generation of legacy names on the cache side — the current conventions nest them (gen_ai.usage.cache_read.input_tokens etc.), so the same dual-emit pattern will be needed there eventually, and it's the exact alias-drift shape that bit other SDKs' extractors (langfuse#17117 for one). The regression shape that guards the whole family: same logical call, two attribute names, one expected total.
Problem
AgentOps still emits the older GenAI semconv usage names (
gen_ai.usage.prompt_tokens/completion_tokens) only. Current OTel GenAI conventions usegen_ai.usage.input_tokens/output_tokens. In a collector that mixes AgentOps with OpenLLMetry / pydantic-ai spans,sum(input_tokens)misses AgentOps andsum(prompt_tokens)misses the others.Fixes the transition gap described in #1447. Addresses the migration question: this PR dual-emits both families instead of a breaking rename, so existing AgentOps dashboards keep working while generic OTel consumers can sum the current names.
Solution
SpanAttributes.LLM_USAGE_INPUT_TOKENS/LLM_USAGE_OUTPUT_TOKENS.TokenUsage.to_attributes(), emit the current OTel names alongside the legacy names when prompt/completion counts are present.No call-site rewrite in this PR — every existing setter of the legacy constants benefits automatically.
Testing
TokenUsage.to_attributes()dual-emit logic:token_usage_dual_emit: 3 passedtests/unit/instrumentation/common/test_token_counting.py:{}prompt_tokensandinput_tokenscompletion_tokensandoutput_tokensAgent dimension
evaluation
Core value
Let mixed-SDK OTel collectors sum AgentOps token usage with the current GenAI attribute names without breaking existing prompt/completion consumers.
Fixes #1447