You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
record_token_usage in mellea/telemetry/backend_instrumentation.py extracts cache and reasoning token counts using get_value, which does not walk nested paths. As a result:
Anthropic: cache_read_input_tokens and cache_creation_input_tokens are top-level in the usage object — extracted correctly today.
OpenAI / LiteLLM: cache tokens live at prompt_tokens_details.cached_tokens and reasoning tokens at completion_tokens_details.reasoning_tokens — silently absent from spans because get_value cannot traverse the nesting.
This means gen_ai.usage.cache_read.input_tokens and gen_ai.usage.reasoning.output_tokens are never emitted for OpenAI or LiteLLM backends, with no warning to the user.
A secondary concern: the attribute names themselves (gen_ai.usage.cache_read.input_tokens etc.) are Anthropic-specific and not yet defined in the upstream OTel GenAI semconv — see #1054 for the naming audit.
Why not fix in the current extraction layer
Patching get_value for dotted-path walking or adding provider-specific branches in record_token_usage is short-lived work: after #1045, record_token_usage will read from mot.generation (GenerationMetadata) rather than raw provider response objects, making any provider-specific path logic obsolete.
Approach
During or after #1045, ensure GenerationMetadata (or the per-backend post-processing that populates it) normalises cache and reasoning token fields to a flat, provider-agnostic shape. record_token_usage then reads the normalised struct without any provider-specific path walking.
Problem
record_token_usageinmellea/telemetry/backend_instrumentation.pyextracts cache and reasoning token counts usingget_value, which does not walk nested paths. As a result:cache_read_input_tokensandcache_creation_input_tokensare top-level in the usage object — extracted correctly today.prompt_tokens_details.cached_tokensand reasoning tokens atcompletion_tokens_details.reasoning_tokens— silently absent from spans becauseget_valuecannot traverse the nesting.This means
gen_ai.usage.cache_read.input_tokensandgen_ai.usage.reasoning.output_tokensare never emitted for OpenAI or LiteLLM backends, with no warning to the user.A secondary concern: the attribute names themselves (
gen_ai.usage.cache_read.input_tokensetc.) are Anthropic-specific and not yet defined in the upstream OTel GenAI semconv — see #1054 for the naming audit.Why not fix in the current extraction layer
Patching
get_valuefor dotted-path walking or adding provider-specific branches inrecord_token_usageis short-lived work: after #1045,record_token_usagewill read frommot.generation(GenerationMetadata) rather than raw provider response objects, making any provider-specific path logic obsolete.Approach
During or after #1045, ensure
GenerationMetadata(or the per-backend post-processing that populates it) normalises cache and reasoning token fields to a flat, provider-agnostic shape.record_token_usagethen reads the normalised struct without any provider-specific path walking.Specifically:
post_processing(): flattenprompt_tokens_details.cached_tokens→mot.usage["cache_read_input_tokens"]post_processing(): flattencompletion_tokens_details.reasoning_tokens→mot.usage["reasoning_tokens"]Prerequisites
GenerationMetadatanormalisation is Phase 1 workRelated