Loading workspace insights... Statistics interval
7 days30 daysLatest CI Pipeline Executions
0f0074a7 feat(ai): emit full usage on otel spans (cost, totals, cache/reasoning details)
otelMiddleware only emitted gen_ai.usage.input_tokens/output_tokens even
though TokenUsage already carries provider-reported cost, total tokens,
cache/reasoning breakdowns, and duration-based billing. Backends like
PostHog had to re-derive cost from their own price tables, losing cache
discounts and gateway markup (OpenRouter), and duration-billed activities
had no cost signal at all.
A shared usageAttributes() helper now builds the full guarded attribute
set at all three emission sites (RUN_FINISHED chunk, onUsage, onFinish
rollup):
- gen_ai.usage.total_tokens / gen_ai.usage.cost (de-facto extensions
consumed directly by PostHog and LiteLLM-style backends)
- gen_ai.usage.cache_read.input_tokens, cache_creation.input_tokens,
reasoning.output_tokens (official GenAI semconv names)
- tanstack.ai.usage.duration_seconds and the upstream cost split
(no semconv equivalent exists)
E2E: new /api/otel-usage route drives the existing openai-usage-details
and openrouter-cost aimock mounts through otelMiddleware with a local
capture tracer; middleware.spec.ts asserts the attributes land on
iteration and root spans.
Fixes #721