Skip to content

fix(ai): roll up OpenTelemetry usage across iterations - #928

Merged
AlemTuzlak merged 1 commit into
TanStack:mainfrom
dexhunter:fix/otel-root-usage-rollup-916
Aug 10, 2026
Merged

fix(ai): roll up OpenTelemetry usage across iterations#928
AlemTuzlak merged 1 commit into
TanStack:mainfrom
dexhunter:fix/otel-root-usage-rollup-916

Conversation

@dexhunter

@dexhunterdexhunter commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • roll up numeric OpenTelemetry usage attributes across chat iterations on the request root span
  • apply the accumulated usage once before the root onSpanEnd callback on finish, error, and abort paths
  • retain per-iteration spans and metrics, with compact unit coverage and a deterministic tool-loop E2E test

Provenance

Weco supplied the request-level accumulator direction. The strict evaluator rejected its initial field mapping, so the final implementation was rebuilt through the existing usageAttributes() contract and hardened across every terminal path. Public autoresearch trajectory

Validation

  • strict evaluator: root_usage_abs_error_tokens improved from 550 to 0; finish, error, and abort fixtures passed
  • pnpm --filter @tanstack/ai test:lib --run tests/middlewares/otel.test.ts (50 tests passed)
  • pnpm --filter @tanstack/ai test:lib (1,137 tests passed)
  • pnpm --filter @tanstack/ai test:types
  • pnpm --filter @tanstack/ai test:eslint (no errors; existing warnings only)
  • pnpm --filter @tanstack/ai-e2e test:types
  • focused tool-loop E2E passed; the constrained full E2E run passed 347 tests with one skipped, one retry-only flaky test, and one environment-only Miniflare EPIPE failure that reproduces on the unchanged base
  • CI=1 pnpm test:pr
  • Prettier and git diff --check

Conflict Review

PR #896 changes adjacent usage-attribute tests and E2E helpers but does not implement request-level root-span rollup. It may require a small test/helper rebase if merged first; it is not a behavioral duplicate of this fix.

Fixes#916

Summary by CodeRabbit

  • Bug Fixes

    • Corrected OpenTelemetry usage reporting for multi-step chat runs.
    • Root spans now include combined token usage, including runs ending with errors or cancellations.
    • Preserved accurate usage details for each individual iteration.
  • Tests

    • Added coverage for multi-iteration and tool-based chat scenarios.
    • Expanded validation across supported provider configurations.

@coderabbitai

coderabbitaiBot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@tombeckenham, you've reached your PR review limit, so we couldn't start this review.

Next review available in:52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ea51819e-2e83-4c15-96a2-f6a53f506aaf

📥 Commits

Reviewing files that changed from the base of the PR and between 888e8b7 and 9ea1a12.

📒 Files selected for processing (5)
  • .changeset/otel-root-usage-rollup.md
  • packages/ai/src/middlewares/otel.ts
  • packages/ai/tests/middlewares/otel.test.ts
  • testing/e2e/src/routes/api.otel-usage.ts
  • testing/e2e/tests/middleware.spec.ts
📝 Walkthrough

Walkthrough

The OpenTelemetry middleware now accumulates usage across chat iterations and applies it once to the root span on finish, error, or abort. Unit and E2E tests cover multi-iteration tool loops, per-iteration spans, metrics, and aggregated root attributes.

Changes

OpenTelemetry root usage rollup

Layer / File(s)Summary
Accumulate and apply root usage
packages/ai/src/middlewares/otel.ts, .changeset/otel-root-usage-rollup.md
Chat request state accumulates usage attributes and applies them once to the root span on finish, error, or abort, with finish usage as fallback.
Validate rollup and terminal paths
packages/ai/tests/middlewares/otel.test.ts
Tests cover mixed iteration usage, preserved iteration attributes, token metrics, and root usage captured during error and abort completion.
Exercise multi-iteration tool flow
testing/e2e/src/routes/api.otel-usage.ts, testing/e2e/tests/middleware.spec.ts
Adds a tool-loop route path and verifies two client iterations plus aggregated root token usage in the E2E span data.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant E2ETest
participant OtelUsageRoute
participant Chat
participant otelMiddleware
participant SpanExporter
E2ETest->>OtelUsageRoute: POST provider=tool-loop
OtelUsageRoute->>Chat: invoke chat with weatherTool
Chat->>otelMiddleware: emit usage for each iteration
otelMiddleware->>otelMiddleware: accumulate root usage
Chat->>otelMiddleware: finish after tool loop
otelMiddleware->>SpanExporter: export iteration spans and rolled-up root span
SpanExporter-->>E2ETest: return captured spans
Loading

Possibly related PRs

  • TanStack/ai#747: Both changes update OpenTelemetry usage attributes and tests in the middleware.

Suggested reviewers:AlemTuzlak, tombeckenham

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly states the main change: rolling up OpenTelemetry usage across iterations.
Description check✅ PassedThe description covers the change, validation, and linked issue, though it doesn't use the repo's exact template headings.
Linked Issues check✅ PassedThe middleware now accumulates usage across iterations and applies it on finish, error, and abort, matching #916.
Out of Scope Changes check✅ PassedAll changes are directly related to the OTEL usage roll-up fix and its supporting tests and changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dexhunter

Copy link
Copy Markdown
ContributorAuthor

This rolls up numeric OpenTelemetry usage attributes across chat iterations onto the request root span, applying the accumulated totals once before the root onSpanEnd callback on the finish, error, and abort paths, while keeping per-iteration spans and metrics unchanged. On the deterministic tool-loop fixture the root-span usage error goes from 550 tokens to 0, and the otel middleware suite (50 tests) plus a tool-loop E2E test cover all three terminal paths. All checks on the branch are green and there are no conflicts against main — it is 22 commits behind by now, so I'm happy to rebase it whenever that's useful. If it would make review easier I can also split the E2E test out or narrow the change to the finish path first.

@tombeckenham
tombeckenhamforce-pushed the fix/otel-root-usage-rollup-916 branch from 027dbf8 to 9ea1a12CompareAugust 10, 2026 09:13
@tombeckenham

Copy link
Copy Markdown
Contributor

Maintainer sweep: rebased onto main (force-with-lease). CI approval handled if it was waiting.

@nx-cloud

nx-cloudBot commented Aug 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 9ea1a12

CommandStatusDurationResult
nx run-many --targets=build --exclude=examples/...✅ Succeeded3sView ↗
nx affected --targets=test:sherif,test:knip,tes...✅ Succeeded8m 59sView ↗

☁️ Nx Cloud last updated this comment at 2026-08-10 09:29:04 UTC

@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@928

@tanstack/ai-acp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-acp@928

@tanstack/ai-angular

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-angular@928

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@928

@tanstack/ai-bedrock

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-bedrock@928

@tanstack/ai-byteplus

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-byteplus@928

@tanstack/ai-claude-code

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-claude-code@928

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@928

@tanstack/ai-code-mode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode@928

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode-skills@928

@tanstack/ai-codex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-codex@928

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@928

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-durable-stream@928

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-elevenlabs@928

@tanstack/ai-event-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-event-client@928

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@928

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@928

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@928

@tanstack/ai-grok-build

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok-build@928

@tanstack/ai-groq

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-groq@928

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-cloudflare@928

@tanstack/ai-isolate-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-daytona@928

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-node@928

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs@928

@tanstack/ai-isolate-quickjs-bun

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs-bun@928

@tanstack/ai-mcp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mcp@928

@tanstack/ai-memory

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-memory@928

@tanstack/ai-mistral

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mistral@928

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@928

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@928

@tanstack/ai-opencode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-opencode@928

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@928

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@928

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@928

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@928

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@928

@tanstack/ai-sandbox

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox@928

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-cloudflare@928

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-daytona@928

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-docker@928

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-local-process@928

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-sprites@928

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-vercel@928

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@928

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@928

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@928

@tanstack/ai-utils

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-utils@928

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@928

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@928

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@928

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@928

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@928

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@928

commit: 9ea1a12

@AlemTuzlak
AlemTuzlak merged commit 47e2464 into TanStack:mainAug 10, 2026
9 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

otelMiddleware root chat span carries last-iteration usage, not the documented cross-iteration roll-up

3 participants

@dexhunter@tombeckenham@AlemTuzlak