fix: parse tool_call arguments as dicts for OpenAI-compatible engines (#69)

messages_to_dicts() serializes tool_call arguments as JSON strings,
but OpenAI-compatible servers (vLLM, SGLang, llama.cpp, etc.) expect
them as JSON objects. This caused 400 Bad Request errors on every
multi-turn tool-calling conversation, breaking GAIA, DeepPlanning,
and LifelongAgent benchmarks for all local models.

The fix adds _fix_tool_call_arguments() to _OpenAICompatibleEngine
which json.loads() any string-typed arguments back to dicts before
sending. Applied to both generate() and stream() methods. This is
the same fix as commit 45c0e44 (Ollama), now applied to all
OpenAI-compatible engines.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jon Saad-Falcon
2026-03-15 12:12:20 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 51a2b4cceb
commit afa50b0ff7
+24 -2
View File
@@ -32,6 +32,26 @@ class _OpenAICompatibleEngine(InferenceEngine):
# -- InferenceEngine interface ------------------------------------------
@staticmethod
def _fix_tool_call_arguments(msg_dicts: list) -> list:
"""Ensure tool_call arguments are dicts, not JSON strings.
OpenAI-compatible servers (vLLM, SGLang, llama.cpp, etc.) expect
tool_call arguments as JSON objects. ``messages_to_dicts`` may
serialize them as strings, which causes 400 errors on multi-turn
tool-calling conversations.
"""
for md in msg_dicts:
for tc in md.get("tool_calls", []):
fn = tc.get("function", {})
args = fn.get("arguments")
if isinstance(args, str):
try:
fn["arguments"] = json.loads(args)
except (json.JSONDecodeError, TypeError):
pass
return msg_dicts
def generate(
self,
messages: Sequence[Message],
@@ -41,9 +61,10 @@ class _OpenAICompatibleEngine(InferenceEngine):
max_tokens: int = 1024,
**kwargs: Any,
) -> Dict[str, Any]:
msg_dicts = self._fix_tool_call_arguments(messages_to_dicts(messages))
payload: Dict[str, Any] = {
"model": model,
"messages": messages_to_dicts(messages),
"messages": msg_dicts,
"temperature": temperature,
"max_tokens": max_tokens,
"stream": False,
@@ -105,9 +126,10 @@ class _OpenAICompatibleEngine(InferenceEngine):
max_tokens: int = 1024,
**kwargs: Any,
) -> AsyncIterator[str]:
msg_dicts = self._fix_tool_call_arguments(messages_to_dicts(messages))
payload: Dict[str, Any] = {
"model": model,
"messages": messages_to_dicts(messages),
"messages": msg_dicts,
"temperature": temperature,
"max_tokens": max_tokens,
"stream": True,