Uh oh!
There was an error while loading. Please reload this page.
feat(ai): emit cost + full usage on otel spans - #747
Conversation
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough
ChangesOpenTelemetry full usage span attributes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…g 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. FixesTanStack#721
10d8c42 to
c7df2a3CompareView your CI Pipeline Execution ↗ for commit 8ea04a2
☁️ Nx Cloud last updated this comment at |
@tanstack/ai@tanstack/ai-angular@tanstack/ai-anthropic@tanstack/ai-client@tanstack/ai-code-mode@tanstack/ai-code-mode-skills@tanstack/ai-devtools-core@tanstack/ai-elevenlabs@tanstack/ai-event-client@tanstack/ai-fal@tanstack/ai-gemini@tanstack/ai-grok@tanstack/ai-groq@tanstack/ai-isolate-cloudflare@tanstack/ai-isolate-node@tanstack/ai-isolate-quickjs@tanstack/ai-mcp@tanstack/ai-ollama@tanstack/ai-openai@tanstack/ai-openrouter@tanstack/ai-preact@tanstack/ai-react@tanstack/ai-react-ui@tanstack/ai-solid@tanstack/ai-solid-ui@tanstack/ai-svelte@tanstack/ai-utils@tanstack/ai-vue@tanstack/ai-vue-ui@tanstack/openai-base@tanstack/preact-ai-devtools@tanstack/react-ai-devtools@tanstack/solid-ai-devtoolscommit: |
Uh oh!
There was an error while loading. Please reload this page.
Resolves overlap from the now-merged TanStack#747 (kept the branch's extraction of usageAttributes/firstNumber/errorMessage into shared modules) and TanStack#763's middleware capability primitives (kept both the observability and capability exports in the package barrel). routeTree.gen.ts unioned; will be regenerated.
Summary
Fixes#721.
otelMiddlewareonly emittedgen_ai.usage.input_tokens/gen_ai.usage.output_tokens, even thoughTokenUsagealready carries provider-reported cost, total tokens, cache/reasoning breakdowns, and duration-based billing (the cost fields landed in #654). Backends like PostHog had to re-derive cost fromtokens × their own price table, losing cache discounts and gateway markup (OpenRouter), and duration-billed activities had no cost signal at all.What changed
A shared
usageAttributes()helper now builds the full attribute set at all three emission sites (RUN_FINISHEDchunk,onUsage,onFinishrollup). Every field is guarded, so spans are unchanged when a provider doesn't report it:gen_ai.usage.total_tokenstotalTokensgen_ai.usage.costcostgen_ai.usage.cache_read.input_tokenspromptTokensDetails.cachedTokensgen_ai.usage.cache_creation.input_tokenspromptTokensDetails.cacheWriteTokensgen_ai.usage.reasoning.output_tokenscompletionTokensDetails.reasoningTokenstanstack.ai.usage.duration_secondsdurationSecondstanstack.ai.usage.upstream_cost/_input_cost/_output_costcostDetailsDeliberately out of scope:
unitsBilledand per-modality token breakdowns (media activities don't flow through chat middleware — that's #720), andproviderUsageDetails(provider-shaped bag, unsafe to spread onto spans).Tests
packages/ai/tests/middlewares/otel.test.tscovering all three emission sites, absent-field omission, and empty detail objects./api/otel-usageroute drives the existingopenai-usage-details(cache/reasoning/totals) andopenrouter-cost(cost/cost split) aimock mounts throughotelMiddlewarewith an in-memory capture tracer;middleware.spec.tsasserts the attributes land on both iteration and root spans.docs/advanced/otel.md.pnpm test:prand the full middleware E2E suite pass locally.Summary by CodeRabbit
New Features
Documentation
Tests / E2E