Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Callable tools: optional scalar parameters (`int | None`) get `"type": "object"` in `parameters_json_schema`, so Gemini sends `{}`

Đã đóng
#3,060 1 bình luận 0 reaction 1 người được giao Xem trên GitHub

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.

  • #3074 của @chauvuusvn — đang mở
  • #3093 của @DavidLeeGarrett — đang mở

Đánh giá

Issue này chưa được đánh giá.

Mô tả

priority: p2 type: bug
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 rejects anyOf
    without type).
  • google/adk-python#3424: the same missing-type validation 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-anyOf unwrapping drops title/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

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của googleapis/python-genai

Tất cả issue của googleapis/python-genai

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.