Skip to content

[AISAGE-706] refactor(ai-gateway): derive legacy variants from reasoning_options - #7587

Merged
chrarnoldus merged 2 commits into
mainfrom
refactor/reasoning-options-source-of-truth
Oct 15, 2026
Merged

chrarnoldus merged 2 commits into
mainfrom
refactor/reasoning-options-source-of-truth

Conversation

@chrarnoldus

Copy link
Copy Markdown
Contributor

Follow-up to #7580, which has been merged; this PR is based on main.

Summary

#7580 serves reasoning_options by converting them out of the legacy variants. Direct BYOK models therefore go through a round trip: models.dev reasoning_options → legacy variants (at sync time) → reasoning_options again.

This PR turns that around and makes reasoning_options the source of truth:

OpenRouter metadata ─┐
models.dev (BYOK)  ──┼─► reasoning_options ─► variants (legacy, for Kilo CLI v1)
model-name fallback ─┘

Changes

  • OpenRouter models. getOpenRouterReasoningOptions replaces getOpenRouterDerivedModelVariants:
    • The listed efforts become an effort entry, sorted from lowest to highest. none is added when reasoning is optional; Kimi K3 is still the exception.
    • Optional reasoning with no listed efforts becomes toggle.
    • Mandatory reasoning with no listed efforts becomes [].
  • Model-name fallback. getFallbackReasoningOptions replaces getFallbackModelVariants. variants.ts is now reasoning-options.ts.
  • Legacy variants. toLegacyVariants and withLegacyVariants (new legacy-variants.ts) are the only place OpenRouter-shaped variants are built:
    • Effort values become effort-named variants.
    • toggle becomes instant/thinking.
    • toggle plus efforts without none gets an extra none.
    • [] becomes a single thinking.
    • Effort variants of Anthropic models (ai_sdk_provider: 'anthropic') get the matching verbosity.
  • Direct BYOK:
    • DirectByokModelSchema.variants is replaced by reasoning_options.
    • The sync stores the models.dev reasoning_options as they are. It treats [] and options with no usable effort as missing, which was already the effective behaviour.
    • Fallback options and verbosity are now applied when the catalog is served, not during the sync.
    • The hard-coded BytePlus list uses reasoning_options.
  • Custom LLMs and auto models: admin-authored variants are served unchanged. If a definition only has reasoning_options, legacy variants are derived from them.
  • getReasoningOptions / withReasoningOptions (variants → reasoning_options) from [AISAGE-706] feat(ai-gateway): serve models.dev reasoning_options in the model catalog #7580 are removed.
  • Fixes main's web-shared typecheck: [AISAGE-706] feat(ai-gateway): serve models.dev reasoning_options in the model catalog #7580 and feat(auto-routing): resolve model aliases before benchmarking #7582 both added the same getOpenRouterModelsMetadataFromDatabase import to openrouter/index.test.ts.

Behaviour changes compared with #7580

  • Mandatory reasoning with no levels (28 models in the local catalog, e.g. liquid/lfm-2.5-24b-a2b, aion-labs/aion-3.0): reasoning_options changes from effort: ["high"] to []. This is the models.dev meaning for "always reasons, no caller control". The legacy variant stays a single thinking.
  • Custom LLMs with only admin-authored variants no longer get derived reasoning_options. An admin can add reasoning_options to the definition; legacy variants are then derived from it when variants is absent.
  • Legacy variants, all edge cases that don't affect the OpenRouter catalog:
    • Mercury's model-name fallback (only used without OpenRouter metadata) names its off variant none instead of instant.
    • opencode-go minimax-m3 (toggle, Anthropic API) no longer adds verbosity: high to its thinking variant. Before this PR the sync added it but the gateway path did not; on/off variants now never carry verbosity, so Claude 4.5 and older stay unchanged.
  • Deploy transition. Stored BYOK lists still contain variants until the next sync-providers run (every 5 minutes) plus the 10-minute list cache. During that window, BYOK reasoning models use the model-name fallback.

Verification

  • End to end (run before rebasing onto main). I ran the local stack, synced providers from live OpenRouter and models.dev, then fetched GET /api/gateway/models (397 models):
  • Tests. pnpm --filter web test -- src/lib/providers ../../packages/web-shared/src/lib/ai-gateway ../../packages/web-shared/src/lib/organizations src/tests/openrouter-models.test.ts src/routers/organizations/organization-settings-router.test.ts src/app/api/cron/sync-providers: 117 suites passed (2293 tests, 2 skipped) after rebasing onto main.
  • Typecheck. scripts/typecheck-all.sh --changes-only and pnpm --filter @kilocode/web-shared --filter web --filter ai-gateway --filter @kilocode/trpc typecheck: pass.
  • Lint and format. oxlint (apps/web/src/lib/providers, packages/web-shared/src/lib/ai-gateway, packages/db/src), oxfmt --check and git diff --check: clean.
  • Not checked end to end: BYOK catalog output (it needs a user with BYOK keys). Unit tests cover the sync and conversion rules instead.

@chrarnoldus chrarnoldus self-assigned this Oct 15, 2026
@chrarnoldus

Copy link
Copy Markdown
Contributor Author

E2E: GET /api/gateway/models

Results: https://gist.github.com/chrarnoldus/c514eac6b4cd3ed99c8c953d05b81195

File Contents
gateway-models.json Full response, pretty-printed
summary.tsv Per model: id, ai_sdk_provider, variant names, reasoning_options
reasoning-options-diff-vs-pr7580.json Models whose reasoning_options differ from #7580

Setup

I ran this branch (5c2fd5d99d) on the local stack (pnpm dev:start nextjs) against a freshly migrated database. Calling GET /api/cron/sync-providers loaded live OpenRouter metadata (669 models) and models.dev data into it. I then fetched /api/gateway/models and /api/openrouter/models through the web app. The two responses are identical, with 397 models. The request was unauthenticated, so the output contains no BYOK models.

Results

@kilo-code-bot

kilo-code-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

This refactor flips reasoning_options to the source of truth and derives legacy variants from it; I traced the conversion, sync, serve, and merge paths across all 19 changed files and found the derivation, precedence, deduplication, Anthropic verbosity, and fallback behavior to be internally consistent and covered by tests.

Notes

  • toLegacyVariants output was verified line-by-line against the removed getFallbackModelVariants / getOpenRouterDerivedModelVariants behavior: effort ordering, toggle→instant/thinking, []→thinking, verbosity only on Anthropic effort variants, and Kimi K3 exception all match.
  • The []-means-always-on semantic change for mandatory-reasoning models with no listed efforts is intentional and documented in the PR description; legacy variants stay unchanged.
  • Verified the duplicate getOpenRouterModelsMetadataFromDatabase import removal leaves exactly one import and all references resolve.
  • No unupdated consumers of the removed variants field on DirectByokModel and no dangling references to the deleted providers/variants module (git grep confirmed).
  • No memory-leak surface introduced: the change is pure data transformation with no new listeners, timers, caches, or retained state.
Files Reviewed (19 files)
  • apps/web/src/lib/providers/direct-byok/sync-direct-byok.test.ts
  • apps/web/src/lib/providers/direct-byok/sync-direct-byok.ts
  • packages/db/src/schema-types.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/listAvailableCustomLlms.ts
  • packages/web-shared/src/lib/ai-gateway/providers/direct-byok/byteplus-coding.ts
  • packages/web-shared/src/lib/ai-gateway/providers/direct-byok/index.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/direct-byok/index.ts
  • packages/web-shared/src/lib/ai-gateway/providers/direct-byok/model-list.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/direct-byok/model-list.ts
  • packages/web-shared/src/lib/ai-gateway/providers/legacy-variants.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/legacy-variants.ts
  • packages/web-shared/src/lib/ai-gateway/providers/model-settings.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/model-settings.ts
  • packages/web-shared/src/lib/ai-gateway/providers/openrouter/index.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/openrouter/index.ts
  • packages/web-shared/src/lib/ai-gateway/providers/reasoning-options.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/reasoning-options.ts
  • packages/web-shared/src/lib/ai-gateway/providers/variants.test.ts (deleted)
  • packages/web-shared/src/lib/ai-gateway/providers/variants.ts (deleted)

Reviewed by deepseek-v4.1-flash · Input: 119.2K · Output: 30.9K · Cached: 2.4M

Review guidance: REVIEW.md from base branch main

@chrarnoldus
chrarnoldus merged commit 8b402e6 into main Oct 15, 2026
60 checks passed
@chrarnoldus
chrarnoldus deleted the refactor/reasoning-options-source-of-truth branch October 15, 2026 13:06
@chrarnoldus chrarnoldus changed the title refactor(ai-gateway): derive legacy variants from reasoning_options [AISAGE-706] refactor(ai-gateway): derive legacy variants from reasoning_options Oct 19, 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

Development

Successfully merging this pull request may close these issues.

2 participants