Skip to content

Commit ebfb5bf

Browse files
committed
Fix llama-stack function tool serialization for Gemini compatibility
llama-stack's Responses API to Chat Completions conversion in streaming.py leaks a "type" field into the function tool dict via model_dump(). The OpenAI Chat Completions FunctionDefinition schema only allows name, description, parameters, and strict — not type. OpenAI silently ignores the extra field, but Gemini's OpenAI-compatible endpoint strictly validates and rejects it with: "Unknown name 'type' at 'tools[N].function': Cannot find field." Fix: exclude type from serialization in streaming.py: function=input_tool.model_dump(exclude={"type"}) Signed-off-by: Dan Prince <dprince@redhat.com>
1 parent d1ccbfe commit ebfb5bf

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

src/llama_stack/providers/inline/agents/meta_reference/responses/streaming.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1397,8 +1397,12 @@ def make_openai_tool(tool_name: str, tool: ToolDef) -> ChatCompletionToolParam:
13971397

13981398
for input_tool in tools:
13991399
if input_tool.type == "function":
1400+
# Exclude "type" from the function dict — it belongs on the outer
1401+
# ChatCompletionToolParam, not inside the FunctionDefinition.
1402+
# OpenAI silently ignores the extra field, but stricter endpoints
1403+
# (e.g. Gemini) reject it.
14001404
self.ctx.chat_tools.append(
1401-
ChatCompletionToolParam(type="function", function=input_tool.model_dump(exclude_none=True))
1405+
ChatCompletionToolParam(type="function", function=input_tool.model_dump(exclude={"type"}, exclude_none=True))
14021406
) # type: ignore[typeddict-item,arg-type] # Dict compatible with FunctionDefinition
14031407
elif input_tool.type in WebSearchToolTypes:
14041408
tool_name = "web_search"

tests/unit/providers/agents/meta_reference/test_openai_responses.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,47 @@ async def test_function_tool_strict_false_included(openai_responses_impl, mock_i
25402540
assert tool_function["strict"] is False, "strict field should be False"
25412541

25422542

2543+
async def test_function_tool_type_field_excluded_from_function_dict(openai_responses_impl, mock_inference_api):
2544+
"""Test that the 'type' field from the input tool is excluded from the function dict.
2545+
2546+
The function dict inside ChatCompletionToolParam should only contain function-specific
2547+
fields (name, description, parameters, strict) and not the top-level 'type' field,
2548+
which belongs on the outer tool param.
2549+
"""
2550+
input_text = "What is the weather?"
2551+
model = "meta-llama/Llama-3.1-8B-Instruct"
2552+
2553+
mock_inference_api.openai_chat_completion.return_value = fake_stream()
2554+
2555+
await openai_responses_impl.create_openai_response(
2556+
input=input_text,
2557+
model=model,
2558+
stream=False,
2559+
tools=[
2560+
OpenAIResponseInputToolFunction(
2561+
type="function",
2562+
name="get_weather",
2563+
description="Get weather information",
2564+
parameters={"type": "object", "properties": {"location": {"type": "string"}}, "required": ["location"]},
2565+
)
2566+
],
2567+
)
2568+
2569+
assert mock_inference_api.openai_chat_completion.call_count == 1
2570+
params = mock_inference_api.openai_chat_completion.call_args[0][0]
2571+
2572+
assert len(params.tools) == 1
2573+
# The outer tool param should have type="function"
2574+
assert params.tools[0]["type"] == "function"
2575+
# The inner function dict should NOT contain 'type'
2576+
tool_function = params.tools[0]["function"]
2577+
assert "type" not in tool_function, (
2578+
"The input tool 'type' field should be excluded from the function dict"
2579+
)
2580+
assert tool_function["name"] == "get_weather"
2581+
assert tool_function["description"] == "Get weather information"
2582+
2583+
25432584
async def test_create_openai_response_with_truncation_disabled_streaming(
25442585
openai_responses_impl, mock_inference_api, mock_responses_store
25452586
):

0 commit comments

Comments
 (0)