fix(vertex_ai): strip a trailing slash from a custom Gemini api_base

A custom `api_base` for `gemini/*` was joined straight into the request path,
so the trailing slash that copying it out of Google's docs leaves behind
produced `.../openai//models/...`. Google answers that with a 404, which made
the model unreachable through the SDK.

The other two branches of `_check_custom_proxy` already strip it; the Gemini
branch was the one that did not.

Fixes #43828
This commit is contained in:
Cabeza 2026-10-13 15:57:35 +08:00
parent 15154a40a8
commit 7a2e23cc92
2 changed files with 44 additions and 1 deletions

View file

@ -667,7 +667,9 @@ class VertexBase:
# For Gemini (Google AI Studio), construct the full path like other providers
if model is None:
raise ValueError("Model parameter is required for Gemini custom API base URLs")
url = f"{api_base}/models/{model}:{endpoint}"
# A trailing slash on api_base is a common copy-paste from the docs; without
# stripping it the joined path carries `//`, which Google answers with a 404.
url = f"{api_base.rstrip('/')}/models/{model}:{endpoint}"
if gemini_api_key is None:
raise ValueError(
"Missing Gemini API key. Set the GEMINI_API_KEY or GOOGLE_API_KEY environment variable."

View file

@ -875,6 +875,47 @@ class TestVertexBase:
result_url == expected_url
), f"Expected {expected_url}, got {result_url} for model {model}"
def test_check_custom_proxy_gemini_url_strips_trailing_slash(self):
"""A trailing slash on api_base must not double up in the joined path.
Copy-pasting the api_base from Google's docs leaves the slash in place,
and the resulting `//` is answered with a 404. #43828
"""
vertex_base = VertexBase()
_, url = vertex_base._check_custom_proxy(
api_base="https://generativelanguage.googleapis.com/v1beta/openai/",
custom_llm_provider="gemini",
gemini_api_key="test-api-key",
endpoint="generateContent",
stream=False,
auth_header=None,
url="https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent",
model="gemini-3.8-flash",
)
assert url == (
"https://generativelanguage.googleapis.com/v1beta/openai/models/gemini-3.8-flash:generateContent"
)
assert "//models" not in url
def test_check_custom_proxy_gemini_url_without_trailing_slash_is_unchanged(self):
"""The slash-free form keeps producing the same URL it always did."""
vertex_base = VertexBase()
_, url = vertex_base._check_custom_proxy(
api_base="https://proxy.example.com/v1beta",
custom_llm_provider="gemini",
gemini_api_key="test-api-key",
endpoint="generateContent",
stream=False,
auth_header=None,
url="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent",
model="gemini-2.5-pro",
)
assert url == "https://proxy.example.com/v1beta/models/gemini-2.5-pro:generateContent"
def test_check_custom_proxy_streaming_parameter(self):
"""Test that streaming parameter correctly adds ?alt=sse to URLs"""
vertex_base = VertexBase()