fix: preserve tool_calls when content is non-empty in chat completions parser#1779
fix: preserve tool_calls when content is non-empty in chat completions parser#1779wgnrai wants to merge 1 commit into
Conversation
…s parser Some OpenAI-compatible models (e.g., Zhipu GLM-4.7) return both content and tool_calls in the same response. The previous parser logic only extracted tool_calls when content was empty, silently dropping tool calls when both were present. This broke the agentic loop for these models. Fix: check for tool_calls first, then fall back to content text. Applied to both ChatCompletionsTransport.parse() and ResponsesTransport.parse_response(). Closes agent0ai#1778
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bcce6d2dc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if tool_calls_text: | ||
| parsed["response_delta"] = tool_calls_text |
There was a problem hiding this comment.
Update the helper DOX contract
This parser precedence change is a behavioral change to helpers/litellm_transport.py, but the matching helpers/litellm_transport.py.dox.md was not updated. helpers/AGENTS.md explicitly requires, "When a helper module is added, removed, renamed, or behaviorally changed, update its matching *.py.dox.md in the same change." Please update the DOX contract to capture that tool/function calls now take precedence even when text content is present.
Useful? React with 👍 / 👎.
Problem
ChatCompletionsTransport.parse()only extractstool_callswhencontentis empty:Some OpenAI-compatible models (e.g., Zhipu GLM-4.7 via Z.AI Coding Plan) return both
contentandtool_callssimultaneously. When this happens, the parser uses the text content and silently drops the tool calls, breaking the agentic loop.Same pattern exists in
ResponsesTransport.parse_response().Fix
Check for tool_calls first, then fall back to content. Two locations:
ChatCompletionsTransport.parse()(line ~520): Extract tool_calls unconditionally; use them asresponse_deltawhen present.ResponsesTransport.parse_response()(line ~1049): Same approach for the Responses API path.Testing
Verified live against all three Z.AI Coding Plan models:
Full agentic pipeline tested: tool call → tool result → natural language response, all through A0's
LiteLLMTransport.complete().Closes #1778