Skip to content
Open
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
37 changes: 36 additions & 1 deletion src/google/adk/models/lite_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2092,6 +2092,39 @@ def _schema_to_dict(schema: types.Schema | dict[str, Any]) -> dict[str, Any]:
return schema_dict


def _append_response_schema_to_description(
description: str,
function_declaration: types.FunctionDeclaration,
) -> str:
"""Appends a rendering of the function's output schema to its description.

OpenAI-compatible chat completions tool definitions have no standard field
for declaring the schema of a tool's result, so the schema is rendered into
the tool description, which is forwarded to the model. The description is
returned unchanged when the function declaration has no output schema.

Args:
description: The original tool description.
function_declaration: The function declaration to read the output schema
from. `response_json_schema` takes precedence over `response`.

Returns:
The description, with the rendered output schema appended when one exists.
"""
response_schema: Optional[dict[str, Any]] = None
if function_declaration.response_json_schema:
response_schema = dict(function_declaration.response_json_schema)
elif function_declaration.response:
response_schema = _schema_to_dict(function_declaration.response)

if not response_schema:
return description

rendered_schema = json.dumps(response_schema, sort_keys=True)
suffix = f"Returns a JSON object conforming to this schema: {rendered_schema}"
return f"{description}\n{suffix}" if description else suffix


def _function_declaration_to_tool_param(
function_declaration: types.FunctionDeclaration,
) -> dict[str, Any]:
Expand Down Expand Up @@ -2129,7 +2162,9 @@ def _function_declaration_to_tool_param(
"type": "function",
"function": {
"name": function_declaration.name,
"description": function_declaration.description or "",
"description": _append_response_schema_to_description(
function_declaration.description or "", function_declaration
),
"parameters": parameters,
},
}
Expand Down
82 changes: 82 additions & 0 deletions tests/unittests/models/test_litellm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,88 @@ def test_function_declaration_to_tool_param_with_parameters_json_schema():
assert _function_declaration_to_tool_param(func_decl) == expected


def test_function_declaration_to_tool_param_with_response_json_schema():
"""Ensure a raw response_json_schema is rendered into the description."""

func_decl = types.FunctionDeclaration(
name="fn_with_output",
description="desc",
parameters_json_schema={
"type": "object",
"properties": {"a": {"type": "string"}},
},
response_json_schema={
"type": "object",
"properties": {"result": {"type": "string"}},
},
)

tool_param = _function_declaration_to_tool_param(func_decl)

assert tool_param["function"]["description"] == (
"desc\nReturns a JSON object conforming to this schema:"
' {"properties": {"result": {"type": "string"}}, "type": "object"}'
)
assert tool_param["function"]["parameters"] == {
"type": "object",
"properties": {"a": {"type": "string"}},
}


def test_function_declaration_to_tool_param_with_response_schema():
"""Ensure a types.Schema response is rendered into the description."""

func_decl = types.FunctionDeclaration(
name="fn_with_output_schema",
description="desc",
response=types.Schema(
type=types.Type.OBJECT,
properties={"result": types.Schema(type=types.Type.STRING)},
),
)

tool_param = _function_declaration_to_tool_param(func_decl)

assert tool_param["function"]["description"] == (
"desc\nReturns a JSON object conforming to this schema:"
' {"properties": {"result": {"type": "string"}}, "type": "object"}'
)


def test_function_declaration_to_tool_param_without_response_schema():
"""Ensure the description is unchanged when no output schema is declared."""

func_decl = types.FunctionDeclaration(
name="fn_without_output",
description="desc",
parameters_json_schema={"type": "object", "properties": {}},
)

assert (
_function_declaration_to_tool_param(func_decl)["function"]["description"]
== "desc"
)


def test_function_declaration_to_tool_param_response_schema_without_description():
"""Ensure an empty description yields only the rendered output schema."""

func_decl = types.FunctionDeclaration(
name="fn_no_description",
response_json_schema={
"type": "object",
"properties": {"result": {"type": "string"}},
},
)

assert _function_declaration_to_tool_param(func_decl)["function"][
"description"
] == (
"Returns a JSON object conforming to this schema:"
' {"properties": {"result": {"type": "string"}}, "type": "object"}'
)


@pytest.mark.asyncio
async def test_generate_content_async_with_system_instruction(
lite_llm_instance, mock_acompletion
Expand Down