feat: Make gemini accept the openai parameter parallel_tool_calls (#11125)

* feat: Make gemini accept the openai parameter parallel_tool_calls

When mapping, allow the parameter: True because that is the
intrinsic behavior of Gemini. Allow False, but reject if there
are multiple tools because there's no actual equivalent in Gemini.

fixes #9686

ref: issues/9686

* chore: cleanup and move test_vertex.py down to tests/litellm

as suggested in https://github.com/BerriAI/litellm/pull/11125#discussion_r2105905871
This commit is contained in:
Adam Holmberg
2025-05-26 07:51:27 -07:00
committed by GitHub
parent 77d2615185
commit c93a78cd6c
3 changed files with 79 additions and 10 deletions
@@ -83,6 +83,7 @@ class GoogleAIStudioGeminiConfig(VertexGeminiConfig):
"logprobs",
"frequency_penalty",
"modalities",
"parallel_tool_calls",
]
if supports_reasoning(model):
supported_params.append("reasoning_effort")
@@ -219,6 +219,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
"logprobs",
"top_logprobs",
"modalities",
"parallel_tool_calls",
]
if supports_reasoning(model):
supported_params.append("reasoning_effort")
@@ -463,6 +464,19 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
)
if _tool_choice_value is not None:
optional_params["tool_choice"] = _tool_choice_value
elif param == "parallel_tool_calls":
if value is False:
tools = non_default_params.get("tools", non_default_params.get("functions"))
num_function_declarations = len(tools) if isinstance(tools, list) else 0
if num_function_declarations > 1:
raise litellm.utils.UnsupportedParamsError(
message=(
"`parallel_tool_calls=False` is not supported when multiple tools are "
"provided for Gemini. Specify a single tool, or set "
"`parallel_tool_calls=True`."
),
status_code=400,
)
elif param == "seed":
optional_params["seed"] = value
elif param == "reasoning_effort" and isinstance(value, str):
@@ -12,8 +12,7 @@ import litellm.litellm_core_utils.prompt_templates
import litellm.litellm_core_utils.prompt_templates.factory
load_dotenv()
import io
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import MagicMock
sys.path.insert(
0, os.path.abspath("../..")
@@ -21,10 +20,8 @@ sys.path.insert(
import pytest
import litellm
from litellm import get_optional_params
from litellm.llms.custom_httpx.http_handler import HTTPHandler
from litellm.llms.vertex_ai.gemini.transformation import _process_gemini_image
from litellm.types.llms.vertex_ai import PartType, BlobType
import httpx
from litellm.types.llms.vertex_ai import BlobType
def encode_image_to_base64(image_path):
@@ -1146,7 +1143,7 @@ def test_process_gemini_image():
from litellm.llms.vertex_ai.gemini.transformation import (
_process_gemini_image,
)
from litellm.types.llms.vertex_ai import PartType, FileDataType, BlobType
from litellm.types.llms.vertex_ai import FileDataType
# Test GCS URI
gcs_result = _process_gemini_image("gs://bucket/image.png")
@@ -1271,10 +1268,6 @@ def test_vertex_embedding_url(model, expected_url):
import pytest
from unittest.mock import Mock, patch
from typing import Dict, Any
# Import your actual module here
# from your_module import _process_gemini_image, PartType, FileDataType, BlobType
# Add these fixtures below existing fixtures
@@ -1409,3 +1402,64 @@ def test_aaavertex_embeddings_distances(
text_embedding = text_response.data[0].embedding
def test_vertex_parallel_tool_calls_true():
"""
Test that parallel_tool_calls = True sets the correct tool_config.
"""
tools = [
{"type": "function", "function": {"name": "get_weather"}},
{"type": "function", "function": {"name": "get_time"}},
]
optional_params = get_optional_params(
model="gemini-1.5-pro",
custom_llm_provider="vertex_ai",
tools=tools,
parallel_tool_calls=True,
)
assert "tools" in optional_params
def test_vertex_parallel_tool_calls_false_multiple_tools_error():
"""
Test that parallel_tool_calls = False with multiple tools raises UnsupportedParamsError
when drop_params is False.
"""
tools = [
{"type": "function", "function": {"name": "get_weather"}},
{"type": "function", "function": {"name": "get_time"}},
]
with pytest.raises(litellm.utils.UnsupportedParamsError) as excinfo:
get_optional_params(
model="gemini-1.5-pro",
custom_llm_provider="vertex_ai",
tools=tools,
parallel_tool_calls=False,
)
assert "`parallel_tool_calls=False` is not supported when multiple tools are provided" in str(excinfo.value)
# works when specified as "functions"
with pytest.raises(litellm.utils.UnsupportedParamsError) as excinfo:
get_optional_params(
model="gemini-1.5-pro",
custom_llm_provider="vertex_ai",
functions=tools,
parallel_tool_calls=False,
)
assert "`parallel_tool_calls=False` is not supported when multiple tools are provided" in str(excinfo.value)
def test_vertex_parallel_tool_calls_false_single_tool():
"""
Test that parallel_tool_calls = False with a single tool does not raise an error
and does not add 'tool_config' if not otherwise specified.
"""
tools = [
{"type": "function", "function": {"name": "get_weather"}},
]
optional_params = get_optional_params(
model="gemini-1.5-pro",
custom_llm_provider="vertex_ai",
tools=tools,
parallel_tool_calls=False,
)
assert "tools" in optional_params