Skip to content

fix(ai-openrouter): stop forwarding root observability metadata to the wire request#737

Merged
AlemTuzlak merged 2 commits into
mainfrom
735-tanstackai-openrouter-013-root-observability-metadata-is-forwarded-to-chatrequestmetadata-failing-sdk-recordstringstring-validation-on-every-call
Jun 22, 2026
Merged

fix(ai-openrouter): stop forwarding root observability metadata to the wire request#737
AlemTuzlak merged 2 commits into
mainfrom
735-tanstackai-openrouter-013-root-observability-metadata-is-forwarded-to-chatrequestmetadata-failing-sdk-recordstringstring-validation-on-every-call

Conversation

@tombeckenham

@tombeckenham tombeckenham commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

Fixes #735.

The OpenRouter chat-completions adapter (since 0.13.0, via #660) and responses adapter (since 0.9.0, via #545) copied chat()'s root-level observability metadata onto the wire as chatRequest.metadata / responsesRequest.metadata. The @openrouter/sdk validates those fields as Record<string, string>, so structured observability metadata (objects, arrays — the documented usage for middleware/devtools/event-client consumers) failed client-side Zod validation with Input validation failed on every call. The spread also sat after ...modelOptions, clobbering an intentional, correctly-typed modelOptions.metadata.

  • Remove the root-metadata forwarding from both adapters' request builders; modelOptions.metadata is the sole source for OpenRouter wire metadata, matching every other adapter.
  • Invert the unit test that locked in the bug; add clobber-protection and omission tests for both adapters.
  • Add an E2E regression spec (root-metadata-wire.spec.ts + a test-only structuredRootMetadata flag in api.chat.ts) that exercises the real SDK's outbound validation — verified to fail pre-fix with the exact error from the issue and pass post-fix.
  • Update the stale TextOptions.metadata doc comment in @tanstack/ai to state the observability-only contract.

Observability consumers are unaffected: the event stream metadata is emitted by the chat() orchestrator, not the adapter.

✅ Checklist

  • I have followed the steps in 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 (no release).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Root-level metadata is now treated as observability-only and is no longer forwarded to the provider wire request, preventing validation errors.
  • Documentation

    • Clarified metadata docs to state root metadata is observability-only and recommend using model-level options for provider-side request metadata.
  • Tests

    • Added unit and end-to-end tests (including a test flag path) to verify correct metadata handling and prevent regressions.

…e wire request

The OpenRouter chat-completions adapter (since 0.13.0) and responses
adapter (since 0.9.0) copied chat()'s root-level observability metadata
onto the wire as chatRequest.metadata / responsesRequest.metadata. The
@openrouter/sdk validates those fields as Record<string, string>, so
structured observability metadata (objects, arrays — the documented
usage for middleware/devtools consumers) failed client-side Zod
validation with "Input validation failed" on every call. The spread
also clobbered an intentional, correctly-typed modelOptions.metadata.

Root metadata is observability-only again (middleware, devtools, event
client) and modelOptions.metadata is the sole source for OpenRouter
wire metadata, matching every other adapter. The TextOptions.metadata
doc comment now states this contract explicitly.

Fixes #735

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c535b5b5-8544-4af4-95f2-16035ee246ed

📥 Commits

Reviewing files that changed from the base of the PR and between fc581b0 and 5a75984.

📒 Files selected for processing (1)
  • packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts

📝 Walkthrough

Walkthrough

Stops forwarding root-level observability metadata to OpenRouter provider requests (which expect Record<string,string>); adapters now use only modelOptions.metadata for wire metadata. Updates JSDoc, adapter mappers, unit tests, and adds an e2e regression test and a changeset.

Changes

Stop Forwarding Root Observability Metadata to OpenRouter Wire

Layer / File(s) Summary
Type Contract: Clarify Metadata as Observability-Only
packages/ai/src/types.ts
JSDoc for TextOptions.metadata updated to state root metadata is observability-only and adapters do not forward it; use modelOptions for provider wire metadata.
OpenRouter Adapter Implementation: Remove Root Metadata Forwarding
packages/ai-openrouter/src/adapters/text.ts, packages/ai-openrouter/src/adapters/responses-text.ts
mapOptionsToRequest no longer spreads options.metadata into OpenRouter ChatRequest/ResponsesRequest; comments added clarifying SDK validation and that modelOptions.metadata is the wire source.
Unit Test Updates: Verify Metadata Handling
packages/ai-openrouter/tests/openrouter-adapter.test.ts, packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts
Tests updated and added to assert root observability metadata is omitted from SDK requests while modelOptions.metadata is forwarded when present; also tests omission of wire metadata when only root metadata exists.
E2E Regression Test: Validate Root Metadata Is Not Forwarded
testing/e2e/src/routes/api.chat.ts, testing/e2e/tests/root-metadata-wire.spec.ts
E2E route handler gains a test-only structuredRootMetadata flag to include structured root metadata; Playwright test posts a chat request with it and asserts no OpenRouter validation error occurs.
Release Documentation
.changeset/root-metadata-not-forwarded.md
Changeset records patch releases for @tanstack/ai-openrouter and @tanstack/ai describing the fix and type documentation change.

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers:

  • AlemTuzlak

"A root of metadata too broad,
Split from the wire with a nod,
Observability stays,
While modelOptions sways,
The OpenRouter path, now untrod. 🐰✨"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: stopping forwarding of root observability metadata to OpenRouter wire requests in the adapter.
Description check ✅ Passed The PR description fully addresses the template requirements with clear motivation, relevant issue reference, completed checklist items, and proper changeset generation confirmation.
Linked Issues check ✅ Passed The code changes fully implement all objectives from #735: removing root metadata forwarding from both adapters, protecting modelOptions.metadata from clobbering, adding unit tests and E2E regression spec, and updating documentation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #735: adapter fixes, test updates for both adapters, documentation updates, and supporting E2E infrastructure—no unrelated changes detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 735-tanstackai-openrouter-013-root-observability-metadata-is-forwarded-to-chatrequestmetadata-failing-sdk-recordstringstring-validation-on-every-call

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 and usage tips.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

5 package(s) bumped directly, 26 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-event-client 0.5.4 → 1.0.0 Changeset
@tanstack/ai-fal 0.7.23 → 1.0.0 Changeset
@tanstack/ai-gemini 0.15.1 → 1.0.0 Changeset
@tanstack/ai-openrouter 0.13.1 → 1.0.0 Changeset
@tanstack/ai-anthropic 0.15.1 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.2.5 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.2.5 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.20 → 1.0.0 Dependent
@tanstack/ai-grok 0.11.2 → 1.0.0 Dependent
@tanstack/ai-groq 0.4.2 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.30 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.30 → 1.0.0 Dependent
@tanstack/ai-ollama 0.8.1 → 1.0.0 Dependent
@tanstack/ai-openai 0.14.1 → 1.0.0 Dependent
@tanstack/ai-preact 0.9.4 → 1.0.0 Dependent
@tanstack/ai-react 0.15.4 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.6 → 1.0.0 Dependent
@tanstack/ai-solid 0.13.4 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.6 → 1.0.0 Dependent
@tanstack/ai-svelte 0.13.4 → 1.0.0 Dependent
@tanstack/ai-vue 0.13.4 → 1.0.0 Dependent
@tanstack/openai-base 0.8.1 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.28.0 → 0.29.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-client 0.16.3 → 0.16.4 Dependent
@tanstack/ai-devtools-core 0.4.8 → 0.4.9 Dependent
@tanstack/ai-isolate-cloudflare 0.2.21 → 0.2.22 Dependent
@tanstack/ai-mcp 0.1.0 → 0.1.1 Dependent
@tanstack/ai-vue-ui 0.2.16 → 0.2.17 Dependent
@tanstack/preact-ai-devtools 0.1.51 → 0.1.52 Dependent
@tanstack/react-ai-devtools 0.2.51 → 0.2.52 Dependent
@tanstack/solid-ai-devtools 0.2.51 → 0.2.52 Dependent

@nx-cloud

nx-cloud Bot commented Jun 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit fc581b0

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

☁️ Nx Cloud last updated this comment at 2026-06-10 04:48:07 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jun 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-anthropic

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

@tanstack/ai-client

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

@tanstack/ai-code-mode

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

@tanstack/ai-code-mode-skills

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

@tanstack/ai-devtools-core

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

@tanstack/ai-elevenlabs

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

@tanstack/ai-event-client

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

@tanstack/ai-fal

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

@tanstack/ai-gemini

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

@tanstack/ai-grok

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

@tanstack/ai-groq

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

@tanstack/ai-isolate-cloudflare

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

@tanstack/ai-isolate-node

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

@tanstack/ai-isolate-quickjs

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

@tanstack/ai-mcp

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

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-openrouter

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

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-utils

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

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/openai-base

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

@tanstack/preact-ai-devtools

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

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

commit: 5a75984

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts`:
- Line 6: The type-only import OpenRouterResponsesTextProviderOptions is out of
order and violates import/order; move the "import type {
OpenRouterResponsesTextProviderOptions }" line so it matches the repository's
import grouping (e.g., third-party imports first, then internal modules, then
type-only or side-effect imports) or group it with the other local/type imports
in the test file to satisfy the linter; alternatively run eslint --fix to
reorder automatically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44df3767-c300-483f-a1b5-f377e80d518c

📥 Commits

Reviewing files that changed from the base of the PR and between e8ce0e1 and fc581b0.

📒 Files selected for processing (8)
  • .changeset/root-metadata-not-forwarded.md
  • 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/types.ts
  • testing/e2e/src/routes/api.chat.ts
  • testing/e2e/tests/root-metadata-wire.spec.ts

Comment thread packages/ai-openrouter/tests/openrouter-responses-adapter.test.ts Outdated
…view

modelOptions accepts { metadata } directly in the responses test, so the
OpenRouterResponsesTextProviderOptions assertion (and its out-of-order
type import, flagged by CodeRabbit) are unnecessary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tombeckenham tombeckenham requested a review from AlemTuzlak June 10, 2026 04:56
@AlemTuzlak AlemTuzlak merged commit 243b8fa into main Jun 22, 2026
10 checks passed
@AlemTuzlak AlemTuzlak deleted the 735-tanstackai-openrouter-013-root-observability-metadata-is-forwarded-to-chatrequestmetadata-failing-sdk-recordstringstring-validation-on-every-call branch June 22, 2026 14:47
@github-actions github-actions Bot mentioned this pull request Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants