Skip to content

feat(llm): add first-class Gemini provider - #119

Merged
Ooooze merged 3 commits into
AtomicBot-ai:mainfrom
yablokolabs:feat/native-gemini-provider
Aug 13, 2026
Merged

feat(llm): add first-class Gemini provider#119
Ooooze merged 3 commits into
AtomicBot-ai:mainfrom
yablokolabs:feat/native-gemini-provider

Conversation

@yablokolabs

Copy link
Copy Markdown
Contributor

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

  • Add a first-class gemini provider backed by Atomic's existing OpenAI transport.
  • Route chat completions and model discovery through Google's documented /v1beta/openai/... paths.
  • Resolve Gemini credentials from GEMINI_API_KEY without including the key in logs or errors.
  • Add Gemini to the provider setup flow with a stable default chat model and no manual base-URL step.
  • Preserve the existing OpenAI-compatible, OpenRouter, and AIMLAPI paths unchanged.

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 lint
  • Post-review Gemini export/configure regressions: 5 passed, 0 failed
  • Affected provider/config/TUI suite: 389 passed, 0 failed
  • npm run build
  • Full suite: 3,953 passed, 6 failed, 4 skipped; no new failure names were introduced versus the captured upstream baseline (one baseline failure did not reproduce)
  • git diff --check
  • Public diff credential/private-path audit

Closes #108

Implementation and test preparation were LLM-assisted, then independently reviewed by Yabloko Labs.

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

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)

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.

Medium: this correctly warms fetchGeminiModels, but the Cloud-pane renderer in src/tui/llm-panel/llm-panel-row-builders.tsinlineModelsForProvider 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".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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"

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.

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:

  1. also call fetchGeminiModels(apiKey) when w.kind === "gemini" and surface picks like the compat path, or
  2. ship a short curated Gemini pick list + free-text escape.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.
@yablokolabs

Copy link
Copy Markdown
Contributor Author

Both medium findings addressed in 6e6daf1 (pushed). Cloud pane gemini branch + wizard live fetch, with regression tests. tsc clean; affected suites 55/55 green. (Full-suite run shows pre-existing failures in memory/http/import areas on the base commit — unrelated to this PR.) Ready for re-review.

@Ooooze
Ooooze merged commit 26f8081 into AtomicBot-ai:main Aug 13, 2026
Ooooze added a commit to yablokolabs/atomic-agent that referenced this pull request Aug 13, 2026
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]>
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.

Add a first-class Gemini provider for Google's OpenAI-compatible endpoint

2 participants