feat: support custom model info overrides - #1142
Conversation
Custom model IDs on router providers (e.g. anthropic/claude-sonnet-4-6 on OpenRouter) render a context window of 1 and a 7000% usage figure when the model list is unavailable. Spec covers three defects: the TaskHeader `|| 1` fallback with no upper clamp, webview/host divergence in model resolution, and the absence of any override UI outside the OpenAI-compatible provider. Design: one `customModelInfo` overlay field on the base provider schema, one shared `applyCustomModelInfo` helper bound at both resolution layers, display hardening independent of any override, and a collapsible settings panel for the five router providers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR adds validated custom model metadata overrides for router providers. Shared resolution applies overrides in the webview and provider handlers, preserves configured model IDs, exposes router settings controls, and hardens context-window rendering. ChangesCustom model settings
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SettingsUI
participant ModelSelection
participant MetadataResolver
participant ProviderHandler
SettingsUI->>ModelSelection: persist customModelInfo
ModelSelection->>MetadataResolver: resolve selected model metadata
ProviderHandler->>MetadataResolver: apply custom overrides to discovered model
MetadataResolver-->>ProviderHandler: resolved ModelInfo
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ 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/types/src/__tests__/custom-model-info.test.tsESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. packages/types/src/model.tsESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. src/api/providers/__tests__/kenari.spec.tsESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.
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 |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/api/providers/__tests__/openrouter.spec.ts (1)
142-160: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTest the undiscovered-model synthesis path.
These tests use a fetched model. Add a case where the configured model ID is absent from the fetched model map and
customModelInfo.contextWindowis valid. Assert that the handler retains the configured ID and derivesmaxTokensfrom synthesized metadata.
src/api/providers/__tests__/openrouter.spec.ts#L142-L160: add an unlisted OpenRouter model case.src/api/providers/__tests__/requesty.spec.ts#L161-L177: add an unlisted Requesty model case.src/api/providers/__tests__/unbound.spec.ts#L41-L60: add an unlisted Unbound model case.🤖 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 `@src/api/providers/__tests__/openrouter.spec.ts` around lines 142 - 160, Add undiscovered-model synthesis test cases in src/api/providers/__tests__/openrouter.spec.ts lines 142-160, src/api/providers/__tests__/requesty.spec.ts lines 161-177, and src/api/providers/__tests__/unbound.spec.ts lines 41-60. Configure each handler with a model ID absent from the fetched model map and valid customModelInfo.contextWindow, then assert fetchModel retains the configured ID and derives maxTokens from the synthesized metadata.
🤖 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/types/src/model.ts`:
- Around line 191-198: Update customModelInfoSchema so maxTokens and
contextWindow validate safe positive integers, matching the Number.isSafeInteger
checks in applyCustomModelInfo. Add a boundary test covering values above
Number.MAX_SAFE_INTEGER and verify those overrides are rejected during schema
validation.
In `@webview-ui/src/components/chat/TaskHeader.tsx`:
- Around line 72-73: Update the contextWindowForDisplay calculation in
TaskHeader to require a finite, positive numeric value, excluding Infinity and
NaN before rendering context details. Add a regression test covering an infinite
context window and verifying that no context total or progress display is
rendered.
In `@webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts`:
- Around line 311-321: Remove the as any assertions from the mockUseRouterModels
and mockUseOpenRouterModelProviders return values. Update these mocks to use
typed query fixtures or precise mock return types that satisfy each hook’s
query-result contract, without using double assertions.
---
Nitpick comments:
In `@src/api/providers/__tests__/openrouter.spec.ts`:
- Around line 142-160: Add undiscovered-model synthesis test cases in
src/api/providers/__tests__/openrouter.spec.ts lines 142-160,
src/api/providers/__tests__/requesty.spec.ts lines 161-177, and
src/api/providers/__tests__/unbound.spec.ts lines 41-60. Configure each handler
with a model ID absent from the fetched model map and valid
customModelInfo.contextWindow, then assert fetchModel retains the configured ID
and derives maxTokens from the synthesized metadata.
🪄 Autofix
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 Plus
Run ID: 93a2e87c-6d2f-45e6-b3db-67fe48e21194
📒 Files selected for processing (25)
docs/superpowers/specs/2026-08-04-custom-model-settings-design.mdpackages/types/src/__tests__/custom-model-info.test.tspackages/types/src/model.tspackages/types/src/provider-settings.tssrc/api/providers/__tests__/openrouter.spec.tssrc/api/providers/__tests__/requesty.spec.tssrc/api/providers/__tests__/unbound.spec.tssrc/api/providers/openrouter.tssrc/api/providers/requesty.tssrc/api/providers/router-provider.tssrc/api/providers/unbound.tswebview-ui/src/components/chat/TaskHeader.tsxwebview-ui/src/components/chat/__tests__/TaskHeader.spec.tsxwebview-ui/src/components/settings/ApiOptions.tsxwebview-ui/src/components/settings/CustomModelInfoSettings.tsxwebview-ui/src/components/settings/__tests__/CustomModelInfoSettings.spec.tsxwebview-ui/src/components/settings/providers/OpenRouter.tsxwebview-ui/src/components/settings/providers/Requesty.tsxwebview-ui/src/components/settings/providers/Unbound.tsxwebview-ui/src/components/settings/providers/VercelAiGateway.tsxwebview-ui/src/components/settings/providers/ZooGateway.tsxwebview-ui/src/components/settings/providers/__tests__/ZooGateway.spec.tsxwebview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.tswebview-ui/src/components/ui/hooks/useSelectedModel.tswebview-ui/src/i18n/locales/en/settings.json
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
webview-ui/src/components/settings/CustomModelInfoSettings.tsx (1)
69-77: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winPreserve existing overrides while numeric input is invalid.
When an existing
contextWindowormaxTokensoverride is edited, an invalid intermediate value makesparsePositiveIntegerreturnundefined.updateOverridetreatsundefinedas deletion, sosetApiConfigurationFieldremoves the previous valid override. Preserve the configured value until the input is empty or valid, and keep the synchronization effects from replacing the invalid local text. Add a regression test with an existing numeric override.Also applies to: 97-107
🤖 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 `@webview-ui/src/components/settings/CustomModelInfoSettings.tsx` around lines 69 - 77, Update the contextWindow and maxTokens input/update logic in CustomModelInfoSettings so invalid non-empty numeric text does not pass undefined to updateOverride or remove an existing configured override; only empty input should delete it, while valid input should update it. Adjust the synchronization useEffects to retain invalid local text and only sync configured values when the input is empty or valid. Add a regression test covering both fields with existing numeric overrides.
🤖 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 `@webview-ui/src/i18n/locales/ca/settings.json`:
- Around line 613-635: Translate every value in the customModelInfo settings
object into the appropriate locale, replacing the English strings in
webview-ui/src/i18n/locales/ca/settings.json lines 613-635,
webview-ui/src/i18n/locales/de/settings.json lines 613-635,
webview-ui/src/i18n/locales/es/settings.json lines 613-635,
webview-ui/src/i18n/locales/fr/settings.json lines 613-635,
webview-ui/src/i18n/locales/hi/settings.json lines 613-635,
webview-ui/src/i18n/locales/id/settings.json lines 613-635,
webview-ui/src/i18n/locales/it/settings.json lines 613-635,
webview-ui/src/i18n/locales/ja/settings.json lines 613-635, and
webview-ui/src/i18n/locales/ko/settings.json lines 613-635. Preserve all keys
and JSON structure while translating the title, descriptions, labels, warning,
and reset text for each language.
In `@webview-ui/src/i18n/locales/nl/settings.json`:
- Around line 613-635: Translate every string in the providers.customModelInfo
block into the appropriate language in
webview-ui/src/i18n/locales/nl/settings.json#L613-L635,
pl/settings.json#L613-L635, pt-BR/settings.json#L613-L635,
ru/settings.json#L613-L635, tr/settings.json#L613-L635,
vi/settings.json#L613-L635, zh-CN/settings.json#L613-L635, and
zh-TW/settings.json#L640-L662, preserving the existing keys and JSON structure.
---
Outside diff comments:
In `@webview-ui/src/components/settings/CustomModelInfoSettings.tsx`:
- Around line 69-77: Update the contextWindow and maxTokens input/update logic
in CustomModelInfoSettings so invalid non-empty numeric text does not pass
undefined to updateOverride or remove an existing configured override; only
empty input should delete it, while valid input should update it. Adjust the
synchronization useEffects to retain invalid local text and only sync configured
values when the input is empty or valid. Add a regression test covering both
fields with existing numeric overrides.
🪄 Autofix
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 Plus
Run ID: 15982886-7401-44c7-9639-be9530e97c14
📒 Files selected for processing (30)
packages/types/src/__tests__/custom-model-info.test.tspackages/types/src/model.tssrc/api/providers/__tests__/kenari.spec.tssrc/api/providers/__tests__/openrouter.spec.tssrc/api/providers/__tests__/requesty.spec.tssrc/api/providers/__tests__/unbound.spec.tssrc/api/providers/__tests__/vercel-ai-gateway.spec.tssrc/api/providers/__tests__/zoo-gateway.spec.tswebview-ui/src/components/chat/TaskHeader.tsxwebview-ui/src/components/chat/__tests__/TaskHeader.spec.tsxwebview-ui/src/components/settings/CustomModelInfoSettings.tsxwebview-ui/src/components/settings/__tests__/CustomModelInfoSettings.spec.tsxwebview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.tswebview-ui/src/i18n/locales/ca/settings.jsonwebview-ui/src/i18n/locales/de/settings.jsonwebview-ui/src/i18n/locales/es/settings.jsonwebview-ui/src/i18n/locales/fr/settings.jsonwebview-ui/src/i18n/locales/hi/settings.jsonwebview-ui/src/i18n/locales/id/settings.jsonwebview-ui/src/i18n/locales/it/settings.jsonwebview-ui/src/i18n/locales/ja/settings.jsonwebview-ui/src/i18n/locales/ko/settings.jsonwebview-ui/src/i18n/locales/nl/settings.jsonwebview-ui/src/i18n/locales/pl/settings.jsonwebview-ui/src/i18n/locales/pt-BR/settings.jsonwebview-ui/src/i18n/locales/ru/settings.jsonwebview-ui/src/i18n/locales/tr/settings.jsonwebview-ui/src/i18n/locales/vi/settings.jsonwebview-ui/src/i18n/locales/zh-CN/settings.jsonwebview-ui/src/i18n/locales/zh-TW/settings.json
🚧 Files skipped from review as they are similar to previous changes (4)
- webview-ui/src/components/chat/tests/TaskHeader.spec.tsx
- webview-ui/src/components/chat/TaskHeader.tsx
- packages/types/src/model.ts
- packages/types/src/tests/custom-model-info.test.ts
| "customModelInfo": { | ||
| "title": "Custom model metadata", | ||
| "description": "Override context and capability metadata when the provider cannot detect your model accurately.", | ||
| "unresolved": "Model metadata is unavailable. Enter the context window to enable accurate token tracking.", | ||
| "contextWindow": { | ||
| "label": "Context window", | ||
| "description": "Total tokens the model can process, including input and output." | ||
| }, | ||
| "maxTokens": { | ||
| "label": "Max output tokens", | ||
| "description": "Maximum number of tokens the model can generate in one response." | ||
| }, | ||
| "supportsImages": { | ||
| "label": "Supports images", | ||
| "description": "Override whether the model accepts image content." | ||
| }, | ||
| "supportsPromptCache": { | ||
| "label": "Supports prompt caching", | ||
| "description": "Override whether prompt caching is supported." | ||
| }, | ||
| "maxTokensWarning": "Max output tokens exceed the context window.", | ||
| "reset": "Reset to detected values" | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Translate the new customModelInfo strings.
The new settings appear in English for users of these non-English locales. Add locale-specific translations before release.
webview-ui/src/i18n/locales/ca/settings.json#L613-L635: Translate the Catalan values.webview-ui/src/i18n/locales/de/settings.json#L613-L635: Translate the German values.webview-ui/src/i18n/locales/es/settings.json#L613-L635: Translate the Spanish values.webview-ui/src/i18n/locales/fr/settings.json#L613-L635: Translate the French values.webview-ui/src/i18n/locales/hi/settings.json#L613-L635: Translate the Hindi values.webview-ui/src/i18n/locales/id/settings.json#L613-L635: Translate the Indonesian values.webview-ui/src/i18n/locales/it/settings.json#L613-L635: Translate the Italian values.webview-ui/src/i18n/locales/ja/settings.json#L613-L635: Translate the Japanese values.webview-ui/src/i18n/locales/ko/settings.json#L613-L635: Translate the Korean values.
📍 Affects 9 files
webview-ui/src/i18n/locales/ca/settings.json#L613-L635(this comment)webview-ui/src/i18n/locales/de/settings.json#L613-L635webview-ui/src/i18n/locales/es/settings.json#L613-L635webview-ui/src/i18n/locales/fr/settings.json#L613-L635webview-ui/src/i18n/locales/hi/settings.json#L613-L635webview-ui/src/i18n/locales/id/settings.json#L613-L635webview-ui/src/i18n/locales/it/settings.json#L613-L635webview-ui/src/i18n/locales/ja/settings.json#L613-L635webview-ui/src/i18n/locales/ko/settings.json#L613-L635
🤖 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 `@webview-ui/src/i18n/locales/ca/settings.json` around lines 613 - 635,
Translate every value in the customModelInfo settings object into the
appropriate locale, replacing the English strings in
webview-ui/src/i18n/locales/ca/settings.json lines 613-635,
webview-ui/src/i18n/locales/de/settings.json lines 613-635,
webview-ui/src/i18n/locales/es/settings.json lines 613-635,
webview-ui/src/i18n/locales/fr/settings.json lines 613-635,
webview-ui/src/i18n/locales/hi/settings.json lines 613-635,
webview-ui/src/i18n/locales/id/settings.json lines 613-635,
webview-ui/src/i18n/locales/it/settings.json lines 613-635,
webview-ui/src/i18n/locales/ja/settings.json lines 613-635, and
webview-ui/src/i18n/locales/ko/settings.json lines 613-635. Preserve all keys
and JSON structure while translating the title, descriptions, labels, warning,
and reset text for each language.
| "customModelInfo": { | ||
| "title": "Custom model metadata", | ||
| "description": "Override context and capability metadata when the provider cannot detect your model accurately.", | ||
| "unresolved": "Model metadata is unavailable. Enter the context window to enable accurate token tracking.", | ||
| "contextWindow": { | ||
| "label": "Context window", | ||
| "description": "Total tokens the model can process, including input and output." | ||
| }, | ||
| "maxTokens": { | ||
| "label": "Max output tokens", | ||
| "description": "Maximum number of tokens the model can generate in one response." | ||
| }, | ||
| "supportsImages": { | ||
| "label": "Supports images", | ||
| "description": "Override whether the model accepts image content." | ||
| }, | ||
| "supportsPromptCache": { | ||
| "label": "Supports prompt caching", | ||
| "description": "Override whether prompt caching is supported." | ||
| }, | ||
| "maxTokensWarning": "Max output tokens exceed the context window.", | ||
| "reset": "Reset to detected values" | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Translate the new custom-model metadata strings in every affected locale.
The same English-only providers.customModelInfo block was added to eight non-English locale files. This leaves the new settings surface untranslated.
webview-ui/src/i18n/locales/nl/settings.json#L613-L635: Translate the block for Dutch.webview-ui/src/i18n/locales/pl/settings.json#L613-L635: Translate the block for Polish.webview-ui/src/i18n/locales/pt-BR/settings.json#L613-L635: Translate the block for Brazilian Portuguese.webview-ui/src/i18n/locales/ru/settings.json#L613-L635: Translate the block for Russian.webview-ui/src/i18n/locales/tr/settings.json#L613-L635: Translate the block for Turkish.webview-ui/src/i18n/locales/vi/settings.json#L613-L635: Translate the block for Vietnamese.webview-ui/src/i18n/locales/zh-CN/settings.json#L613-L635: Translate the block for Simplified Chinese.webview-ui/src/i18n/locales/zh-TW/settings.json#L640-L662: Translate the block for Traditional Chinese.
📍 Affects 8 files
webview-ui/src/i18n/locales/nl/settings.json#L613-L635(this comment)webview-ui/src/i18n/locales/pl/settings.json#L613-L635webview-ui/src/i18n/locales/pt-BR/settings.json#L613-L635webview-ui/src/i18n/locales/ru/settings.json#L613-L635webview-ui/src/i18n/locales/tr/settings.json#L613-L635webview-ui/src/i18n/locales/vi/settings.json#L613-L635webview-ui/src/i18n/locales/zh-CN/settings.json#L613-L635webview-ui/src/i18n/locales/zh-TW/settings.json#L640-L662
🤖 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 `@webview-ui/src/i18n/locales/nl/settings.json` around lines 613 - 635,
Translate every string in the providers.customModelInfo block into the
appropriate language in webview-ui/src/i18n/locales/nl/settings.json#L613-L635,
pl/settings.json#L613-L635, pt-BR/settings.json#L613-L635,
ru/settings.json#L613-L635, tr/settings.json#L613-L635,
vi/settings.json#L613-L635, zh-CN/settings.json#L613-L635, and
zh-TW/settings.json#L640-L662, preserving the existing keys and JSON structure.
Summary
Implementation details
customModelInfoschema and shared application helper in@roo-code/types.Testing
Notes
Summary by CodeRabbit