Verified model defaults and rejected-model fallback - #632
Conversation
- ChatGPT device OAuth verifies the model with a real completion before writing it into routing, walking the provider's default candidates when the requested id is rejected - runtime completion path falls back to siblings from the provider's default routing table when a configured model id is rejected and no explicit fallback chain exists; the terminal error names the rejected ids and asks the user to pick a model and report it - openai-chatgpt defaults move to gpt-5.6-sol with a gpt-5.6/gpt-5.5 chain - provider default models served from GET /providers/default-models; the divergent frontend table and CHATGPT_OAUTH_DEFAULT_MODEL constant are gone - routing editor gains an All Models select that fills every slot at once
WalkthroughThe change moves provider model defaults into backend routing data. It adds OAuth model verification and fallback selection. The interface fetches these defaults, supports applying one model to text roles, and accepts optional OAuth model input. ChangesProvider Model Routing
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/api/providers.rs (1)
412-431: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winDo not persist an unverified model.
When every verification attempt fails, Line 413 sets
applied_modelto the rejected requested model. Line 431 then overwrites the default and default-agent routing with that model. Keep the existing routing, or fail finalization before the config write, when no candidate verifies.🤖 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.rs` around lines 412 - 431, Update the no-verified-model branch that assigns applied_model so it does not select the rejected requested model; preserve the existing routing or abort finalization before apply_model_routing and the config write when all verification attempts fail. Keep successful verified-model behavior unchanged.
🧹 Nitpick comments (1)
interface/src/components/agent-config/ConfigSectionEditor.tsx (1)
229-229: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse a descriptive callback parameter name.
Rename
vtoslotValueorvalue. This is a local readability refactor with no behavior change.As per coding guidelines, don't abbreviate variable names. Use
queuenotqandmessagenotmsg.Proposed rename
- const uniformModel = textSlotValues.every((v) => v === textSlotValues[0]) + const uniformModel = textSlotValues.every((slotValue) => slotValue === textSlotValues[0])🤖 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 `@interface/src/components/agent-config/ConfigSectionEditor.tsx` at line 229, Rename the callback parameter in the uniformModel calculation to a descriptive name such as slotValue or value, while leaving the every comparison and behavior unchanged.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 `@interface/src/routes/Settings.tsx`:
- Around line 633-646: The provider edit action must not initialize modelInput
from an unloaded defaultModels result. Update the edit control around the onEdit
handler and defaultModels usage to disable editing until provider defaults are
available, or add an effect that updates the open editor when defaultModels
loads; preserve the current routing-model precedence for configured providers.
---
Outside diff comments:
In `@src/api/providers.rs`:
- Around line 412-431: Update the no-verified-model branch that assigns
applied_model so it does not select the rejected requested model; preserve the
existing routing or abort finalization before apply_model_routing and the config
write when all verification attempts fail. Keep successful verified-model
behavior unchanged.
---
Nitpick comments:
In `@interface/src/components/agent-config/ConfigSectionEditor.tsx`:
- Line 229: Rename the callback parameter in the uniformModel calculation to a
descriptive name such as slotValue or value, while leaving the every comparison
and behavior unchanged.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1b66549e-81ea-49b9-b5f8-804e1b22e450
📒 Files selected for processing (10)
interface/src/api/client.tsinterface/src/components/agent-config/ConfigSectionEditor.tsxinterface/src/components/settings/constants.tsinterface/src/components/settings/index.tsinterface/src/routes/Settings.tsxsrc/api/portal.rssrc/api/providers.rssrc/api/server.rssrc/llm/model.rssrc/llm/routing.rs
💤 Files with no reviewable changes (1)
- interface/src/components/settings/constants.ts
OAuth finalization now fails instead of writing the rejected model when no candidate verifies. Routing keeps whatever it had; credentials stay saved so the sign-in doesn't need repeating. The candidate walk moved into resolve_verified_model, which takes the verifier as a parameter so the requested/fallback/unverified paths are unit tested. Settings disables the provider edit button until the default-models query resolves, so the model field can't open seeded with an empty string.
Setting up a fresh instance via ChatGPT device OAuth wrote
openai-chatgpt/gpt-5.3-codex— a frontend constant that was never verified — into all five routing slots. The model isn't served for OAuth accounts, so every call failed until the user hand-edited five slots. Three layers of fix here so this class of bug can't strand an instance again.OAuth flow verifies before writing.
finalize_openai_oauthruns a real completion against the requested model, and on rejection walks the provider's default candidates and applies the first one that responds. If nothing verifies, finalization fails before touchingconfig.toml— existing routing is left alone and the status reports the failure with every id it tried. Credentials still get saved so the sign-in doesn't have to be repeated. Themodelfield on the start request is now optional — the backend owns the default.Runtime fallback for rejected model ids. Rejected ids (stale default, typo, no access) previously bailed with no recourse because fallback chains are keyed by exact model string. Now, when there's no explicit chain:
If every candidate is also rejected, the error names the tried ids, calls the built-in defaults stale, and points at Settings → Model Routing.
One source of truth for defaults. New
GET /providers/default-modelsserves per-provider defaults fromdefaults_for_provider; the frontend's second table insettings/constants.ts(which disagreed on anthropic, openrouter, and gemini) is deleted, along withCHATGPT_OAUTH_DEFAULT_MODEL.openai-chatgptdefaults move togpt-5.6-solwith agpt-5.6→gpt-5.5chain, verified working on a real ChatGPT OAuth account.Settings won't open the provider editor until
GET /providers/default-modelsresolves — the model field seeds from that response, so opening early left it blank with no way to recover short of typing an id by hand.Also: the Model Routing editor gets an "All Models" select that fills channel/branch/worker/compactor/cortex in one action (voice excluded), for when you want to switch everything to one model without clicking through five dropdowns.
Testing
Unit tests for
is_model_not_found_error(OpenAI, Anthropic, and gateway phrasings, plus non-matches like plain 404s and rate limits) anddefault_model_candidates(provider-prefixed, deduped, empty for unknown providers).The OAuth candidate walk takes its verifier as a parameter, so the four outcomes are covered without network:
Requested model accepted (no extra candidates tried), requested rejected → default fallback applied, nothing accepted →
Unverifiedwith one failure per attempt, and a requested id that's already a default isn't tried twice.