feat(llm): add first-class Gemini provider - #119
Conversation
Call GeminiProvider.listModels route via /v1beta/openai/models when the Cloud pane or /model first activates a gemini provider, so its model section resolves instead of staying on the loading placeholder.
Ooooze
left a comment
There was a problem hiding this comment.
Code review — medium findings only
Architecture (thin GeminiProvider over OpenAi + /v1beta/openai) looks right. Blocking on two medium TUI gaps before merge.
| Severity | Where | Issue |
|---|---|---|
| Medium | llm-panel-row-builders.ts inlineModelsForProvider (not updated in this PR) |
Gemini warms fetchGeminiModels cache, but the Cloud pane consumer still only reads inlineModels + getCachedOpenAiCompatModelsForBaseUrl. No baseUrl on gemini entries → secondary cache miss; if inlineModels is null/for another provider, list degrades (and empty chatModel can fall through to OPENAI_COMPAT_DEFAULT_CHAT_MODEL / gpt-5.4-mini). Add a getCachedGeminiModels() branch symmetric with openai-compat. |
| Medium | providers-wizard.tsx CompatChatModelStep |
Gemini lands on free-text chat_model_line with no live /v1beta/openai/models fetch (isCompat gates openai-compatible only). Operator must guess the model id at setup; Cloud pane only warms later. Wire fetchGeminiModels here (or a short curated pick list + free-text escape). |
Low/nits left out of this review (GOOGLE_API_KEY fallback, parallel-tools default vs #104, trailing newlines, KIND_ROW_ORDER comment).
| const models = await fetchOpenAiCompatModels(baseUrl, apiKey); | ||
| const models = | ||
| provider.kind === "gemini" | ||
| ? await fetchGeminiModels(apiKey) |
There was a problem hiding this comment.
Medium: this correctly warms fetchGeminiModels, but the Cloud-pane renderer in src/tui/llm-panel/llm-panel-row-builders.ts → inlineModelsForProvider was not updated.
That helper only falls back to getCachedOpenAiCompatModelsForBaseUrl(provider.baseUrl). Gemini entries have no baseUrl, so when providersPanel.inlineModels is null or belongs to another provider the secondary lookup always misses — and an empty chatModel can surface OPENAI_COMPAT_DEFAULT_CHAT_MODEL (gpt-5.4-mini).
Please add a gemini branch that reads getCachedGeminiModels() (same pattern as openai-compat’s module-cache fallback), and prefer GEMINI_DEFAULT_CHAT_MODEL over the openai-compat placeholder when kind === "gemini".
There was a problem hiding this comment.
Fixed in 6e6daf1. inlineModelsForProvider now has a gemini branch: it reads the gemini-keyed cache via a new getCachedGeminiModelsForPanel() (key-agnostic read mirroring getCachedOpenAiCompatModelsForBaseUrl) and falls back to GEMINI_DEFAULT_CHAT_MODEL — never the openai-compat placeholder. Added a regression test asserting an empty chatModel can't surface gpt-5.4-mini.
| value: w.chatModelLine, | ||
| placeholder: OPENAI_COMPAT_DEFAULT_CHAT_MODEL, | ||
| placeholder: | ||
| w.kind === "gemini" |
There was a problem hiding this comment.
Medium: placeholder for Gemini is good, but this step still has no live model list for kind === "gemini".
CompatChatModelStep only fetches when isCompat (openai-compatible). Gemini skips base_url and lands here as free-text, so onboarding forces the operator to know a model id; fetchGeminiModels only warms later in the Cloud pane.
Please either:
- also call
fetchGeminiModels(apiKey)whenw.kind === "gemini"and surface picks like the compat path, or - ship a short curated Gemini pick list + free-text escape.
There was a problem hiding this comment.
Fixed in 6e6daf1. CompatChatModelStep now calls fetchGeminiModels(apiKey) for kind === "gemini" and surfaces the live /v1beta/openai/models list as a pick list, matching the openai-compatible path (listCompatChatModelPicks gained a gemini branch).
Address review findings on the Gemini provider: - llm-panel-row-builders: add a gemini branch to inlineModelsForProvider. Gemini entries carry no baseUrl, so the openai-compat URL-keyed cache could never hit and an empty chatModel fell through to the openai-compat placeholder (gpt-5.4-mini). Read the gemini-keyed cache via the new getCachedGeminiModelsForPanel() and fall back to GEMINI_DEFAULT_CHAT_MODEL. - providers-wizard: fetch the live /v1beta/openai/models list for kind === "gemini" in CompatChatModelStep and surface it as a pick list, matching the openai-compatible path. listCompatChatModelPicks gains a gemini branch. - fetch-gemini-models: add getCachedGeminiModelsForPanel(), a key-agnostic read for UI surfaces mirroring getCachedOpenAiCompatModelsForBaseUrl.
|
Both medium findings addressed in |
Resolve conflicts with the native Gemini provider (AtomicBot-ai#119): keep both provider kinds, share OpenAI-compat API key resolution, and preserve apiPathPrefix alongside tagged-tool streaming adapt. Co-authored-by: Cursor <[email protected]>
Problem
Atomic's generic OpenAI-compatible provider appends
/v1/..., while Google's documented OpenAI-compatible API root is/v1beta/openai. That made direct Gemini setup require a translating proxy.Change
geminiprovider backed by Atomic's existing OpenAI transport./v1beta/openai/...paths.GEMINI_API_KEYwithout including the key in logs or errors.Scope
This PR fixes native provider setup and endpoint routing only. Gemini SSE reconstruction, parallel-tool-call control, and structured retry delays remain tracked by #103, #104, and #106 respectively.
Testing
npm run lintnpm run buildgit diff --checkCloses #108
Implementation and test preparation were LLM-assisted, then independently reviewed by Yabloko Labs.