This commit is contained in:
maya-lukas 2026-08-27 19:58:10 -05:00 • committed by GitHub
commit 5523a98bc3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 95 additions and 12 deletions

View file

@ -2,6 +2,7 @@
## httpx client for vertex ai calls
## Initial implementation - covers gemini + image gen calls
import json
import re
import time
from collections.abc import Callable, Mapping, Sequence
from copy import deepcopy
@ -108,6 +109,17 @@ else:
StreamingChoices = Any
GEMINI_MAJOR_VERSION_PATTERN: Final[re.Pattern[str]] = re.compile(r"gemini-(\d+)")
GEMINI_ROLLING_LATEST_ALIASES: Final[frozenset[str]] = frozenset(
{
"gemini-flash-latest",
"gemini-flash-lite-latest",
"gemini-pro-latest",
}
)
class VertexAIBaseConfig:
def get_mapped_special_auth_params(self) -> dict:
"""
@ -259,20 +271,18 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
@staticmethod
def _is_gemini_3_or_newer(model: str) -> bool:
"""
Check if the model is Gemini 3 Pro or newer.
Check if the model is Gemini 3 or newer, e.g. gemini-3-pro-preview,
gemini-3.1-flash, gemini-3.5-flash, and every later major version.
Gemini 3 models include:
- gemini-3-pro-preview
- gemini-3-flash
- gemini-3-flash-preview (Gemini 3 Flash)
- gemini-3.1-pro-preview, gemini-3.1-flash, gemini-3.1-flash-lite-preview
- gemini-3.5-flash
- Any future Gemini 3.x models
The major version is read off the model name, so Gemini 4 and beyond
satisfy this without a code change. Names carrying no version at all are
matched against GEMINI_ROLLING_LATEST_ALIASES, which always resolve to
the newest release of their tier.
"""
# Check for Gemini 3 models
if "gemini-3" in model:
return True
return False
major_version: Final = GEMINI_MAJOR_VERSION_PATTERN.search(model)
if major_version is not None:
return int(major_version.group(1)) >= 3
return model.split("/")[-1] in GEMINI_ROLLING_LATEST_ALIASES
@staticmethod
def _forward_gemini_function_call_id(model: str) -> bool:

View file

@ -2431,6 +2431,79 @@ def test_is_gemini_3_or_newer():
# Edge cases
assert VertexGeminiConfig._is_gemini_3_or_newer("") == False
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini-flash-latest") == True
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini-flash-lite-latest") == True
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini-pro-latest") == True
assert (
VertexGeminiConfig._is_gemini_3_or_newer("gemini/gemini-flash-latest") == True
)
assert (
VertexGeminiConfig._is_gemini_3_or_newer("vertex_ai/gemini-pro-latest") == True
)
assert (
VertexGeminiConfig._is_gemini_3_or_newer(
"gemini-2.5-flash-native-audio-latest"
)
== False
)
# Later major versions must satisfy this without a code change
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini-4-flash") == True
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini-4.2-pro-preview") == True
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini/gemini-10-flash") == True
# Unversioned names that are not rolling aliases stay excluded
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini-embedding-001") == False
assert VertexGeminiConfig._is_gemini_3_or_newer("gemini-exp-1206") == False
def test_thought_signature_fallback_for_rolling_latest_alias():
"""
A tool call whose thought signature did not survive the round-trip must
still get the dummy signature Google documents, otherwise Gemini rejects
the follow-up turn with:
400 Function call is missing a thought_signature in functionCall parts.
Regression test: `gemini-flash-latest` serves Gemini 3.x but was not
detected as such, so the fallback was skipped and every multi-turn tool
call that lost its signature failed.
"""
from litellm.litellm_core_utils.prompt_templates.factory import (
convert_to_gemini_tool_call_invoke,
)
message = {
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_abc123", # no `__thought__` suffix -> signature lost
"type": "function",
"function": {
"name": "search_website",
"arguments": '{"query": "Iceland"}',
},
}
],
}
for model in [
"gemini-3-flash",
"gemini-flash-latest",
"gemini-flash-lite-latest",
"gemini-pro-latest",
"gemini/gemini-flash-latest",
]:
parts = convert_to_gemini_tool_call_invoke(message, model=model)
assert any(
"thoughtSignature" in part for part in parts
), f"expected a thought signature for {model}"
parts = convert_to_gemini_tool_call_invoke(message, model="gemini-2.5-flash")
assert not any("thoughtSignature" in part for part in parts)
def _tool_call_messages(tool_call_id: str):
return [