fix: handle empty or missing tool-call arguments in from_openai_dict_format (#11972)

Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: bogdankostic <[email protected]>
This commit is contained in:
Andrew Chen
2026-07-16 10:03:20 +02:00
committed by GitHub
co-authored by Claude Opus 4.8 bogdankostic
parent 64fb0d2010
commit 79c79ff7bb
3 changed files with 34 additions and 1 deletions
+4 -1
View File
@@ -804,10 +804,13 @@ class ChatMessage:
if tool_calls:
haystack_tool_calls = []
for tc in tool_calls:
# Zero-argument tool calls from OpenAI-compatible servers may send an
# empty string, null, or omit `arguments` entirely; treat all as {}.
raw_arguments = tc["function"].get("arguments")
haystack_tc = ToolCall(
id=tc.get("id"),
tool_name=tc["function"]["name"],
arguments=json.loads(tc["function"]["arguments"]),
arguments=json.loads(raw_arguments) if raw_arguments else {},
)
haystack_tool_calls.append(haystack_tc)
return cls.from_assistant(text=content, name=name, tool_calls=haystack_tool_calls)
@@ -0,0 +1,8 @@
---
fixes:
- |
Fixed ``ChatMessage.from_openai_dict_format`` crashing on zero-argument tool
calls. OpenAI-compatible servers (vLLM, llama.cpp, Ollama, LM Studio, ...) may
send an empty string or omit the ``arguments`` field for a tool call that takes
no arguments; this previously raised ``json.JSONDecodeError`` or ``KeyError``.
Such tool calls are now parsed into a ``ToolCall`` with empty ``arguments``.
+22
View File
@@ -1047,6 +1047,28 @@ class TestFromOpenaiDictFormat:
assert tool_call.tool_name == "get_weather"
assert tool_call.arguments == {"location": "Berlin"}
def test_from_openai_dict_format_tool_call_with_empty_arguments(self):
# OpenAI-compatible servers (vLLM, llama.cpp, Ollama, ...) emit an empty
# string for a zero-argument tool call; it must not crash.
openai_msg = {
"role": "assistant",
"content": None,
"tool_calls": [{"id": "call_1", "function": {"name": "now", "arguments": ""}}],
}
message = ChatMessage.from_openai_dict_format(openai_msg)
assert message.tool_call == ToolCall(id="call_1", tool_name="now", arguments={})
def test_from_openai_dict_format_tool_call_with_missing_arguments(self):
# Some servers omit the `arguments` key entirely for zero-argument calls.
openai_msg = {
"role": "assistant",
"content": None,
"tool_calls": [{"id": "call_1", "function": {"name": "now"}}],
}
message = ChatMessage.from_openai_dict_format(openai_msg)
assert message.tool_call is not None
assert message.tool_call.arguments == {}
def test_from_openai_dict_format_tool_message(self):
openai_msg = {"role": "tool", "content": "The weather is sunny", "tool_call_id": "call_123"}
message = ChatMessage.from_openai_dict_format(openai_msg)