Description
ChatCompletionsTransport.parse() in helpers/litellm_transport.py (line ~520) only extracts tool_calls from the response message when content is empty:
if not response_delta:
tool_calls = _as_list(_get_value(message, "tool_calls"))
response_delta = ChatCompletionsTransport.tool_calls_text(tool_calls)
Some OpenAI-compatible models (e.g., Zhipu GLM-4.7 via the Z.AI Coding Plan endpoint) return both content and tool_calls simultaneously. When this happens, the parser uses the text content and silently drops the tool calls, breaking the agentic loop.
The same pattern exists in ResponsesTransport.parse_response() (line ~1049).
Steps to Reproduce
- Configure a model that returns both content + tool_calls (e.g.,
openai/glm-4.7 via api.z.ai/api/coding/paas/v4)
- Send a request with tools and
tool_choice: "auto"
- Observe that the response contains both
message.content (non-empty text) and message.tool_calls
ChatCompletionsTransport.parse() returns the text content and _output_items is missing
Expected Behavior
Tool calls should be preserved regardless of whether content is also present. The finish_reason: "tool_calls" field indicates the model intended a tool call.
Suggested Fix
Check for tool calls first, then fall back to content:
# ChatCompletionsTransport.parse() — line ~518
parsed = {"reasoning_delta": reasoning_delta, "response_delta": response_delta}
tool_calls = _as_list(_get_value(message, "tool_calls"))
tool_calls_text = ChatCompletionsTransport.tool_calls_text(tool_calls) if tool_calls else ""
if tool_calls_text:
parsed["response_delta"] = tool_calls_text
parsed["_output_items"] = ChatCompletionsTransport.output_items(tool_calls)
return parsed
Environment
- Agent Zero v2.5
- LiteLLM via
litellm_provider: openai with custom api_base
- Z.AI Coding Plan endpoint (
api.z.ai/api/coding/paas/v4)
- GLM-4.7 model
a0_api_mode: chat
Additional Context
Tested all three models on the Z.AI Coding Plan:
- GLM-5.2: Returns empty content + tool_calls (standard format) ✅
- GLM-5-Turbo: Returns empty content + tool_calls (standard format) ✅
- GLM-4.7: Returns both content + tool_calls (non-standard) ❌ — breaks parser
The fix is backward-compatible — when no tool_calls are present, behavior is unchanged.
Description
ChatCompletionsTransport.parse()inhelpers/litellm_transport.py(line ~520) only extractstool_callsfrom the response message whencontentis empty:Some OpenAI-compatible models (e.g., Zhipu GLM-4.7 via the Z.AI Coding Plan endpoint) return both
contentandtool_callssimultaneously. When this happens, the parser uses the text content and silently drops the tool calls, breaking the agentic loop.The same pattern exists in
ResponsesTransport.parse_response()(line ~1049).Steps to Reproduce
openai/glm-4.7viaapi.z.ai/api/coding/paas/v4)tool_choice: "auto"message.content(non-empty text) andmessage.tool_callsChatCompletionsTransport.parse()returns the text content and_output_itemsis missingExpected Behavior
Tool calls should be preserved regardless of whether content is also present. The
finish_reason: "tool_calls"field indicates the model intended a tool call.Suggested Fix
Check for tool calls first, then fall back to content:
Environment
litellm_provider: openaiwith customapi_baseapi.z.ai/api/coding/paas/v4)a0_api_mode: chatAdditional Context
Tested all three models on the Z.AI Coding Plan:
The fix is backward-compatible — when no tool_calls are present, behavior is unchanged.