feat(ai-openrouter): per-request native combined tools + outputSchema mode#836
feat(ai-openrouter): per-request native combined tools + outputSchema mode#836season179 wants to merge 3 commits into
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 (4)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughOpenRouter now supports sending tools and schema-constrained output in a single request for eligible upstream models. The package exports the combined-mode model set, both OpenRouter text adapters use it to gate request shaping, and tests plus E2E support cover the new paths. ChangesOpenRouter combined tools + schema mode
Sequence Diagram(s)sequenceDiagram
participant OpenRouterTextAdapter
participant openRouterSupportsCombinedToolsAndSchema
participant OPENROUTER_COMBINED_TOOLS_AND_SCHEMA_MODELS
participant makeStructuredOutputCompatible
participant OpenRouter Chat Completions endpoint
OpenRouterTextAdapter->>openRouterSupportsCombinedToolsAndSchema: supportsCombinedToolsAndSchema(modelOptions)
openRouterSupportsCombinedToolsAndSchema->>OPENROUTER_COMBINED_TOOLS_AND_SCHEMA_MODELS: check resolved upstream model ids
OpenRouterTextAdapter->>makeStructuredOutputCompatible: convert outputSchema
OpenRouterTextAdapter->>OpenRouter Chat Completions endpoint: send tools + responseFormat
sequenceDiagram
participant OpenRouterResponsesTextAdapter
participant openRouterSupportsCombinedToolsAndSchema
participant OPENROUTER_COMBINED_TOOLS_AND_SCHEMA_MODELS
participant OpenRouter Responses endpoint
OpenRouterResponsesTextAdapter->>openRouterSupportsCombinedToolsAndSchema: supportsCombinedToolsAndSchema(modelOptions)
openRouterSupportsCombinedToolsAndSchema->>OPENROUTER_COMBINED_TOOLS_AND_SCHEMA_MODELS: check resolved upstream model ids
OpenRouterResponsesTextAdapter->>OpenRouter Responses endpoint: send tools + text.format
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 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)
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
packages/ai-openrouter/vite.config.tsParsing error: "parserOptions.project" has been provided for 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 |
… mode
Give both OpenRouter text adapters (chat-completions and Responses) native
combined mode. When chat({ outputSchema, tools, stream: true }) targets a
combined-capable upstream model, the schema is wired into the same streaming
request as the tools and the final-turn JSON is harvested directly, skipping
the separate finalization round-trip.
Capability is per resolved upstream model via the new exported
OPENROUTER_COMBINED_TOOLS_AND_SCHEMA_MODELS set, consulted by both adapters'
supportsCombinedToolsAndSchema(). Membership tracks the upstream per-provider
combined-mode gates (Anthropic 4.5+ mirrors ANTHROPIC_COMBINED_TOOLS_AND_SCHEMA_MODELS,
Gemini 3.x, OpenAI strict json_schema era, Grok 4.x) rather than the broader
catalog responseFormat flag.
Closes TanStack#612.
f8caa2f to
608f0c7
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/ai-openrouter/tests/openrouter-combined-structured-output.test.ts (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove this unit test alongside the source adapter files.
This test is in
packages/ai-openrouter/tests/, but the guideline requires*.test.tsunit tests to live alongside source code.As per coding guidelines, "
**/*.test.ts: Place unit tests alongside source code in*.test.tsfiles".🤖 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-openrouter/tests/openrouter-combined-structured-output.test.ts` at line 1, The unit test is in the wrong location for the project’s test layout guidelines. Move the openrouter structured output test from the separate tests folder to sit alongside the relevant source adapter files, keeping the same `*.test.ts` naming and preserving the existing Vitest setup/imports.Source: Coding guidelines
🤖 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/src/adapters/responses-text.ts`:
- Line 1578: The `combinedOutputSchema` assignment in `responses-text.ts` uses a
redundant type assertion on `options.outputSchema`, triggering
`no-unnecessary-type-assertion`. Replace the cast with an explicit type
annotation on `combinedOutputSchema` while keeping the `JSONSchema` import
referenced, matching the approach used in the chat-completions adapter and
preserving the existing `options.outputSchema` type.
In `@packages/ai-openrouter/src/adapters/text.ts`:
- Line 1197: The `combinedOutputSchema` assignment in `text.ts` uses a redundant
`as JSONSchema` assertion on `options.outputSchema`, triggering the
`no-unnecessary-type-assertion` lint rule. Replace the cast with an explicit
type annotation on `combinedOutputSchema` (keeping the `JSONSchema` import in
use) and preserve the existing `JSONSchema | undefined` shape from
`options.outputSchema`.
---
Nitpick comments:
In `@packages/ai-openrouter/tests/openrouter-combined-structured-output.test.ts`:
- Line 1: The unit test is in the wrong location for the project’s test layout
guidelines. Move the openrouter structured output test from the separate tests
folder to sit alongside the relevant source adapter files, keeping the same
`*.test.ts` naming and preserving the existing Vitest setup/imports.
🪄 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: 10aa8a2c-1c20-49e3-a970-477975318133
📒 Files selected for processing (10)
.changeset/openrouter-combined-tools-and-schema.mdpackages/ai-openrouter/package.jsonpackages/ai-openrouter/src/adapters/responses-text.tspackages/ai-openrouter/src/adapters/text.tspackages/ai-openrouter/src/index.tspackages/ai-openrouter/src/internal/combined-tools-and-schema.tspackages/ai-openrouter/src/model-meta.tspackages/ai-openrouter/tests/openrouter-combined-structured-output.test.tspackages/ai-openrouter/vite.config.tstesting/e2e/src/lib/feature-support.ts
Closes #612 (extends #605).
What changed
This adds native OpenRouter combined mode for
tools + outputSchemain both text adapters:responseFormat: { type: 'json_schema', jsonSchema: { strict: true, schema } }on the combined path.text.formaton the combined path.Because OpenRouter is a routing layer, the capability check is intentionally conservative. The adapter now checks the primary model plus any
modelOptions.modelsfallbacks and only enables combined mode when every possible routed model is in the combined-capable set.:variantsuffixes such as:nitroare still ignored for capability purposes because they are routing directives, not different upstream model capabilities.The new
OPENROUTER_COMBINED_TOOLS_AND_SCHEMA_MODELSset is exported from@tanstack/ai-openrouter/model-meta, as requested in #612. The list is derived from the upstream provider gates rather than the broader OpenRouterresponseFormatflag, so older structured-output-capable models that do not support native tools + schema stay on the legacy path.Tests
Added OpenRouter unit coverage for:
responseFormatpayloadtext.formatpayload:variantsuffixes not changing capabilitytext.*fields on ResponsesOPENROUTER_CHAT_MODELSI also added the
model-metapackage subpath to the build entries so the requested export is actually published.Local verification
Passed:
CI=true pnpm --filter @tanstack/ai-openrouter test:lib -- openrouter-combined-structured-output.test.tsCI=true pnpm --filter @tanstack/ai-openrouter test:typesCI=true pnpm --filter @tanstack/ai-openrouter test:eslintCI=true pnpm --filter @tanstack/ai-openrouter buildCI=true pnpm --filter @tanstack/ai-openrouter test:buildCI=true pnpm --filter @tanstack/ai-e2e exec playwright test tests/agentic-structured-stream.spec.ts --grep "openrouter"I also ran
pnpm test:pr. The OpenRouter affected targets passed, but the full gate currently stops inroot:test:kiiraon unrelated docs snippets indocs/adapters/grok.mdanddocs/media/video-generation.md.Summary by CodeRabbit
New Features
Bug Fixes