Skip to content

feat(ai): pick a model from what the key can use, instead of typing one - #459

Merged
parthrohit22 merged 1 commit into
devfrom
fix/ai-provider-model-discovery
Sep 13, 2026
Merged

parthrohit22 merged 1 commit into
devfrom
fix/ai-provider-model-discovery

Conversation

@parthrohit22

Copy link
Copy Markdown
Collaborator

Reported live: Gemini could not be connected, and neither could anything else.

Why it failed

gemini-1.5-flash was PARTHA's Gemini default. Google stopped offering the 1.5 models to projects created after they were retired, so a key made today gets:

AI provider rejected the request, most likely because of an unsupported model ID. Confirm the model ID and try again.

An error that names the problem and nothing that would fix it. There is no way to discover a valid ID from inside the product, so "confirm the model ID" is advice the user cannot act on.

Every provider default carried the same fault. A hardcoded model ID is a fact with a shelf life, and this one had expired.

The fix

Providers publish their own model lists, so none of this has to be guessed.

POST /ai/models asks the provider what this key can use and returns the list plus one recommendation — taken from that list, never invented. It shares config_for_test, so a key typed into the form and a key already saved both resolve the same way: the list is fetchable before anything is saved, which is exactly when it is needed.

Security surface is unchanged. Discovery is a GET through the same egress policy, the same allowlisted host and the same IP-pinned connection a completion already uses — /v1/models and /v1beta/models were already permitted under each provider's base path. SecureProviderHttpSender gained a method parameter and nothing else; POST still routes through post, so every existing sender (including the fakes in tests) is untouched.

Gemini lists embedding-only models alongside chat ones. Offering one would produce a failure a user could not diagnose, so anything without generateContent never reaches the list.

UI

Credentials now come before the model, because that is the order setup actually runs in — you cannot list models until you have the key. Fetch models turns the free-text box into a picker, and a model the provider no longer offers is replaced rather than left in place to be rejected again. The list is dropped when the provider changes, so one provider never offers another's models.

Defaults

Refreshed — but they are only a starting point now, not the only route to a working config.

Worth flagging: the Gemini default I first wrote was also wrong. I checked gemini-2.0-flash against a live catalogue before committing and it no longer exists either; the lowest current tier is gemini-2.5-flash. That is the second time a hand-picked default was stale within this one change, which is the whole argument for discovery.

provider default verified against a live catalogue
openai gpt-4.1-mini yes
anthropic claude-haiku-4-5-20251001 id shape per Anthropic docs; claude-haiku-4.5 present upstream
gemini gemini-2.5-flash yes
openrouter openai/gpt-4.1-mini yes
ollama llama3.2 local, unchanged

Verified

The parser ran against real provider data — OpenRouter's public catalogue, 445 models, parsed correctly, recommendation resolved to openai/gpt-4.1-mini. The Gemini endpoint answers 403 "needs a key" rather than 404, confirming host and path. I did not test with the user's own key and did not ask for it.

Tests

8 backend (each provider's list shape, Gemini's embedding filter, an empty list reported rather than returned, missing credential per provider, and that the recommendation is always drawn from the returned list) and 4 frontend (fetch and select, keep a still-offered choice, drop the list on provider change, surface a failure without leaving a stale list).

One existing test changed on purpose: a secret-scanning guard on /ai/providers flags any 24+ character token-shaped string, and a dated Anthropic model id matches that shape. Exempted by exact value against the registry rather than by loosening the pattern, which would have let a real leak through.

1187 backend / 468 frontend passing; ruff, mypy, tsc, eslint clean; API contract regenerated.

Connecting a provider failed at the model ID, and the product gave the user
no way out of it. `gemini-1.5-flash` was PARTHA's Gemini default and is not
offered at all to a Google AI Studio project created today, so a new key met
"AI provider rejected the request, most likely because of an unsupported
model ID" -- an error that names the problem and nothing that would fix it.
Every provider default carried the same fault, because a hardcoded model ID
is a fact with a shelf life.

Providers publish their own model lists, so nothing has to be guessed.
`POST /ai/models` asks the provider what this key can use and returns the
list, plus one recommendation taken from that list and never invented. It
shares `config_for_test`, so a key typed into the form and a key already
saved both work -- the list is fetchable before anything is saved, which is
when it is needed.

Discovery is a GET through the same egress policy, the same allowlisted host
and the same pinned connection a completion already uses; `/v1/models` and
`/v1beta/models` were permitted under each provider's base path already. The
sender gained a method parameter and nothing else. POST still routes through
`post`, so every existing sender -- including the fakes in tests -- is
untouched.

Gemini lists embedding-only models alongside chat ones; offering those would
produce a failure a user could not diagnose, so anything without
generateContent never reaches the list.

In the UI, credentials now come before the model, because that is the order
setup actually runs in: you cannot list models until you have the key. Fetch
models turns the free-text box into a picker, and a model the provider no
longer offers is replaced rather than left in place to be rejected again.

Defaults are refreshed too, but they are only a starting point now. The two
I checked against a live catalogue before committing: the Gemini default I
first wrote (gemini-2.0-flash) does not exist any more either, and the
lowest current tier is gemini-2.5-flash.
@vercel

vercel Bot commented Sep 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
partha-frontend Ready Ready Preview Sep 13, 2026 10:11am UTC

@parthrohit22
parthrohit22 merged commit ca3c5f9 into dev Sep 13, 2026
14 checks passed
@parthrohit22
parthrohit22 deleted the fix/ai-provider-model-discovery branch September 13, 2026 10:26
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