Skip to content

feat(otel): emit cost + full usage on spans (gen_ai.usage.cost, total_tokens, cache/reasoning details, duration) #721

Description

@tombeckenham

Summary

otelMiddleware emits only two usage attributes — gen_ai.usage.input_tokens and gen_ai.usage.output_tokens (from usage.promptTokens / usage.completionTokens). The TokenUsage it receives already carries more that never reaches the span.

Available but not emitted

From TokenUsage (@tanstack/ai-event-client):

  • cost — provider-reported cost for the request
  • costDetails — upstream input/output cost split
  • totalTokens
  • promptTokensDetails — e.g. cached tokens
  • completionTokensDetails — e.g. reasoning tokens
  • durationSeconds — duration-based billing (e.g. Whisper transcription)

Impact

Because cost is absent from the span, backends (e.g. PostHog $ai_generation) must re-derive cost from tokens × their own price table. Provider-reported actual cost — cache discounts, gateway markup (OpenRouter), etc. — is therefore lost, and duration-billed activities have no cost signal at all.

Proposal

When present on usage, emit (all optional/guarded so spans stay clean when a provider doesn't report them):

  • gen_ai.usage.costusage.cost
  • gen_ai.usage.total_tokensusage.totalTokens
  • cached/reasoning attributes ← promptTokensDetails / completionTokensDetails
  • duration ← usage.durationSeconds
  • optionally costDetails (upstream input/output split)

This is independently useful for chat today. It also matters most for the cross-activity case in #720: media activities have no tokens to derive cost from, so without an explicit gen_ai.usage.cost a backend gets nothing.

Observed on @tanstack/ai@0.26.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions