Adds Vertex AI as a first-class provider for Gemini models via Vertex's OpenAI-compatible endpoint. Vertex authenticates with short-lived OAuth2 access tokens (service-account JSON or ADC), not a static API key — the missing piece behind the recurring requests (#13484, #12639, #56259). - agent/vertex_adapter.py: OAuth2 token minting + refresh-on-expiry (5-min margin), ADC->service-account fallback, global vs regional endpoint URLs. Config precedence: env var > config.yaml > default. - plugins/model-providers/vertex/: provider profile (auth_type=vertex), reuses Gemini's extra_body.google.thinking_config translation. - runtime_provider: vertex short-circuit BEFORE the credential pool so a credentials-file path is never mistaken for a static API key; mints a fresh token + computes base_url per resolve. - run_agent + conversation_loop: _try_refresh_vertex_client_credentials() re-mints the token and rebuilds the client on a mid-session 401, so a long-lived gateway agent survives token expiry (~1h). - auxiliary_client: vertex auth_type branch for side-LLM tasks. - config.yaml: vertex.project_id / vertex.region (non-secret, bridged to env); credential path stays in .env (VERTEX_CREDENTIALS_PATH). - setup wizard + model picker: dedicated _model_flow_vertex; curated google/gemini-* model list; --provider choices. - pricing/metadata: Vertex prices off the gemini docs snapshot; endpoint host auto-maps to the vertex provider (no probe spam). - lazy_deps + pyproject [vertex] extra: google-auth, opt-in only. - docs: guides/google-vertex.md + providers page; tests for adapter + runtime resolution. Salvages and modernizes #8427 by @slawt onto current main: rewired from the legacy PROVIDER_REGISTRY path to the provider-profile architecture, moved non-secret config out of .env into config.yaml, and added the per-turn 401 token-refresh the original lacked.
75 lines
2.8 KiB
Python
75 lines
2.8 KiB
Python
"""Google Vertex AI provider profile.
|
|
|
|
vertex: Gemini models via Google Cloud's OpenAI-compatible endpoint.
|
|
|
|
Auth is OAuth2 — short-lived access tokens minted from a service-account JSON
|
|
or Application Default Credentials (ADC), NOT a static API key. Token
|
|
resolution and refresh live in ``agent/vertex_adapter.py``; runtime_provider.py
|
|
calls it to obtain a fresh ``(token, base_url)`` pair, then hands the token to
|
|
the standard OpenAI client as ``api_key``. Because the wire format is the
|
|
OpenAI-compatible chat/completions surface, no message translation is needed —
|
|
the only Gemini-specific concern is the ``thinking_config`` reasoning hook,
|
|
which is emitted here exactly as the ``gemini`` provider does for its
|
|
OpenAI-compat subpath (``extra_body.google.thinking_config``).
|
|
|
|
``auth_type="vertex"`` marks this as an OAuth-token provider (resolved
|
|
specially, like bedrock's ``aws_sdk``) so it is never treated as an
|
|
api_key provider that would mistake a credentials-file path for a key.
|
|
"""
|
|
|
|
from typing import Any
|
|
|
|
from providers import register_provider
|
|
from providers.base import ProviderProfile
|
|
|
|
|
|
class VertexProfile(ProviderProfile):
|
|
"""Vertex AI — reuse Gemini's thinking_config translation for extra_body."""
|
|
|
|
def build_extra_body(
|
|
self, *, session_id: str | None = None, **context: Any
|
|
) -> dict[str, Any]:
|
|
"""Emit ``extra_body.google.thinking_config`` for the OpenAI-compat
|
|
Vertex surface, mirroring the ``gemini`` provider's behavior.
|
|
"""
|
|
from agent.transports.chat_completions import (
|
|
_build_gemini_thinking_config,
|
|
_snake_case_gemini_thinking_config,
|
|
)
|
|
|
|
model = context.get("model") or ""
|
|
reasoning_config = context.get("reasoning_config")
|
|
|
|
raw_thinking_config = _build_gemini_thinking_config(model, reasoning_config)
|
|
if not raw_thinking_config:
|
|
return {}
|
|
|
|
thinking_config = _snake_case_gemini_thinking_config(raw_thinking_config)
|
|
if not thinking_config:
|
|
return {}
|
|
return {"extra_body": {"google": {"thinking_config": thinking_config}}}
|
|
|
|
def fetch_models(
|
|
self,
|
|
*,
|
|
api_key: str | None = None,
|
|
base_url: str | None = None,
|
|
timeout: float = 8.0,
|
|
) -> list[str] | None:
|
|
"""Vertex's OpenAI-compat endpoint has no ``/models`` listing route;
|
|
model discovery is not available. The setup wizard ships a curated list.
|
|
"""
|
|
return None
|
|
|
|
|
|
vertex = VertexProfile(
|
|
name="vertex",
|
|
aliases=("google-vertex", "vertex-ai", "gcp-vertex"),
|
|
api_mode="chat_completions",
|
|
env_vars=(), # OAuth2 via service account / ADC — not a static key env var
|
|
base_url="https://aiplatform.googleapis.com", # real base_url computed at runtime
|
|
auth_type="vertex",
|
|
default_aux_model="google/gemini-3-flash-preview",
|
|
)
|
|
|
|
register_provider(vertex)
|