fix(passthrough): name Google failures from the URL on untracked methods too

A Gemini or Vertex pass-through failure on a method the success handler does
not track (countTokens, for one) still logged model "" and no provider,
although the model sits in the URL. The failure payload now falls back to the
Google AI host for the provider and reads /models/<name> from the URL, so
countTokens 400s name their model and provider like generateContent ones do.
Non-Google hosts keep the tracked-route rule and still log nothing extra.
This commit is contained in:
mateo-berri 2026-09-05 03:19:27 -07:00
parent 60ae16db8c
commit 571927705a
3 changed files with 43 additions and 2 deletions

View file

@ -35,6 +35,8 @@ from litellm.types.utils import (
)
vertex_search_api_config: Final = VertexSearchAPIVectorStoreConfig()
GEMINI_API_HOST_SUFFIX: Final = "generativelanguage.googleapis.com"
VERTEX_API_HOST_SUFFIX: Final = "aiplatform.googleapis.com"
if TYPE_CHECKING:
from litellm.types.utils import LiteLLMBatch
@ -560,10 +562,15 @@ class VertexPassthroughLoggingHandler:
return "openapi"
return None
@staticmethod
def is_google_ai_host(url: str) -> bool:
hostname: Final = urlparse(url).hostname
return hostname is not None and hostname.endswith((GEMINI_API_HOST_SUFFIX, VERTEX_API_HOST_SUFFIX))
@staticmethod
def custom_llm_provider_from_url(url: str) -> str:
parsed_url: Final = urlparse(url)
if parsed_url.hostname and parsed_url.hostname.endswith("generativelanguage.googleapis.com"):
if parsed_url.hostname and parsed_url.hostname.endswith(GEMINI_API_HOST_SUFFIX):
return litellm.LlmProviders.GEMINI.value
return litellm.LlmProviders.VERTEX_AI.value

View file

@ -735,7 +735,9 @@ def _carry_guardrail_logging_info(request_data: dict, guardrail_data: dict | Non
def _passthrough_google_provider(url_route: str, custom_llm_provider: str | None) -> str | None:
if pass_through_endpoint_logging.is_gemini_route(url_route, custom_llm_provider):
return "gemini"
if pass_through_endpoint_logging.is_vertex_route(url_route):
if pass_through_endpoint_logging.is_vertex_route(url_route) or VertexPassthroughLoggingHandler.is_google_ai_host(
url_route
):
return VertexPassthroughLoggingHandler.custom_llm_provider_from_url(url_route)
return None

View file

@ -5897,6 +5897,8 @@ def _upstream_400_as_event_stream(target: str) -> httpx.Response:
(_GEMINI_FLASH_TARGET, "gemini", True, "gemini"),
(_VERTEX_FLASH_TARGET, None, False, "vertex_ai"),
(_VERTEX_FLASH_TARGET, None, True, "vertex_ai"),
(_GEMINI_FLASH_TARGET.replace(":generateContent", ":countTokens"), "gemini", False, "gemini"),
(_VERTEX_FLASH_TARGET.replace(":generateContent", ":countTokens"), None, False, "vertex_ai"),
],
)
async def test_passthrough_upstream_error_logs_model_from_url(
@ -5986,6 +5988,36 @@ async def test_passthrough_mid_stream_drop_logs_model_from_url(
"",
"vertex_ai",
),
(
{"contents": []},
_GEMINI_FLASH_TARGET.replace(":generateContent", ":countTokens"),
"gemini",
"gemini-3.8-flash",
"gemini",
),
(
{"contents": []},
_VERTEX_FLASH_TARGET.replace(":generateContent", ":countTokens"),
None,
"gemini-3.8-flash",
"vertex_ai",
),
(
{"contents": []},
"https://aiplatform.googleapis.com/v1/projects/p/locations/global"
"/publishers/google/models/gemini-3.8-flash:countTokens",
None,
"gemini-3.8-flash",
"vertex_ai",
),
(
{},
"https://us-central1-aiplatform.googleapis.com/v1/projects/p/locations/us-central1/cachedContents",
None,
"",
"vertex_ai",
),
({"contents": []}, "https://api.example.com/v1/models/gpt-x:countTokens", None, "", None),
({"contents": []}, "https://api.example.com/v1/models/gpt-x:thing", None, "", None),
(None, None, None, "", None),
],