Skip to content

old semconv attribute names (prompt_tokens vs input_tokens) — cross-SDK aggregation gap #1447

Open

Description

@roy-tong

A follow-up to #1445 (Anthropic cache tokens), from the same 30-project conformance audit.

The observation

AgentOps uses the older GenAI semconv attribute names in agentops/semconv/span_attributes.py (~L61-63):

LLM_USAGE_PROMPT_TOKENS = "gen_ai.usage.prompt_tokens"
LLM_USAGE_COMPLETION_TOKENS = "gen_ai.usage.completion_tokens"

The current OTel GenAI semantic conventions use gen_ai.usage.input_tokens / gen_ai.usage.output_tokens. OpenLLMetry, pydantic-ai, and other instrumentation we audited have already migrated to the new names.

The file header (~L8-24) contains TODO comments acknowledging mapping issues.

The consequence

In an OTLP collector receiving spans from multiple SDKs:

  • sum(gen_ai.usage.input_tokens) misses all AgentOps spans (they emit prompt_tokens)
  • sum(gen_ai.usage.prompt_tokens) misses all OpenLLMetry spans (they emit input_tokens)
  • Cross-SDK token aggregation requires knowing which attribute name each SDK uses — per-SDK knowledge that generic consumers shouldn't need

The question

Is there a plan to migrate to the current gen_ai.usage.input_tokens / output_tokens names, or to emit both during a transition? The old names still work within the AgentOps ecosystem, but any downstream tool consuming generic OTel GenAI attributes will silently undercount when AgentOps spans are mixed in.

Happy to send a two-SDK repro (one AgentOps span + one OpenLLMetry span in the same collector, asserting the aggregation gap).

Context: AgentMeasure conformance check; this is the AgentOps follow-up after the Anthropic cache-token finding in #1445.

Activity

  1. ZxlDragonDoctor commented on Sep 12, 2026

    @ZxlDragonDoctor

    I'd like to take this. Plan: dual-emit current OTel GenAI usage names (gen_ai.usage.input_tokens / output_tokens) alongside the existing prompt_tokens / completion_tokens in TokenUsage.to_attributes(), with regression coverage, so cross-SDK collectors can sum either family during the transition. No call-site rewrite in this PR.

  2. roy-tong commented on Sep 14, 2026

    @roy-tong
    Author

    Thanks for picking this up — dual-emit is the right transitional shape, and putting the regression tests around TokenUsage.to_attributes() is exactly where this class of drift belongs.

    Two things worth pinning down while the implementation settles:

    1. Canonical name + removal horizon. If gen_ai.usage.input_tokens/output_tokens become primary, stating in the code/docs that prompt_tokens/completion_tokens are compatibility aliases (even with a soft removal horizon) helps consumers keying dashboards on the old names — they break silently, not loudly, the day the aliases drop.

    2. Double-count risk while both are live. Any consumer that sums every gen_ai.usage.* token attribute will count both copies. Either document "prefer the new names, old ones are fallback", or gate the legacy names behind a flag — one sentence in the docs will save someone a confusing afternoon.

    The regression shape that bit every SDK in our audit: same logical call, two attribute names, one expected total. Happy to review the PR when it's up.

    Repro fixtures: AgentMeasure conformance pack — plain JSONL + expected totals, no runtime install. Found during an open ecosystem audit of usage-accounting tools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions