Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions src/celeste/providers/google/generate_content/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,34 +210,14 @@ def _map_user_tool(tool: dict[str, Any]) -> dict[str, Any]:
params = tool.get("parameters", {})
if isinstance(params, type) and issubclass(params, BaseModel):
schema = params.model_json_schema()
# Remove unsupported 'title' fields
schema = ToolsMapper._remove_titles(schema)
else:
schema = params

result: dict[str, Any] = {"name": tool["name"]}
if "description" in tool:
result["description"] = tool["description"]
if schema:
result["parameters"] = schema
return result

@staticmethod
def _remove_titles(schema: dict[str, Any]) -> dict[str, Any]:
"""Remove unsupported 'title' fields from schema for Google."""
result: dict[str, Any] = {}
for key, value in schema.items():
if key == "title":
continue
if isinstance(value, dict):
result[key] = ToolsMapper._remove_titles(value)
elif isinstance(value, list):
result[key] = [
ToolsMapper._remove_titles(item) if isinstance(item, dict) else item
for item in value
]
else:
result[key] = value
result["parametersJsonSchema"] = schema
return result


Expand Down
65 changes: 65 additions & 0 deletions tests/unit_tests/test_google_tools_mapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Regression coverage for Gemini ToolsMapper — emits `parametersJsonSchema`."""

from __future__ import annotations

from celeste.providers.google.generate_content.parameters import ToolsMapper


def _map(tool: dict) -> dict:
return ToolsMapper._map_user_tool(tool)


def test_maps_to_parameters_json_schema_not_legacy_parameters() -> None:
fn = _map(
{
"name": "example",
"description": "example tool",
"parameters": {
"type": "object",
"properties": {"prompt": {"type": "string"}},
"required": ["prompt"],
},
},
)
assert "parametersJsonSchema" in fn
assert "parameters" not in fn
assert fn["parametersJsonSchema"]["properties"]["prompt"]["type"] == "string"


def test_preserves_integer_enum_on_non_string_type() -> None:
fn = _map(
{
"name": "example",
"parameters": {
"type": "object",
"properties": {
"duration": {"type": "integer", "enum": [4, 6, 8]},
},
},
},
)
duration = fn["parametersJsonSchema"]["properties"]["duration"]
assert duration["type"] == "integer"
assert duration["enum"] == [4, 6, 8]


def test_preserves_title_fields() -> None:
fn = _map(
{
"name": "example",
"parameters": {
"title": "ExampleInput",
"type": "object",
"properties": {"x": {"type": "string", "title": "X"}},
},
},
)
assert fn["parametersJsonSchema"]["title"] == "ExampleInput"
assert fn["parametersJsonSchema"]["properties"]["x"]["title"] == "X"


def test_tool_with_no_parameters_emits_no_schema_field() -> None:
fn = _map({"name": "example", "description": "no-args"})
assert "parameters" not in fn
assert "parametersJsonSchema" not in fn
assert fn["description"] == "no-args"
Loading