Skip to content

feat(ai-openrouter): surface generation metadata on RUN_FINISHED - #941

Open
trevorWieland wants to merge 6 commits into
TanStack:mainfrom
cat-cave:feat/openrouter-run-finished-metadata
Open

feat(ai-openrouter): surface generation metadata on RUN_FINISHED#941
trevorWieland wants to merge 6 commits into
TanStack:mainfrom
cat-cave:feat/openrouter-run-finished-metadata

Conversation

@trevorWieland

@trevorWieland trevorWieland commented Jul 15, 2026

Copy link
Copy Markdown

🎯 Changes

Expose OpenRouter generation IDs and selected providers on RUN_FINISHED.

Metadata is captured in-band from response chunks, following the cost handling in #654. The non-streaming structured-output fallback forwards the same fields.

Test plan

  • pnpm test:pr
  • pnpm --filter @tanstack/ai-e2e test:e2e
  • Added chat and Responses coverage for streaming and structured output.
  • Live /generation lookup pending API-key access.

✅ Checklist

  • I have followed the contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only.

Summary by CodeRabbit

  • New Features

    • OpenRouter structured outputs now include a generation ID and the provider that served the request when available.
    • RUN_FINISHED events now surface generation IDs and serving-provider details for streamed and non-streamed responses.
    • Metadata is preserved when usage information arrives in a trailing stream event.
  • Tests

    • Added coverage for provider and generation metadata across structured output, streaming, cost tracking, and end-to-end scenarios.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

OpenRouter adapters now extract generation IDs and selected providers from responses and streamed metadata. These values flow through structured-output results and RUN_FINISHED events, with updated contracts, fallback forwarding, unit tests, end-to-end coverage, and release metadata.

Changes

OpenRouter generation metadata

Layer / File(s) Summary
Public metadata contracts and fallback forwarding
packages/ai/src/activities/chat/adapter.ts, packages/ai/src/types.ts, packages/ai/src/activities/chat/index.ts, packages/ai/tests/...
Structured-output results and RUN_FINISHED events accept optional generationId and provider fields, which fallback streaming forwards and tests validate.
OpenRouter metadata extraction and adapter propagation
packages/ai-openrouter/src/adapters/metadata.ts, packages/ai-openrouter/src/adapters/text.ts, packages/ai-openrouter/src/adapters/responses-text.ts
Adapters extract selected providers, track response IDs and providers across streams, and include them in structured-output results and terminal events.
Adapter metadata tests
packages/ai-openrouter/tests/openrouter-adapter.test.ts, packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts
Structured-output and cost-tracking tests verify generation IDs and selected providers.
End-to-end exposure and release metadata
testing/e2e/global-setup.ts, testing/e2e/src/routes/api.openrouter-cost.ts, testing/e2e/tests/openrouter-cost.spec.ts, .changeset/generation-id-run-finished.md
The cost SSE fixture, API response, end-to-end assertions, and package version changes reflect the new metadata.

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

Sequence Diagram(s)

sequenceDiagram
  participant OpenRouter
  participant Adapter
  participant RUN_FINISHED
  participant E2E_API
  OpenRouter->>Adapter: response chunks with id and provider metadata
  Adapter->>RUN_FINISHED: generationId and provider
  RUN_FINISHED->>E2E_API: finished event payload
Loading

Possibly related PRs

  • TanStack/ai#654: Both changes extend OpenRouter stream plumbing and terminal RUN_FINISHED payloads.
  • TanStack/ai#789: Both changes modify fallback structured-output forwarding into RUN_FINISHED.

Suggested reviewers: season179, tombeckenham, alemtuzlak

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: surfacing generation metadata on RUN_FINISHED.
Description check ✅ Passed The PR description covers the required Changes, Checklist, and Release Impact sections, with concrete test notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

testing/e2e/global-setup.ts

Parsing error: "parserOptions.project" has been provided for @typescript-eslint/parser.
The file was not found in any of the provided project(s): testing/e2e/global-setup.ts

testing/e2e/tests/openrouter-cost.spec.ts

Parsing error: "parserOptions.project" has been provided for @typescript-eslint/parser.
The file was not found in any of the provided project(s): testing/e2e/tests/openrouter-cost.spec.ts


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/ai/src/activities/chat/index.ts (1)

3072-3073: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a nullish check for optional properties.

Checking for truthiness will drop the properties if they happen to be empty strings. While it is unlikely that a provider or generation ID would be an empty string, explicitly checking for != null (which covers both null and undefined) is generally a more robust pattern for optional string fields.

💡 Proposed refactor
-    ...(result.generationId ? { generationId: result.generationId } : {}),
-    ...(result.provider ? { provider: result.provider } : {}),
+    ...(result.generationId != null ? { generationId: result.generationId } : {}),
+    ...(result.provider != null ? { provider: result.provider } : {}),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai/src/activities/chat/index.ts` around lines 3072 - 3073, Update
the conditional spreads for result.generationId and result.provider to use
nullish checks (`!= null`) instead of truthiness checks, preserving these
properties when their values are empty strings while still omitting null or
undefined values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/ai/src/activities/chat/index.ts`:
- Around line 3072-3073: Update the conditional spreads for result.generationId
and result.provider to use nullish checks (`!= null`) instead of truthiness
checks, preserving these properties when their values are empty strings while
still omitting null or undefined values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 28332c4a-247b-41a0-808e-6373221c7fb5

📥 Commits

Reviewing files that changed from the base of the PR and between 5fcaf90 and 3426582.

📒 Files selected for processing (13)
  • .changeset/generation-id-run-finished.md
  • packages/ai-openrouter/src/adapters/metadata.ts
  • packages/ai-openrouter/src/adapters/responses-text.ts
  • packages/ai-openrouter/src/adapters/text.ts
  • packages/ai-openrouter/tests/openrouter-adapter.test.ts
  • packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts
  • packages/ai/src/activities/chat/adapter.ts
  • packages/ai/src/activities/chat/index.ts
  • packages/ai/src/types.ts
  • packages/ai/tests/chat-structured-output-stream.test.ts
  • testing/e2e/global-setup.ts
  • testing/e2e/src/routes/api.openrouter-cost.ts
  • testing/e2e/tests/openrouter-cost.spec.ts

@tombeckenham

Copy link
Copy Markdown
Contributor

Thanks for the PR, @trevorWieland! 🙌 @AlemTuzlak will take a look.

Automated pre-review checks

  • ✅ CI passing
  • ✅ No merge conflicts
  • ✅ Changeset present
  • ✅ E2E test changes included

Automated triage — a human review follows.

@tombeckenham tombeckenham added the waiting-on: maintainer The ball is in the maintainers’ court label Jul 23, 2026
@nx-cloud

nx-cloud Bot commented Aug 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 01ee2b4

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 1m 52s View ↗

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-acp

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

@tanstack/ai-angular

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

@tanstack/ai-anthropic

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

@tanstack/ai-bedrock

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

@tanstack/ai-claude-code

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

@tanstack/ai-client

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

@tanstack/ai-code-mode

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

@tanstack/ai-code-mode-skills

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

@tanstack/ai-codex

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

@tanstack/ai-devtools-core

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

@tanstack/ai-durable-stream

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

@tanstack/ai-elevenlabs

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

@tanstack/ai-event-client

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

@tanstack/ai-fal

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

@tanstack/ai-gemini

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

@tanstack/ai-grok

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

@tanstack/ai-grok-build

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

@tanstack/ai-groq

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

@tanstack/ai-isolate-cloudflare

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

@tanstack/ai-isolate-node

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

@tanstack/ai-isolate-quickjs

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

@tanstack/ai-mcp

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

@tanstack/ai-memory

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

@tanstack/ai-mistral

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

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-opencode

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

@tanstack/ai-openrouter

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

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-sandbox

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

@tanstack/ai-sandbox-cloudflare

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

@tanstack/ai-sandbox-daytona

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

@tanstack/ai-sandbox-docker

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

@tanstack/ai-sandbox-local-process

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

@tanstack/ai-sandbox-sprites

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

@tanstack/ai-sandbox-vercel

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

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-utils

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

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@941

@tanstack/preact-ai-devtools

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

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

commit: 01ee2b4

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

Labels

waiting-on: maintainer The ball is in the maintainers’ court

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants