fix(models): stop silently defaulting to Gemini outside Google-OAuth deployments
From the PR description
Summary
Removes the hardcoded gemini-3-flash-preview default. Deployments with Sign-in-with-Google enabled keep it; everywhere else, models resolve from what the signed-in user can actually use, and requests fail with an actionable message when nothing is configured.
What changed
Backend
- New
lib/googleOauth.ts: detects the Supabase external Google provider (SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_ID/_SECRET) and exposeslegacyDefaultModel(tier)- the historical Gemini constants, retained only for Google-OAuth deployments. getUserModelSettings: title/tabular fallbacks become availability-based chains (first-party keys by tier → saved router selections). When nothing is usable they returnnullfor non-Google-OAuth deployments; OAuth deployments keep today's Gemini defaults.- Chat streaming: requests without a model use saved router selections first, then first-party keys, and fail loudly ("No AI provider is configured...") instead of running an unchosen model. Explicit-but-unknown model ids now also fail loudly instead of silently degrading to the default.
- Title generation and tabular generation entry points skip or return a clear 409 when no provider is usable.
- Profile payloads expose nullable
titleModel/tabularModelpluscomposerDefaultModelso clients preselect the deployment's intended default without hardcoding it.
Frontend / add-in
- Composer renders an explicit "Select model" state instead of assuming Gemini, resets stale selections atomically, and preselects the first usable candidate - server default first, then saved router selections, then first-party models filtered by real key availability.
- Word add-in catalog mirrors the change;
REACT_APP_DEFAULT_MODELremains available as an operator override. - Tabular views skip key checks while no model is selected.
.env.exampledocuments that setting both Google vars retains the legacy default.
Why
A stale composer selection (e.g. after a catalog rename) or an omitted model field silently ran Gemini - surfacing only as an opaque stream failure (in one observed deployment, a monthly spend-cap 400) that users could not connect to their own selection. Model choice should come from configuration and user keys, not a compile-time constant.
Testing performed
backend: full vitest suite green on Node 22 (727 passed), including new coverage: availability-based fallbacks, null-when-nothing-usable outside Google-OAuth deployments, retained Gemini defaults under the Google flag, and loud rejection of unknown model ids.frontend: full vitest suite green (548 passed; three pre-existingwordAddin/*load failures are identical on pristinemainin containerized runs and unrelated to this change),tscclean, eslint clean.word-addin:tscclean.- Manually exercised both modes against a local stack: with no Google env, a request omitting
modelstreams from the user's saved OpenRouter selection; with the Google vars set, the legacy Gemini default is restored.
Our analysis
Remove Gemini as the implicit model default — read the full analysis →
Think the analysis missed something the PR description covers?
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-378.md from
inside the repo you want the changes in.