Skip to content

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

Open
tombeckenham wants to merge 2 commits into
mainfrom
735-tanstackai-openrouter-013-root-observability-metadata-is-forwarded-to-chatrequestmetadata-failing-sdk-recordstringstring-validation-on-every-call
Open

fix(ai-openrouter): stop forwarding root observability metadata to the wire request#737
tombeckenham wants to merge 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant