Skip to content

fix(providers): use native query-param auth for Gemini model discovery (#62259) - #62267

Closed
PRATHAMESH75 wants to merge 2 commits into
NousResearch:mainfrom
PRATHAMESH75:fix/gemini-native-model-discovery
Closed

PRATHAMESH75 wants to merge 2 commits into
NousResearch:mainfrom
PRATHAMESH75:fix/gemini-native-model-discovery

Conversation

@PRATHAMESH75

Copy link
Copy Markdown

What does this PR do?

Fixes live model discovery for the Gemini provider. hermes model / the /model picker showed only the 4 hard-coded fallback models instead of the 50+ the Google API actually exposes.

ProviderProfile.fetch_models() hard-codes OpenAI-style auth: Authorization: Bearer <key> against {base_url}/models. Gemini's base_url is the native endpoint https://generativelanguage.googleapis.com/v1beta, which rejects Bearer auth with HTTP 401 — it requires the key as a ?key= query param. So the probe 401'd, fetch_models() returned None, and the picker silently fell back to the static list.

GeminiProfile now overrides fetch_models() to hit {base_url}/models?key=<key> — the same query-param auth the native inference client already uses — and strips the models/ prefix each returned name carries, so live discovery matches what inference expects. This is Approach A from the issue (native endpoint), chosen because it reuses the native auth model already in place rather than adding a second endpoint path.

Related Issue

Fixes #62259

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • plugins/model-providers/gemini/__init__.py — add GeminiProfile.fetch_models() override: native {base_url}/models?key=<key> query-param auth, parse the {"models": [{"name": "models/..."}]} shape, strip the models/ prefix. Returns None on no key / no base_url / any error so callers keep the static fallback.
  • tests/providers/test_fetch_models_base_url.py — regression tests: a fake native handler that 401s on Bearer and honours ?key=, asserting the prefix is stripped and IDs returned; plus a no-api-key → None case.

How to Test

scripts/run_tests.sh tests/providers/test_fetch_models_base_url.py

Result: 8 tests passed, 0 failed (2 new + 6 existing). The new test_native_query_param_auth_strips_prefix fails against the old code path (Bearer → 401 → None) and passes with the fix.

Real-endpoint proof from the issue:

# Old path — native endpoint + Bearer:
curl -s -o /dev/null -w "%{http_code}\n" \
  "https://generativelanguage.googleapis.com/v1beta/models" \
  -H "Authorization: Bearer $GOOGLE_API_KEY"     # -> 401

# New path — native endpoint + query-param auth:
curl -s "https://generativelanguage.googleapis.com/v1beta/models?key=$GOOGLE_API_KEY" | grep -c '"name"'   # -> 50

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass (ran the affected suite via scripts/run_tests.sh)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15 (Darwin 25.5)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A (behavior fix; docstring added on the override)
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — pure stdlib urllib, no platform-specific code
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Credits

Root-cause analysis and the chosen Approach A (native /v1beta endpoint with ?key= query-param auth) come from @Olegever's report in #62259.

@alt-glitch alt-glitch added type/bug Something isn't working comp/plugins Plugin system and bundled plugins provider/gemini Google Gemini (AI Studio, Cloud Code) P3 Low — cosmetic, nice to have duplicate This issue or pull request already exists labels Jul 10, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Duplicate of #42693 (earliest open PR, filed 2026-06-09). Both override GeminiProfile.fetch_models() in plugins/model-providers/gemini/__init__.py with the same native ?key= query-param auth mechanism (parse name, strip the models/ prefix). #42693 is the broader/canonical version — it also restores the gemma-4 static fallback and thinking config. This PR's fetch_models change is fully covered by it. Fixes the same underlying discovery bug filed as #62259. Cross-linking so a maintainer can pick the canonical one.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for tracing the native Gemini discovery path. The current-main premise is valid: providers/base.py:199-214 uses Bearer auth and OpenAI-style data[].id parsing, while the Gemini profile has no override.

Problems

  • plugins/model-providers/gemini/__init__.py:42-65 applies native ?key= auth and native models[].name parsing to every supplied base URL. Main explicitly supports Gemini's /openai compatibility base URL in agent/transports/chat_completions.py:92-98; retain the base implementation for that branch and add a regression test.
  • plugins/model-providers/gemini/__init__.py:50,68 places the API key in the URL and logs the raw caught exception. Do not log an exception value that may contain that URL; log a safe exception type/category instead.

Suggested changes

  • Gate the override to native Gemini endpoints and delegate /openai discovery to ProviderProfile.fetch_models().
  • Replace raw exception logging with non-secret-safe diagnostics and cover the compatibility branch.

Automated hermes-sweeper review.

the native inference client already uses) and strip the ``models/``
prefix each entry's ``name`` carries so IDs match what inference expects.
"""
effective_base = (base_url or self.base_url or "").rstrip("/")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This override also receives a configured Gemini /openai base URL, which main explicitly treats as OpenAI-compatible. Do not apply native ?key= auth and models[].name parsing to that branch; gate this path to native endpoints and delegate the compatibility endpoint to super().fetch_models(...).

]
return ids or None
except Exception as exc:
logger.debug("fetch_models(gemini): %s", exc)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The request URL above contains the API key. Avoid logging the raw exception because URL-bearing exception text can expose that key in debug logs; log a safe category such as type(exc).__name__ instead.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 11, 2026
@PRATHAMESH75
PRATHAMESH75 force-pushed the fix/gemini-native-model-discovery branch from 514bc92 to 258c715 Compare July 11, 2026 20:00
@PRATHAMESH75

Copy link
Copy Markdown
Author

Addressed both review problems and rebased onto current main (now mergeable):

  1. Gate to native endpoints — the OpenAI-compat /openai base URL now delegates to ProviderProfile.fetch_models() (Bearer + data[].id) instead of forcing native ?key= auth.
  2. No key leak in logs — the failure path now logs type(exc).__name__ only, since urllib errors embed the request URL which carries the api_key in the ?key= query param.

Added regression tests for the compat-branch delegation and for the failure path not leaking the key.

The native Gemini /v1beta endpoint rejects Bearer auth with HTTP 401, so
ProviderProfile.fetch_models()'s OpenAI-style Authorization header returned
None and the /model picker silently fell back to the 4 static fallback
models instead of the 50+ the account can actually call.

Override fetch_models() in GeminiProfile to hit {base_url}/models?key=<key>
(the same query-param auth the native inference client already uses) and
strip the 'models/' prefix each returned name carries, so live discovery
matches inference.

Fixes NousResearch#62259
…act errors

Address review: delegate the OpenAI-compat /openai base URL to
ProviderProfile.fetch_models (it speaks Bearer + data[].id) instead of
forcing native query-param auth, and stop logging the caught exception
value — urllib errors embed the request URL, which carries the api_key in
the ?key= query param. Log the exception type only. Add regression tests
for the compat-branch delegation and the no-key-leak failure path.
@PRATHAMESH75
PRATHAMESH75 force-pushed the fix/gemini-native-model-discovery branch from 28f395a to be83c1e Compare September 10, 2026 17:10
teknium1 added a commit that referenced this pull request Sep 24, 2026
…ng static xfails

#120319, #120374 and #120299 are on main, so their probes and every Gap
naming them go (the module's own rule); those cells are plain tests now.

The two static strict xfails with an open fix PR (#95375 cli
resize_scrollback, fix #120321; #62259 Gemini listing, fix #62267/#116509)
turned main red the moment the fix merged (XPASS). They now go through
_pending_fixes.known_failure: a run-time xfail only while the cell fails
with that gap's own message (a turn rendered more than once; an empty live
listing), any other failure stays red, and the fix just makes it pass.
teknium1 added a commit that referenced this pull request Sep 24, 2026
…ng static xfails

#120319, #120374 and #120299 are on main, so their probes and every Gap
naming them go (the module's own rule); those cells are plain tests now.

The two static strict xfails with an open fix PR (#95375 cli
resize_scrollback, fix #120321; #62259 Gemini listing, fix #62267/#116509)
turned main red the moment the fix merged (XPASS). They now go through
_pending_fixes.known_failure: a run-time xfail only while the cell fails
with that gap's own message (a turn rendered more than once; an empty live
listing), any other failure stays red, and the fix just makes it pass.
@teknium1 teknium1 closed this in b47b787 Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have provider/gemini Google Gemini (AI Studio, Cloud Code) sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gemini live model discovery fails — native /v1beta endpoint rejects Bearer auth (falls back to 4 static models)

3 participants