Skip to content

Verified model defaults and rejected-model fallback - #632

Merged
jamiepine merged 3 commits into
mainfrom
jamiepine/model-default-fallbacks
Aug 10, 2026
Merged

Verified model defaults and rejected-model fallback#632
jamiepine merged 3 commits into
mainfrom
jamiepine/model-default-fallbacks

Conversation

@jamiepine

@jamiepine jamiepine commented Aug 10, 2026

Copy link
Copy Markdown
Member

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_oauth runs 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 touching config.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. The model field 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 fallbacks.is_empty() && routing::is_model_not_found_error(&error.to_string()) {
    fallbacks = routing::default_model_candidates(&self.provider)
        .into_iter()
        .filter(|candidate| candidate != &self.full_model_name)
        .collect();
}

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-models serves per-provider defaults from defaults_for_provider; the frontend's second table in settings/constants.ts (which disagreed on anthropic, openrouter, and gemini) is deleted, along with CHATGPT_OAUTH_DEFAULT_MODEL. openai-chatgpt defaults move to gpt-5.6-sol with a gpt-5.6gpt-5.5 chain, verified working on a real ChatGPT OAuth account.

Settings won't open the provider editor until GET /providers/default-models resolves — 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) and default_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:

async fn resolve_verified_model<Verify, Fut>(requested: &str, verify: Verify) -> ModelVerification

Requested model accepted (no extra candidates tried), requested rejected → default fallback applied, nothing accepted → Unverified with one failure per attempt, and a requested id that's already a default isn't tried twice.

- 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
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The 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.

Changes

Provider Model Routing

Layer / File(s) Summary
Routing defaults and fallback recovery
src/llm/routing.rs, src/llm/model.rs, src/api/portal.rs
Provider-prefixed candidates now support rejection detection, fallback-chain deduplication, Ollama defaults, and recovery when a primary model is unavailable. Portal defaults use the routing configuration.
OAuth verification and provider defaults
src/api/providers.rs, src/api/server.rs
OAuth model selection verifies requested and fallback candidates, persists only verified models, and returns completion status. The API exposes provider defaults.
Frontend default model integration
interface/src/api/client.ts, interface/src/components/agent-config/ConfigSectionEditor.tsx, interface/src/components/settings/*, interface/src/routes/Settings.tsx
The frontend fetches backend defaults, applies one model to text roles, updates provider editing states, and sends optional OAuth model parameters.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary changes: verifying model defaults and adding fallback handling for rejected models.
Description check ✅ Passed The description directly explains the OAuth verification, runtime fallback, centralized defaults, routing editor change, and test coverage.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jamiepine/model-default-fallbacks

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jamiepine
jamiepine marked this pull request as ready for review August 10, 2026 01:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Do not persist an unverified model.

When every verification attempt fails, Line 413 sets applied_model to 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 value

Use a descriptive callback parameter name.

Rename v to slotValue or value. This is a local readability refactor with no behavior change.

As per coding guidelines, don't abbreviate variable names. Use queue not q and message not msg.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 04e048e and 3ab25d1.

📒 Files selected for processing (10)
  • interface/src/api/client.ts
  • interface/src/components/agent-config/ConfigSectionEditor.tsx
  • interface/src/components/settings/constants.ts
  • interface/src/components/settings/index.ts
  • interface/src/routes/Settings.tsx
  • src/api/portal.rs
  • src/api/providers.rs
  • src/api/server.rs
  • src/llm/model.rs
  • src/llm/routing.rs
💤 Files with no reviewable changes (1)
  • interface/src/components/settings/constants.ts

Comment thread interface/src/routes/Settings.tsx
jamiepine and others added 2 commits August 9, 2026 21:49
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.
@jamiepine
jamiepine merged commit 0cca2d2 into main Aug 10, 2026
5 checks passed
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.

1 participant