fix(vertex_ai): use aiplatform.{geo}.rep.googleapis.com for multi-region locations

Vertex multi-region endpoints (e.g. us, eu) use the rep host pattern, not
{geo}-aiplatform.googleapis.com. Regional IDs still contain a hyphen.

common_utils.get_vertex_base_url centralizes the rule for SDK/API URL building.
Proxy pass-through duplicates the same branching in a local get_vertex_base_url
(with trailing slashes) to avoid importing from common_utils there; live
WebSocket passthrough uses the same multi-region host logic for wss://.

Tests cover us/eu for the common_utils helper.

Made-with: Cursor
This commit is contained in:
Milan 2026-04-17 17:38:09 +03:00
parent c81342e3c2
commit 3bc8338cdf
No known key found for this signature in database
4 changed files with 47 additions and 7 deletions

View file

@ -229,6 +229,10 @@ def get_vertex_base_url(
) -> str:
"""
Get the base URL for Vertex AI API calls.
- ``global`` uses the global control plane host.
- Multi-region geographies (e.g. ``us``, ``eu``) use ``aiplatform.{geo}.rep.googleapis.com``.
- Regional locations (e.g. ``us-central1``) use ``{region}-aiplatform.googleapis.com``.
"""
if vertex_location == "global":
return "https://aiplatform.googleapis.com"
@ -236,6 +240,8 @@ def get_vertex_base_url(
raise ValueError("vertex_location is required")
if not re.match(r"^[a-z][a-z0-9-]*$", vertex_location):
raise ValueError("Invalid vertex_location format")
if "-" not in vertex_location:
return f"https://aiplatform.{vertex_location}.rep.googleapis.com"
return f"https://{vertex_location}-aiplatform.googleapis.com"

View file

@ -1497,7 +1497,9 @@ class VertexAIPassThroughHandler(BaseVertexAIPassThroughHandler):
def get_vertex_base_url(vertex_location: Optional[str]) -> str:
"""
Returns the base URL for Vertex AI based on the provided location.
Base URL for Vertex AI pass-through (trailing slash for URL joining).
Keep location rules aligned with ``litellm.llms.vertex_ai.common_utils.get_vertex_base_url``.
"""
if vertex_location == "global":
return "https://aiplatform.googleapis.com/"
@ -1505,6 +1507,8 @@ def get_vertex_base_url(vertex_location: Optional[str]) -> str:
raise ValueError("vertex_location is required")
if not re.match(r"^[a-z][a-z0-9-]*$", vertex_location):
raise ValueError("Invalid vertex_location format")
if "-" not in vertex_location:
return f"https://aiplatform.{vertex_location}.rep.googleapis.com/"
return f"https://{vertex_location}-aiplatform.googleapis.com/"
@ -1708,7 +1712,8 @@ async def _base_vertex_proxy_route(
Base function for Vertex AI passthrough routes.
Handles common logic for all Vertex AI services.
Default base_target_url is `https://{vertex_location}-aiplatform.googleapis.com/`
Default base_target_url is derived from ``get_vertex_base_url`` in this module
(regional, ``global``, or multi-region ``.rep.`` hosts), with a trailing slash.
Args:
endpoint: The endpoint path
@ -2280,11 +2285,7 @@ async def vertex_ai_live_websocket_passthrough(
return
host_location = resolved_location or vertex_llm_base.get_default_vertex_location()
host = (
"aiplatform.googleapis.com"
if host_location == "global"
else f"{host_location}-aiplatform.googleapis.com"
)
host = get_vertex_base_url(host_location).removeprefix("https://").rstrip("/")
service_url = (
f"wss://{host}/ws/google.cloud.aiplatform.v1.LlmBidiService/BidiGenerateContent"
)

View file

@ -5,6 +5,7 @@ This test suite ensures that all Vertex AI endpoints properly handle the 'global
which uses a different URL format than regional endpoints.
Regional: https://{region}-aiplatform.googleapis.com/...
Multi-region: https://aiplatform.{geo}.rep.googleapis.com/...
Global: https://aiplatform.googleapis.com/...
"""
@ -30,6 +31,8 @@ class TestVertexBaseURL:
("europe-west1", "https://europe-west1-aiplatform.googleapis.com"),
("asia-northeast1", "https://asia-northeast1-aiplatform.googleapis.com"),
("global", "https://aiplatform.googleapis.com"),
("us", "https://aiplatform.us.rep.googleapis.com"),
("eu", "https://aiplatform.eu.rep.googleapis.com"),
],
)
def test_get_vertex_base_url(self, vertex_location, expected_base_url):

View file

@ -21,6 +21,7 @@ from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
bedrock_llm_proxy_route,
create_pass_through_route,
cursor_proxy_route,
get_vertex_base_url,
llm_passthrough_factory_proxy_route,
milvus_proxy_route,
openai_proxy_route,
@ -31,6 +32,35 @@ from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
from litellm.types.passthrough_endpoints.vertex_ai import VertexPassThroughCredentials
class TestVertexPassthroughGetVertexBaseUrl:
"""Module-local get_vertex_base_url (trailing slash); rules match common_utils."""
@pytest.mark.parametrize(
"vertex_location, expected",
[
("global", "https://aiplatform.googleapis.com/"),
("us-central1", "https://us-central1-aiplatform.googleapis.com/"),
("us", "https://aiplatform.us.rep.googleapis.com/"),
("eu", "https://aiplatform.eu.rep.googleapis.com/"),
],
)
def test_returns_base_with_trailing_slash(self, vertex_location, expected):
assert get_vertex_base_url(vertex_location) == expected
@pytest.mark.parametrize(
"vertex_location, expected_host",
[
("global", "aiplatform.googleapis.com"),
("us-central1", "us-central1-aiplatform.googleapis.com"),
("us", "aiplatform.us.rep.googleapis.com"),
("eu", "aiplatform.eu.rep.googleapis.com"),
],
)
def test_websocket_host_strips_scheme(self, vertex_location, expected_host):
host = get_vertex_base_url(vertex_location).removeprefix("https://").rstrip("/")
assert host == expected_host
class TestBaseOpenAIPassThroughHandler:
def test_join_url_paths(self):
print("\nTesting _join_url_paths method...")