fix(google_genai): read functionDeclarations.parameters in the generateContent bridge

The generate_content -> chat/completions bridge only read
functionDeclarations[].parametersJsonSchema, the field gemini-cli sends.
The google-genai SDKs serialize tool schemas to `parameters`, so those
requests reached the upstream provider with a function that had a name
and description but no arguments, and the model invented its own.

Fall back to `parameters` when `parametersJsonSchema` is absent, and
declare it on _GenAIFunctionDeclaration.
This commit is contained in:
vovanphuc 2026-08-20 09:30:16 +07:00
parent a0f367fcd1
commit 7871a9d2a3
2 changed files with 85 additions and 0 deletions

View file

@ -47,6 +47,7 @@ class _GenAIPart(TypedDict, total=False):
class _GenAIFunctionDeclaration(TypedDict, total=False):
name: ReadOnly[str]
description: ReadOnly[str]
parameters: ReadOnly[dict[str, object]]
parametersJsonSchema: ReadOnly[dict[str, object]]
@ -359,6 +360,8 @@ class GoogleGenAIAdapter:
function_chunk["description"] = func_decl["description"]
if "parametersJsonSchema" in func_decl:
function_chunk["parameters"] = func_decl["parametersJsonSchema"]
elif "parameters" in func_decl:
function_chunk["parameters"] = func_decl["parameters"]
openai_tool: dict[str, object] = {"type": "function", "function": function_chunk}
openai_tools.append(openai_tool)

View file

@ -1276,3 +1276,85 @@ def test_inline_data_backward_compatibility_text_only():
content, str
), "Content should be a string for text-only messages (backward compatibility)"
assert content == "Hello, how are you?"
def test_tools_transformation_parameters_field():
"""FunctionDeclaration.parameters must survive the generate_content -> completion bridge.
The google-genai SDKs serialize tool schemas to `parameters`; only gemini-cli
sends `parametersJsonSchema`. Reading just the latter silently dropped the
schema, so the upstream model received a function with no arguments.
"""
from litellm.google_genai.adapters.transformation import GoogleGenAIAdapter
adapter = GoogleGenAIAdapter()
schema = {
"type": "OBJECT",
"properties": {"city": {"type": "STRING", "description": "City name"}},
"required": ["city"],
}
tools = [
{
"functionDeclarations": [
{
"name": "get_weather",
"description": "Get the weather for a city",
"parameters": schema,
}
]
}
]
completion_request = adapter.translate_generate_content_to_completion(
model="gpt-3.5-turbo",
contents={"role": "user", "parts": [{"text": "Weather in Hanoi?"}]},
tools=tools,
)
function = completion_request["tools"][0]["function"]
assert function["name"] == "get_weather"
assert function["parameters"] == {
"type": "object",
"properties": {"city": {"type": "string", "description": "City name"}},
"required": ["city"],
}
def test_tools_transformation_prefers_parameters_json_schema():
"""When both fields are present, parametersJsonSchema wins."""
from litellm.google_genai.adapters.transformation import GoogleGenAIAdapter
adapter = GoogleGenAIAdapter()
tools = [
{
"functionDeclarations": [
{
"name": "get_weather",
"parameters": {
"type": "OBJECT",
"properties": {"ignored": {"type": "STRING"}},
},
"parametersJsonSchema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
]
}
]
completion_request = adapter.translate_generate_content_to_completion(
model="gpt-3.5-turbo",
contents={"role": "user", "parts": [{"text": "Weather in Hanoi?"}]},
tools=tools,
)
function = completion_request["tools"][0]["function"]
assert function["parameters"] == {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
}