Skip to content

Commit 79c79ff

Browse files
chuenchen309claudebogdankostic
authored
fix: handle empty or missing tool-call arguments in from_openai_dict_format (#11972)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: bogdankostic <bogdankostic@web.de>
1 parent 64fb0d2 commit 79c79ff

3 files changed

Lines changed: 34 additions & 1 deletion

File tree

haystack/dataclasses/chat_message.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,10 +804,13 @@ def from_openai_dict_format(cls, message: dict[str, Any]) -> "ChatMessage":
804804
if tool_calls:
805805
haystack_tool_calls = []
806806
for tc in tool_calls:
807+
# Zero-argument tool calls from OpenAI-compatible servers may send an
808+
# empty string, null, or omit `arguments` entirely; treat all as {}.
809+
raw_arguments = tc["function"].get("arguments")
807810
haystack_tc = ToolCall(
808811
id=tc.get("id"),
809812
tool_name=tc["function"]["name"],
810-
arguments=json.loads(tc["function"]["arguments"]),
813+
arguments=json.loads(raw_arguments) if raw_arguments else {},
811814
)
812815
haystack_tool_calls.append(haystack_tc)
813816
return cls.from_assistant(text=content, name=name, tool_calls=haystack_tool_calls)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed ``ChatMessage.from_openai_dict_format`` crashing on zero-argument tool
5+
calls. OpenAI-compatible servers (vLLM, llama.cpp, Ollama, LM Studio, ...) may
6+
send an empty string or omit the ``arguments`` field for a tool call that takes
7+
no arguments; this previously raised ``json.JSONDecodeError`` or ``KeyError``.
8+
Such tool calls are now parsed into a ``ToolCall`` with empty ``arguments``.

test/dataclasses/test_chat_message.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,28 @@ def test_from_openai_dict_format_assistant_message_with_tool_calls(self):
10471047
assert tool_call.tool_name == "get_weather"
10481048
assert tool_call.arguments == {"location": "Berlin"}
10491049

1050+
def test_from_openai_dict_format_tool_call_with_empty_arguments(self):
1051+
# OpenAI-compatible servers (vLLM, llama.cpp, Ollama, ...) emit an empty
1052+
# string for a zero-argument tool call; it must not crash.
1053+
openai_msg = {
1054+
"role": "assistant",
1055+
"content": None,
1056+
"tool_calls": [{"id": "call_1", "function": {"name": "now", "arguments": ""}}],
1057+
}
1058+
message = ChatMessage.from_openai_dict_format(openai_msg)
1059+
assert message.tool_call == ToolCall(id="call_1", tool_name="now", arguments={})
1060+
1061+
def test_from_openai_dict_format_tool_call_with_missing_arguments(self):
1062+
# Some servers omit the `arguments` key entirely for zero-argument calls.
1063+
openai_msg = {
1064+
"role": "assistant",
1065+
"content": None,
1066+
"tool_calls": [{"id": "call_1", "function": {"name": "now"}}],
1067+
}
1068+
message = ChatMessage.from_openai_dict_format(openai_msg)
1069+
assert message.tool_call is not None
1070+
assert message.tool_call.arguments == {}
1071+
10501072
def test_from_openai_dict_format_tool_message(self):
10511073
openai_msg = {"role": "tool", "content": "The weather is sunny", "tool_call_id": "call_123"}
10521074
message = ChatMessage.from_openai_dict_format(openai_msg)

0 commit comments

Comments
 (0)