mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
Merge a686f36f0c into 74050e03c5
This commit is contained in:
commit
5523a98bc3
2 changed files with 95 additions and 12 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue