mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix: drop vertex search tools when functions are present
This commit is contained in:
parent
d3891e6eae
commit
92f84d35ab
2 changed files with 180 additions and 0 deletions
|
|
@ -536,6 +536,68 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
|||
|
||||
return googleSearch, googleSearchRetrieval, enterpriseWebSearch, urlContext
|
||||
|
||||
@staticmethod
|
||||
def _has_function_declarations_tool(tools: Optional[List[dict]]) -> bool:
|
||||
if not tools:
|
||||
return False
|
||||
|
||||
return any(
|
||||
bool(tool.get("function_declarations") or tool.get("functionDeclarations"))
|
||||
for tool in tools
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_search_tool(tool: dict) -> bool:
|
||||
search_tool_keys = {
|
||||
VertexToolName.GOOGLE_SEARCH.value,
|
||||
VertexToolName.GOOGLE_SEARCH_RETRIEVAL.value,
|
||||
VertexToolName.ENTERPRISE_WEB_SEARCH.value,
|
||||
VertexToolName.URL_CONTEXT.value,
|
||||
"google_search",
|
||||
"google_search_retrieval",
|
||||
"enterprise_web_search",
|
||||
"urlContext",
|
||||
}
|
||||
return any(key in tool for key in search_tool_keys)
|
||||
|
||||
def _drop_search_tools_if_mixed_with_functions(
|
||||
self, optional_params: dict
|
||||
) -> None:
|
||||
tools = optional_params.get("tools")
|
||||
if not isinstance(tools, list) or not self._has_function_declarations_tool(
|
||||
tools
|
||||
):
|
||||
return
|
||||
|
||||
server_side_tool_invocations = optional_params.get(
|
||||
"include_server_side_tool_invocations", False
|
||||
)
|
||||
if server_side_tool_invocations:
|
||||
return
|
||||
|
||||
filtered_tools = [
|
||||
tool for tool in tools if not self._is_search_tool(tool=tool)
|
||||
]
|
||||
if len(filtered_tools) == len(tools):
|
||||
return
|
||||
|
||||
verbose_logger.warning(
|
||||
"Vertex AI does not support mixing function declarations with "
|
||||
"search tools in the same request. Dropping search tools and "
|
||||
"keeping function declarations. To use search tools, send a "
|
||||
"request without function calling tools."
|
||||
)
|
||||
optional_params["tools"] = filtered_tools
|
||||
|
||||
def _add_tools_to_optional_params(self, optional_params: dict, tools: List) -> dict:
|
||||
optional_params = super()._add_tools_to_optional_params(
|
||||
optional_params=optional_params, tools=tools
|
||||
)
|
||||
self._drop_search_tools_if_mixed_with_functions(
|
||||
optional_params=optional_params
|
||||
)
|
||||
return optional_params
|
||||
|
||||
def _map_function( # noqa: PLR0915
|
||||
self, value: List[dict], optional_params: dict
|
||||
) -> List[Tools]:
|
||||
|
|
|
|||
|
|
@ -3905,6 +3905,124 @@ def test_vertex_ai_web_search_options_in_map_openai_params():
|
|||
), "web_search_options should be removed after transformation"
|
||||
|
||||
|
||||
def test_vertex_ai_web_search_options_after_function_tools_drops_search():
|
||||
"""
|
||||
web_search_options is mapped outside _map_function, so the conflict resolver
|
||||
must also run when tools are appended to optional_params.
|
||||
"""
|
||||
from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import (
|
||||
VertexGeminiConfig,
|
||||
)
|
||||
|
||||
v = VertexGeminiConfig()
|
||||
optional_params = {}
|
||||
|
||||
function_tools = v._map_function(
|
||||
value=[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "lookup_weather",
|
||||
"description": "Lookup weather",
|
||||
},
|
||||
}
|
||||
],
|
||||
optional_params=optional_params,
|
||||
)
|
||||
optional_params = v._add_tools_to_optional_params(
|
||||
optional_params=optional_params, tools=function_tools
|
||||
)
|
||||
|
||||
search_tool = v._map_web_search_options({})
|
||||
optional_params = v._add_tools_to_optional_params(
|
||||
optional_params=optional_params, tools=[search_tool]
|
||||
)
|
||||
|
||||
assert len(optional_params["tools"]) == 1
|
||||
assert "function_declarations" in optional_params["tools"][0]
|
||||
assert optional_params["tools"][0]["function_declarations"][0]["name"] == (
|
||||
"lookup_weather"
|
||||
)
|
||||
|
||||
|
||||
def test_vertex_ai_web_search_options_before_function_tools_drops_search():
|
||||
"""
|
||||
The same conflict can occur when web_search_options is processed before
|
||||
function tools, depending on request parameter order.
|
||||
"""
|
||||
from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import (
|
||||
VertexGeminiConfig,
|
||||
)
|
||||
|
||||
v = VertexGeminiConfig()
|
||||
optional_params = {}
|
||||
|
||||
search_tool = v._map_web_search_options({})
|
||||
optional_params = v._add_tools_to_optional_params(
|
||||
optional_params=optional_params, tools=[search_tool]
|
||||
)
|
||||
|
||||
function_tools = v._map_function(
|
||||
value=[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "lookup_weather",
|
||||
"description": "Lookup weather",
|
||||
},
|
||||
}
|
||||
],
|
||||
optional_params=optional_params,
|
||||
)
|
||||
optional_params = v._add_tools_to_optional_params(
|
||||
optional_params=optional_params, tools=function_tools
|
||||
)
|
||||
|
||||
assert len(optional_params["tools"]) == 1
|
||||
assert "function_declarations" in optional_params["tools"][0]
|
||||
assert optional_params["tools"][0]["function_declarations"][0]["name"] == (
|
||||
"lookup_weather"
|
||||
)
|
||||
|
||||
|
||||
def test_vertex_ai_web_search_options_with_function_tools_preserved_for_server_side_invocations():
|
||||
"""
|
||||
Gemini 3+ server-side tool invocation mode intentionally allows combining
|
||||
function declarations with search tools.
|
||||
"""
|
||||
from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import (
|
||||
VertexGeminiConfig,
|
||||
)
|
||||
|
||||
v = VertexGeminiConfig()
|
||||
optional_params = {"include_server_side_tool_invocations": True}
|
||||
|
||||
function_tools = v._map_function(
|
||||
value=[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "lookup_weather",
|
||||
"description": "Lookup weather",
|
||||
},
|
||||
}
|
||||
],
|
||||
optional_params=optional_params,
|
||||
)
|
||||
optional_params = v._add_tools_to_optional_params(
|
||||
optional_params=optional_params, tools=function_tools
|
||||
)
|
||||
|
||||
search_tool = v._map_web_search_options({})
|
||||
optional_params = v._add_tools_to_optional_params(
|
||||
optional_params=optional_params, tools=[search_tool]
|
||||
)
|
||||
|
||||
tool_keys = {key for tool in optional_params["tools"] for key in tool.keys()}
|
||||
assert "function_declarations" in tool_keys
|
||||
assert "googleSearch" in tool_keys
|
||||
|
||||
|
||||
def test_vertex_ai_service_tier_in_map_openai_params():
|
||||
"""Test that service_tier is correctly mapped to optional_params."""
|
||||
from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue