ITADN

[bug] Anthropic instrumentor: unguarded usage.input_tokens can blank out an entire streaming LLM span

#3499Openmikeldking 创建于 12 天前
buglanguage: pythoninstrumentation: anthropic
M
mikeldkingcommented
### Summary `_get_token_counts` (recently extracted into `_utils.py` and now shared by the streaming and non-streaming paths) sums `usage.input_tokens` without the `or 0` guard applied to every other field: ```python prompt_tokens = ( usage.input_tokens # <- no `or 0` + (usage.cache_creation_input_tokens or 0) + (usage.cache_read_input_tokens or 0) ) ``` If `usage.input_tokens` is `None`, `None + 0` raises `TypeError`. ### Why it matters more now Both paths call the same helper, but they wrap it differently: - **Non-streaming** — `_wrappers._get_llm_token_counts` is decorated with `@_stop_on_exception`, so the error is swallowed and the span quietly loses its token counts. - **Streaming** — `_MessageExtractor.get_attributes()` has no such guard. The exception escapes into `_utils._finish_tracing`, which catches it and sets `attributes = None`. The exported LLM span then ends with **no** input messages, output messages, model name or token counts — an empty LLM span. So one nullable field on a partial/delta-shaped `Usage` degrades a whole streaming span, not just its token attributes. ### Suggested fix Guard `input_tokens` like the rest (`(usage.input_tokens or 0)`), and consider giving the streaming extractor the same `@_stop_on_exception`-style containment so a token-count failure can never blank out the rest of a span's attributes. ### Notes Pre-existing behavior — not introduced by #3490's fix, which only moved the code. Found during a code review of that change.
0 条评论