Skip to content

feat(vertex): add native Gemini support to GeminiNativeClient (mirrors Anthropic Vertex pattern) - #21480

Closed
TAMdrew wants to merge 1 commit into
NousResearch:mainfrom
TAMdrew:feat/vertex-gemini-native-aux
Closed

feat(vertex): add native Gemini support to GeminiNativeClient (mirrors Anthropic Vertex pattern)#21480
TAMdrew wants to merge 1 commit into
NousResearch:mainfrom
TAMdrew:feat/vertex-gemini-native-aux

Conversation

@TAMdrew

@TAMdrew TAMdrew commented May 7, 2026

Copy link
Copy Markdown

What does this PR do?

Adds Vertex AI support to the existing GeminiNativeClient so users with custom_providers configured against the publisher endpoint stop hitting silent 404s on every auxiliary call.

This is the symmetric companion to agent/anthropic_adapter.py, which already handles Claude-on-Vertex (:rawPredict / :streamRawPredict with GCE bearer auth). Same approach, applied to Gemini.

The bug this fixes

When you configure a custom_provider pointing at the native Vertex publisher endpoint:

custom_providers:
  - name: vertex-gemini-3-flash
    base_url: https://aiplatform.googleapis.com/v1/projects/<P>/locations/global/publishers/google/models/gemini-3-flash-preview:generateContent
    auth: gce_metadata          # GCE bearer token from metadata server

…the OpenAI SDK takes over (because the URL doesn't match any existing adapter predicate), appends /chat/completions to a URL that already ends in :generateContent, and the request silently 404s. The main agent path happens to use a different code branch and still works, but every auxiliary call breaks:

  • /compress → "Compression summary failed: Error code: 404"
  • Session title generation → no titles, just timestamps
  • vision_analyze on Gemini → "Generation failed"
  • Curator runs → empty results
  • web_extract / browser_automation → silent failures

The user-visible symptom is "everything mostly works but the side features are broken", which is hard to root-cause without reading the auxiliary client routing code.

Fix

Three small changes that mirror the existing Anthropic Vertex pattern:

agent/gemini_native_adapter.py (+138)

  • is_native_gemini_base_url() now recognises aiplatform.googleapis.com/.../publishers/google URLs (it previously only matched generativelanguage.googleapis.com)
  • New is_vertex_gemini_base_url() helper for code that needs to branch on Vertex specifically
  • _strip_vertex_model_suffix() removes /models/<NAME>:generateContent (or :streamGenerateContent) at adapter init — the OpenAI SDK treats base_url as a prefix and would otherwise produce malformed paths
  • _fetch_gce_metadata_token() — minimal GCE metadata-server client, copy of the pattern in anthropic_adapter
  • GeminiNativeClient.__init__ records ._is_vertex and strips the suffix; ._headers() injects Authorization: Bearer <token> on Vertex and drops any inherited x-goog-api-key (which would otherwise trigger GCP's 400 Multiple authentication credentials received)

agent/auxiliary_client.py (+27 −0)

  • resolve_provider_client._wrap_if_needed() gains a Gemini-native gate that runs first — before the Codex/Anthropic/etc rewrap checks — because Vertex Gemini URLs match no other adapter's predicate but always need GeminiNativeClient

run_agent.py (+95 −14)

  • Main agent's client construction routes Vertex Gemini base URLs through GeminiNativeClient (mirrors the auxiliary path so all routes are consistent)

Tests

tests/agent/test_gemini_vertex_native.py (new, 11 tests, all passing):

  • URL recognition — vertex publisher URLs match, GenLang still matches, unrelated URLs (api.openai.com, vertex Anthropic) don't
  • Suffix stripping — idempotent on already-clean base, handles :generateContent and :streamGenerateContent
  • Auth headers — Vertex injects bearer + drops x-goog-api-key; GenLang keeps x-goog-api-key + no bearer
  • End-to-endresolve_provider_client(provider='custom', explicit_base_url=<vertex>) returns a GeminiNativeClient (the regression that would cause silent 404s without the gate)

The 2 pre-existing failures in tests/run_agent/test_concurrent_interrupt.py (AttributeError: '_Stub' object has no attribute '_tool_guardrails') reproduce identically on pristine main without this patch — they're unrelated to this change.

Manual validation

I've been running this patch on my live Hermes runtime since 2026-05-05 against a real GCP project. Auxiliary title generation correctly routes to the Vertex URL with no 404 follow-up:

agent.auxiliary_client: Auxiliary title_generation: using custom (gemini-3.1-flash-lite-preview)
  at https://aiplatform.googleapis.com/v1/projects/<P>/locations/global/publishers/google

/compress, vision_analyze, curator, and web_extract all work without 404s on Gemini-on-Vertex endpoints.

Type of Change

  • Bug fix (non-breaking change that fixes broken behaviour for custom_providers pointed at the native Vertex publisher endpoint)
  • New feature (Vertex AI Gemini support — but via the existing custom_providers path, no new top-level provider added)

Related Issues / PRs

Backwards compatibility

  • is_native_gemini_base_url() retains its existing behaviour for generativelanguage.googleapis.com URLs (test asserts this)
  • GeminiNativeClient.__init__ for non-Vertex URLs is unchanged (_is_vertex=False branch is a no-op)
  • _wrap_if_needed() — the new Vertex Gemini gate runs first but only fires when is_native_gemini_base_url() matches, so non-Gemini URLs flow through the original adapter chain unchanged
  • No new configuration keys required — uses the existing custom_providers mechanism
  • No new dependencies (GCE metadata client uses stdlib urllib)

Checklist

  • Code follows the project's style (matched existing agent/anthropic_adapter.py patterns for Vertex GCE auth)
  • Added regression tests (tests/agent/test_gemini_vertex_native.py, 11 tests)
  • All new tests pass; no regressions in adjacent test files
  • Validated manually against a real GCP project for ~3 days before opening this PR
  • No new dependencies added
  • No breaking changes to existing config or behaviour

…s Anthropic Vertex pattern)

Hermes already routes Claude-on-Vertex through agent/anthropic_adapter.py
(":rawPredict" / ":streamRawPredict" with GCE bearer auth). This patch
adds the symmetric path for Gemini-on-Vertex: when a custom_provider is
pointed at the publisher endpoint

  https://aiplatform.googleapis.com/v1/projects/<P>/locations/global/
      publishers/google/models/<MODEL>:generateContent

GeminiNativeClient is now selected (instead of the OpenAI SDK) and uses a
GCE metadata bearer token instead of x-goog-api-key.

Without this gate the OpenAI SDK takes over, appends /chat/completions to
a URL ending in :generateContent, and every auxiliary request (compress,
title, vision, curator, web_extract, browser_automation) silently 404s —
the user-visible symptom is broken /compress, missing session titles, and
empty curator runs even though the main agent still succeeds (the main
agent uses a different code path that wasn't affected).

Changes
-------
agent/gemini_native_adapter.py (+138)
  - is_native_gemini_base_url() now accepts aiplatform.googleapis.com URLs
    that contain /publishers/google
  - new is_vertex_gemini_base_url() helper for code that needs to branch
  - _strip_vertex_model_suffix() removes /models/<NAME>:generateContent
    (or :streamGenerateContent) at adapter init — the OpenAI SDK treats
    base_url as a prefix and would otherwise produce malformed paths
  - _fetch_gce_metadata_token() — minimal GCE metadata-server client,
    same pattern as anthropic_adapter
  - GeminiNativeClient.__init__ records ._is_vertex and strips the
    suffix; ._headers() injects "Authorization: Bearer <token>" on
    Vertex and drops any inherited x-goog-api-key (which would trigger
    GCP's "Multiple authentication credentials received" 400)

agent/auxiliary_client.py (+27 −0)
  - resolve_provider_client._wrap_if_needed() gains a Gemini-native gate
    that runs FIRST — before the Codex/Anthropic/etc rewrap checks —
    because Vertex Gemini URLs match no other adapter's predicate but
    always need GeminiNativeClient

run_agent.py (+95 −14)
  - main agent's client construction routes Vertex Gemini base URLs
    through GeminiNativeClient (mirrors the auxiliary path)

Tests
-----
tests/agent/test_gemini_vertex_native.py (new, 11 tests)
  - URL recognition: vertex publisher URLs match, GenLang still matches,
    unrelated URLs (api.openai.com, vertex Anthropic) don't
  - Suffix stripping: idempotent, handles :generateContent and
    :streamGenerateContent
  - Auth headers: Vertex injects bearer + drops x-goog-api-key;
    GenLang keeps x-goog-api-key + no bearer
  - End-to-end: resolve_provider_client(provider='custom', explicit_base_url=
    <vertex>) returns a GeminiNativeClient (the regression that would
    cause silent 404s without the gate)

All 11 new tests pass; no regressions in existing
test_gemini_native_adapter.py / test_gemini_*.py / test_run_agent
suite (the 2 pre-existing failures in test_concurrent_interrupt.py
fail identically on pristine main without this patch).

Validated against my live Hermes runtime since 2026-05-05; auxiliary
title generation correctly routes to the Vertex URL and aux compress /
vision / curator all work without 404s.

Refs Issue NousResearch#13484, NousResearch#12639. Complementary to PRs NousResearch#8427 / NousResearch#16010 (which
add a first-class "vertex" provider using the OpenAI-compatible
endpoint) — this PR keeps the existing custom_providers path working
when pointed at the native :generateContent endpoint, which gives
better multimodal + native tool support than the compat shim.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/gemini Google Gemini (AI Studio, Cloud Code) labels May 7, 2026
@joofsh

joofsh commented Jun 19, 2026

Copy link
Copy Markdown

@TAMdrew anything left to do on this PR that we can help with? My company is eager to get hermes working with gemini+vertex

@teknium1

teknium1 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Superseded by #56363, which adds Google Vertex AI as a first-class Gemini provider on current main (OAuth2 via service-account JSON / ADC on the OpenAI-compatible endpoint, per-turn token refresh, setup/model/desktop pickers, docs + tests).

That PR salvaged the earliest submission in this cluster (#8427 by @slawt) and preserved its authorship. Closing this as a duplicate — thanks for working on the same gap; the merged implementation covers the Gemini-via-Vertex use case. If you spot something it misses, please open a focused follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/gemini Google Gemini (AI Studio, Cloud Code) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants