Callable tools: optional scalar parameters (`int | None`) get `"type": "object"` in `parameters_json_schema`, so Gemini sends `{}`
Maintainer thường phản hồi trong vòng 1 ngày
@Venkaiahbabuneelam đang làm issue này rồi.
Từ ngày 6/10/2026.
Đánh giá
Issue này chưa được đánh giá.
Mô tả
Environment
- google-genai 2.25.0 (latest on PyPI as of 2026-10-06), pydantic 2.13.5
- Python 3.13.5, Gemini Developer API, model
gemini-pro-latest
Summary
When a plain Python function is passed in tools=[...], an optional scalar
parameter such as start_line: int | None = None is declared as:
{"anyOf": [{"type": "integer"}, {"type": "null"}], "type": "object", "default": null}
anyOf already allows only an integer or null. The extra "type": "object"
adds a requirement that the value also be an object, which no integer meets.
The model follows the object part and fills the argument with {}. Calling
the function with the returned args then fails, e.g. max(1, {}) raises
TypeError: '>' not supported between instances of 'dict' and 'int'.
Cause
t_tool in _transformers.py builds callable tools with
FunctionDeclaration.from_callable(..., use_json_schema=True). That path,
parse_function_declaration_json_schema in _automatic_function_calling_util.py,
does:
# pydantic doesn't assign the `type` field when the schema has 'anyOf'.
if not 'type' in json_schema_dict and 'anyOf' in json_schema_dict:
json_schema_dict['type'] = 'object'
pydantic's schema for int | None is already complete without type. Setting
it to 'object' is only correct for unions of object types, and it breaks
every optional scalar (int | None, str | None, bool | None, ...).
Reproduction
# /// script
# dependencies = ["google-genai"]
# ///
import json
import os
from google import genai
from google.genai import types
def read_file(path: str, start_line: int | None = None, end_line: int | None = None) -> dict:
"""Reads a file, optionally only lines start_line to end_line (1-indexed)."""
return {}
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
config = types.GenerateContentConfig(
tools=[read_file],
automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
tool_config=types.ToolConfig(
function_calling_config=types.FunctionCallingConfig(mode="ANY")
),
)
for i in range(3):
response = client.models.generate_content(
model="gemini-pro-latest",
contents="Read lines 100 to 120 of CONCEPT.md.",
config=config,
)
for call in response.function_calls or []:
print(json.dumps(call.args))
Output, even though the prompt names the lines explicitly:
{"start_line": {}, "end_line": {}, "path": "CONCEPT.md"}
{"path": "CONCEPT.md", "end_line": {}, "start_line": {}}
{"end_line": {}, "path": "CONCEPT.md", "start_line": {}}
The generated schema can be inspected without an API call:
from google.genai import _transformers
print(_transformers.t_tool(client._api_client, read_file).model_dump(mode="json", exclude_none=True))
Expected behavior
The model sends integers for start_line/end_line, or leaves them out.
Building the declaration through the non-JSON-schema path does exactly that:
types.Tool(function_declarations=[
types.FunctionDeclaration.from_callable_with_api_option(callable=read_file)
])
This yields {"type": "INTEGER", "nullable": true}. Over 8 trials of a similar
prompt, the model sent integers every time with that schema, and {} every
time with the anyOf + object schema.
Suggested fix
The type: 'object' default exists for a reason: some backends reject an
anyOf schema without a top-level type. See #1807, where Vertex AI fails
with "schema didn't specify the schema type field", and
google/adk-python#3424. So the fix should keep a top-level type but derive
it from the union's non-null members, falling back to 'object' only when
they are objects:
if 'type' not in json_schema_dict and 'anyOf' in json_schema_dict:
member_types = {
m.get('type') for m in json_schema_dict['anyOf'] if m.get('type') != 'null'
}
json_schema_dict['type'] = (
member_types.pop() if len(member_types) == 1 and None not in member_types
else 'object'
)
I tested the candidate schemas against the Gemini Developer API
(gemini-pro-latest, prompt "Read lines 100 to 120 of CONCEPT.md.", 3 calls
each):
Schema for start_line / end_line |
Args returned |
|---|---|
Current: anyOf[integer, null] + "type": "object" |
{} 3/3 |
anyOf[integer, null], no type |
100/120 3/3 |
anyOf[integer, null] + "type": "integer" (proposed) |
100/120 3/3 |
Collapsed: "type": ["integer", "null"] |
100/120 3/3 |
Leaving type unset also works on the Developer API, but it would likely
bring back the Vertex errors in #1807. Collapsing anyOf: [X, null] into a
type list is an alternative if Vertex accepts type lists. I couldn't test
either option on Vertex.
Related
- #1807: why the
type: 'object'default was added (Vertex AI rejectsanyOf
withouttype). - google/adk-python#3424: the same missing-
typevalidation failure for MCP
tools. - mlflow/mlflow#26359: the same symptom (Gemini returns empty arguments for
anyOf: [T, null]) from a different source, mlflow's gateway forwarding
OpenAI schemas. - #1992 and #2917: nullable-
anyOfunwrapping dropstitle/description. A
different code path (Schema.from_json_schema), in the same area.
Workaround
Pass explicit declarations built with
FunctionDeclaration.from_callable_with_api_option(callable=fn) instead of
bare callables.
- Ngôn ngữ chính
- Python
- Star
- 4k
- Fork
- 1k
- Merge trung bình
- 1 ngày 8 giờ
- Pull request đã merge (30 ngày)
- 93
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của googleapis/python-genai
-
Test import module that doesn't existCó thể đã có người làm @Venkaiahbabuneelam đã nhận 3 ngày trước. Đang mởpriority: p2 status:awaiting user response type: bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
googleapis/python-genai#3122 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
FunctionDeclaration.from_callable drops enum and properties from Optional[...] parametersCó thể đã có người làm @Venkaiahbabuneelam đã nhận 4 ngày trước. Đang mởpriority: p2 type: bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 67/100
googleapis/python-genai#3105 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Curated history keeps half a user turn when the model turn is invalidCó thể đã có người làm @Venkaiahbabuneelam đã nhận 12 ngày trước. Đang mởpriority: p2 type: bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
googleapis/python-genai#3051 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[Bug]: Unsubscripted typing.List and typing.Dict crash convert_if_exist_pydantic_model in AFCCó thể đã có người làm @Venkaiahbabuneelam đã nhận 12 ngày trước. Đang mởpriority: p2 type: bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
googleapis/python-genai#3044 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Name collision in google.genai.interactions: triggers.Interaction shadows response model Interaction in static type checkersCó thể đã có người làm @Venkaiahbabuneelam đã nhận 19 ngày trước. Đang mởpriority: p2 type: bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
googleapis/python-genai#3013 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của googleapis/python-genai
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
omegaup/omegaup#10353 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 5 ngày
-
bot-triaged module: LrScheduler module: optimizer triaged
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
Maintainer thường phản hồi trong vòng 1 ngày
-
documentation glm5 llm-model
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
vllm-project/vllm-ascend#19062 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Author Page: shuai-zhao-llmĐang mởauthor-page correction
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
acl-org/acl-anthology#10496 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày