d364473620
vertex, bedrock and copilot-acp each overrode ProviderProfile.fetch_models with an identical `return None`, and the overrides could not simply be deleted because the base implementation derives a URL from base_url and would GET e.g. bedrock-runtime.../models or acp://copilot/models. ProviderProfile now carries `supports_model_listing` (default True); the base fetch_models returns None before touching the network when it is False, and the three SDK/subprocess-backed profiles set it in their constructors instead of overriding. Separately, two catalog GETs still went through bare urllib.request.urlopen: commandcode's fetch_models and hermes_cli.models._fetch_ai_gateway_models (which sends the AI Gateway bearer). Both now use the redirect-safe open_credentialed_url path (_urlopen_model_catalog_request in models.py, also applied to the unauthenticated fetch_ai_gateway_models for consistency). This is a security behavior change: a cross-origin redirect from either endpoint no longer forwards the Authorization/attribution headers to the redirect target. The AI Gateway tests that patched the global urlopen are repointed at hermes_cli.models._urlopen_model_catalog_request (the seam tests/hermes_cli/test_models.py already uses), the commandcode test patches open_credentialed_url on the plugin module, and two invariant tests pin the no-network guard and the bearer routing.
35 lines
1.7 KiB
Python
35 lines
1.7 KiB
Python
"""Google Vertex AI provider profile: Gemini via Google Cloud's OpenAI-compatible endpoint.
|
|
|
|
Auth is OAuth2 (service-account JSON or ADC), not a static key: ``agent/vertex_adapter.py``
|
|
mints ``(token, base_url)`` and the token is passed as ``api_key``. ``auth_type="vertex"``
|
|
keeps it out of the api_key provider path so a credentials-file path is never mistaken 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`` like the ``gemini`` provider's OpenAI-compat subpath."""
|
|
from agent.transports.chat_completions import _build_gemini_thinking_config, _snake_case_gemini_thinking_config
|
|
|
|
raw = _build_gemini_thinking_config(context.get("model") or "", context.get("reasoning_config"))
|
|
thinking_config = _snake_case_gemini_thinking_config(raw) if raw else None
|
|
return {"extra_body": {"google": {"thinking_config": thinking_config}}} if thinking_config else {}
|
|
|
|
|
|
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.6-flash",
|
|
supports_model_listing=False, # no /models route on the OpenAI-compat endpoint; setup ships a curated list
|
|
)
|
|
|
|
register_provider(vertex)
|