Fix GLM OpenAI-compatible content format handling#9293
Conversation
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
_finally_convert_payload, the GLM conversion will overwritemessage['content']with joined text even when the original list contains non-text parts; consider only converting when all parts aretype == 'text'to avoid silently dropping multimodal content. - The GLM-specific text concatenation behavior is inconsistent between
assemble_context(no separator) and_finally_convert_payload(joined with"\n"); aligning these or centralizing the logic would make the behavior more predictable. - You now have GLM-specific text-only normalization logic both in
assemble_contextand_finally_convert_payload; consider factoring this into a shared helper to reduce duplication and keep future GLM rules in one place.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `_finally_convert_payload`, the GLM conversion will overwrite `message['content']` with joined text even when the original list contains non-text parts; consider only converting when *all* parts are `type == 'text'` to avoid silently dropping multimodal content.
- The GLM-specific text concatenation behavior is inconsistent between `assemble_context` (no separator) and `_finally_convert_payload` (joined with `"\n"`); aligning these or centralizing the logic would make the behavior more predictable.
- You now have GLM-specific text-only normalization logic both in `assemble_context` and `_finally_convert_payload`; consider factoring this into a shared helper to reduce duplication and keep future GLM rules in one place.
## Individual Comments
### Comment 1
<location path="astrbot/core/provider/sources/openai_source.py" line_range="1007-1011" />
<code_context>
+ if isinstance(part, dict) and part.get("type") == "text":
+ texts.append(part.get("text", ""))
+
+ message["content"] = "\n".join(texts)
+
is_gemini = "gemini" in model
</code_context>
<issue_to_address>
**suggestion:** Align GLM text concatenation behavior between payload preparation and context assembly.
Here you join text parts with newlines, whereas `assemble_context` concatenates them without a separator. That means the same multi-part content is formatted differently depending on the path taken. Please standardize the behavior (e.g., always use `"\n".join(...)` or a space) or make the distinction explicit so GLM sees consistent text across both stages.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| message["content"] = "\n".join(texts) | ||
|
|
||
| is_gemini = "gemini" in model | ||
| _deepseek_v4_markers = ("deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v4") | ||
| is_deepseek_v4_reasoning = ( |
There was a problem hiding this comment.
suggestion: Align GLM text concatenation behavior between payload preparation and context assembly.
Here you join text parts with newlines, whereas assemble_context concatenates them without a separator. That means the same multi-part content is formatted differently depending on the path taken. Please standardize the behavior (e.g., always use "\n".join(...) or a space) or make the distinction explicit so GLM sees consistent text across both stages.
There was a problem hiding this comment.
Code Review
This pull request introduces compatibility handling for GLM models in the OpenAI source provider, ensuring that message payloads and contexts are formatted as plain strings rather than multimodal lists. The review feedback highlights two potential TypeError bugs where part.get("text", "") could return None if the "text" key is explicitly set to None, which would cause the string joining operations to fail. Code suggestions were provided to safely handle and coerce these values.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| for part in content: | ||
| if isinstance(part, dict) and part.get("type") == "text": | ||
| texts.append(part.get("text", "")) |
There was a problem hiding this comment.
If part.get("text") is None, part.get("text", "") will still return None because the key "text" exists but its value is None. This will cause texts.append(None) to be executed, and subsequently "\n".join(texts) will raise a TypeError: sequence item expected str instance, NoneType found. To prevent this, we should defensively coerce the value to a string or handle None values safely, similar to how it is done in _normalize_content.
| for part in content: | |
| if isinstance(part, dict) and part.get("type") == "text": | |
| texts.append(part.get("text", "")) | |
| for part in content: | |
| if isinstance(part, dict) and part.get("type") == "text": | |
| val = part.get("text") | |
| texts.append(str(val) if val is not None else "") |
| if "glm" in self.get_model().lower() and all( | ||
| part.get("type") == "text" for part in content_blocks | ||
| ): | ||
| text_content = "".join(part.get("text", "") for part in content_blocks) |
There was a problem hiding this comment.
Similar to the payload conversion, using part.get("text", "") can return None if the key "text" is present but its value is None. To ensure robustness and prevent potential TypeError during "".join(), we should safely filter out or coerce None values.
| text_content = "".join(part.get("text", "") for part in content_blocks) | |
| text_content = "".join(str(part.get("text")) for part in content_blocks if part.get("text") is not None) |
Description
This PR fixes compatibility with Zhipu GLM OpenAI-Compatible APIs.
Currently AstrBot sends:
However GLM expects:
content: "..."which causes the API to return:
Changes
_finally_convert_payload().glm.assemble_context()to GLM only.Tested
Tested with:
GLM-4.7-FlashThe API now works correctly without affecting Gemini, DeepSeek, or other providers.
This patch was generated with assistance from ChatGPT Codex and verified through actual testing.
Summary by Sourcery
Improve compatibility with Zhipu GLM models in the OpenAI-compatible provider by normalizing chat message content formatting.
Enhancements: