Skip to content

fix(agent): populate primary model from models fallback array (#121) - #124

Open
warun7 wants to merge 4 commits into
OpenRouterTeam:mainfrom
warun7:fix/server-tools-model-fallback
Open

warun7 wants to merge 4 commits into
OpenRouterTeam:mainfrom
warun7:fix/server-tools-model-fallback

Conversation

@warun7

@warun7 warun7 commented Sep 4, 2026

Copy link
Copy Markdown

Fixes #121.

Summary

  • Populated model from models[0] across callModel, resolveAsyncFunctions, and ModelResult when model is omitted.
  • OpenRouter server tools (and backend request processors) require the primary model property on incoming requests.
  • Synchronized model and models in buildNextTurnParamsContext and applyNextTurnParamsToRequest.
  • Preserved explicit model when provided alongside models.
  • Added unit tests for server tools with models fallback, explicit model preservation, and async models functions.
  • Added changeset.

LukasParke and others added 2 commits October 21, 2026 18:03
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Add fillPrimaryModelFromModels (src/lib/primary-model.ts) and call it
  once in ModelResult.resolveRequestForContext for both the static and
  async-function paths, plus in applyNextTurnParamsToRequest.
- Drop the duplicate blocks in call-model.ts (ModelResult already
  resolves it) and async-params.ts (keeps the public
  resolveAsyncFunctions unchanged).
- Revert buildNextTurnParamsContext reporting models as [model] when the
  caller never set models; add a test pinning that.
- Document that applyNextTurnParamsToRequest resets an explicit model to
  models[0] when only models changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@LukasParke

LukasParke commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

I pushed three non-force commits (merge, refactor, and a follow-up that moves the helper):

  1. A merge of main, with no conflicts.
  2. A refactor on top of the fix:
    • The four copied models[0] blocks are now one helper, fillPrimaryModelFromModels in src/lib/async-params.ts (a separate module tripped the sentrux god-file gate on model-result.ts). It's called once in ModelResult.resolveRequestForContext, which covers both the static and the async-function paths, and once in applyNextTurnParamsToRequest. I dropped the copies in call-model.ts (redundant, since ModelResult resolves it) and in async-params.ts, so the public resolveAsyncFunctions behaves the same as on main.
    • Reverted the change where buildNextTurnParamsContext reported models as [model] when the caller never set it, and added a test for that.
    • Documented that applyNextTurnParamsToRequest resets an explicit model to models[0] when only models changes, because models takes precedence.

typecheck, lint and unit tests (1244) pass locally.

Still open: we need live API confirmation that the backend actually requires a top-level model with server tools. That means a POST /api/v1/responses with tools: [{"type":"openrouter:datetime"}], once with models only and once with model + models. I couldn't run it because no API key was available.

model-result.ts already imports async-params; adding a separate module
pushed it over the sentrux god-file threshold and failed the structural
gate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@warun7

warun7 commented Oct 21, 2026

Copy link
Copy Markdown
Author

Ran the live check on POST /api/v1/responses with tools: [{"type":"openrouter:datetime"}], using two model pairs (2026-10-21, ~18:35 UTC):

Request anthropic/claude-haiku-5.5 + openai/gpt-6-luna mistralai/mistral-small-2603 + mistralai/mistral-large-2512
model only (control) 200, openrouter:datetime item in output 200, openrouter:datetime item in output
models only 400 Model call failed: invalid request (400), on every attempt in previous_errors 400, same error
model + models 200, openrouter:datetime item in output 429 from the provider (Mistral rate limit, not a validation error)

So yes, the backend needs a top-level model when server tools are used. models alone is rejected for both pairs, and adding model makes the same request succeed. The error text has changed since #121 was filed (it used to be Server tool request failed), but the behavior is the same.

I also ran the failing example from #121 (callModel with only models and serverTool({ type: "openrouter:datetime" })) against this branch's build at e9f6c28. With the Haiku/GPT pair it now returns Today is Wednesday, October 21, 2026 (UTC). The Mistral pair hit the same provider 429.

@LukasParke

Copy link
Copy Markdown
Contributor

Thanks @warun7, this confirms it: models alone gets a 400 when server tools are used, and adding model makes the same request succeed. #121's repro also passes on e9f6c28. That was the last open question here, so from my side this is ready for maintainer review.

The backend should still be fixed too (accept models-only requests that include server tools), so the Python and Go ports and raw API users are covered as well. That's tracked separately; this SDK-side default stays useful either way.

🤖 Generated with Claude Code

This branch has not been deployed

No deployments
Sign up for free to 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.

Server Tools fail when using model fallback via models

2 participants