Skip to content

fix(openai): Adapt content format for third-party LLM like MindIE#9337

Open
dourylee wants to merge 2 commits into
AstrBotDevs:masterfrom
dourylee:fix-openai-content-array-for-mindie
Open

fix(openai): Adapt content format for third-party LLM like MindIE#9337
dourylee wants to merge 2 commits into
AstrBotDevs:masterfrom
dourylee:fix-openai-content-array-for-mindie

Conversation

@dourylee

@dourylee dourylee commented Jul 21, 2026

Copy link
Copy Markdown

Fix 422 Input Validation Error when connecting MindIE and other third-party LLM services:

  1. Convert messages.content array format to plain text string
  2. Fill empty content with placeholder to avoid input check failure Adapt for MindIE LLM inference framework and Qwen3-30B-A3B-w8a8 model.

问题描述

When connecting AstrBot to third-party LLM services based on MindIE framework (e.g. Qwen3-30B-A3B-w8a8 model),
the interface returns 422 Input Validation Error.

Root cause:

  1. AstrBot sends messages.content as array format, but MindIE only supports string content.
  2. Empty content will trigger input text check failure on MindIE.

修复方案

  1. Convert content array to plain text string by extracting all text blocks.
  2. Fill empty content with a space placeholder to avoid validation error.
  3. Apply fix to both normal chat mode and stream chat mode.

测试情况

Tested with MindIE + Qwen3-30B-A3B-w8a8 model:

  • Normal Bot mode: works normally
  • Stream Chat mode: works normally
  • No more 422 error.

Summary by Sourcery

Bug Fixes:

  • Normalize array-based and empty message content into plain text placeholders to avoid 422 input validation errors with MindIE and similar LLM services in both standard and streaming chat modes.

Fix 422 Input Validation Error when connecting MindIE and other third-party LLM services:
1. Convert messages.content array format to plain text string
2. Fill empty content with placeholder to avoid input check failure
Adapt for MindIE LLM inference framework and Qwen3-30B-A3B-w8a8 model.
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. labels Jul 21, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The content normalization logic is duplicated in both _query and _query_stream; consider extracting it into a shared helper to keep behavior consistent and easier to maintain.
  • Currently all models go through the MindIE-oriented content flattening and placeholder logic; you might want to gate this behavior based on model/provider configuration to avoid altering content for providers that support the array format.
  • Using a single space as a placeholder for empty content is a bit opaque; consider defining a named constant or configurable placeholder so the intent is clearer and easier to adjust if MindIE’s validation rules change.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The content normalization logic is duplicated in both `_query` and `_query_stream`; consider extracting it into a shared helper to keep behavior consistent and easier to maintain.
- Currently all models go through the MindIE-oriented content flattening and placeholder logic; you might want to gate this behavior based on model/provider configuration to avoid altering content for providers that support the array format.
- Using a single space as a placeholder for empty content is a bit opaque; consider defining a named constant or configurable placeholder so the intent is clearer and easier to adjust if MindIE’s validation rules change.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces payload adaptation logic in both the _query and _query_stream methods of openai_source.py to handle third-party LLM services (such as MindIE) that do not support content arrays or empty content. The review feedback highlights that this implementation introduces duplicated code and lacks defensive type checking. It is recommended to refactor this logic into a shared helper method and add type checks to prevent potential runtime errors.

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.

Comment on lines +538 to +546
# 修复第三方LLM服务(MindIE等)不支持content数组、空content导致422校验失败
for msg in payloads.get("messages", []):
if isinstance(msg.get("content"), list):
# 拼接数组内所有文本内容
texts = [block.get("text", "") for block in msg["content"] if block.get("type") == "text"]
msg["content"] = " ".join(texts) if texts else " "
# 空内容填充占位符,避免输入校验失败
if not msg.get("content"):
msg["content"] = " "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

问题与改进建议

  1. 代码重复 (Code Duplication)
    _query_query_stream 中,完全相同的消息格式适配逻辑被重复编写了两次。根据通用规则,应当将此类相似逻辑重构为共享的辅助函数/方法,以提高代码的可维护性并避免重复。

  2. 防御性编程 (Defensive Programming)
    当前代码直接调用了 msg.get("content")block.get("text"),但没有预先校验 msgblock 是否为字典类型(dict)。如果 messages 列表中包含非字典类型的元素,或者 content 列表内包含非字典类型的元素,程序将会抛出 AttributeError 异常。

重构方案

建议在 ProviderOpenAIOfficial 类中添加一个私有辅助方法 _adapt_messages_for_third_party,并在 _query_query_stream 中调用它。

请在类中添加如下辅助方法:

    def _adapt_messages_for_third_party(self, payloads: dict) -> None:
        # 修复第三方LLM服务(MindIE等)不支持content数组、空content导致422校验失败
        for msg in payloads.get("messages", []):
            if not isinstance(msg, dict):
                continue
            content = msg.get("content")
            if isinstance(content, list):
                # 拼接数组内所有文本内容
                texts = [
                    block.get("text", "")
                    for block in content
                    if isinstance(block, dict) and block.get("type") == "text"
                ]
                msg["content"] = " ".join(texts) if texts else " "
            # 空内容填充占位符,避免输入校验失败
            if not msg.get("content"):
                msg["content"] = " "
Suggested change
# 修复第三方LLM服务(MindIE等)不支持content数组、空content导致422校验失败
for msg in payloads.get("messages", []):
if isinstance(msg.get("content"), list):
# 拼接数组内所有文本内容
texts = [block.get("text", "") for block in msg["content"] if block.get("type") == "text"]
msg["content"] = " ".join(texts) if texts else " "
# 空内容填充占位符,避免输入校验失败
if not msg.get("content"):
msg["content"] = " "
self._adapt_messages_for_third_party(payloads)
References
  1. When implementing similar functionality for different cases, refactor the logic into a shared helper function to avoid code duplication.

Comment on lines +605 to +613
# 修复第三方LLM服务(MindIE等)不支持content数组、空content导致422校验失败
for msg in payloads.get("messages", []):
if isinstance(msg.get("content"), list):
# 拼接数组内所有文本内容
texts = [block.get("text", "") for block in msg["content"] if block.get("type") == "text"]
msg["content"] = " ".join(texts) if texts else " "
# 空内容填充占位符,避免输入校验失败
if not msg.get("content"):
msg["content"] = " "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

代码重复与重构建议

此处的逻辑与 _query 中的消息适配逻辑完全相同。建议将其重构为共享的辅助方法 _adapt_messages_for_third_party,以消除重复代码并增强防御性校验。具体重构方案和辅助方法实现请参考 _query 方法中的评审意见。

Suggested change
# 修复第三方LLM服务(MindIE等)不支持content数组、空content导致422校验失败
for msg in payloads.get("messages", []):
if isinstance(msg.get("content"), list):
# 拼接数组内所有文本内容
texts = [block.get("text", "") for block in msg["content"] if block.get("type") == "text"]
msg["content"] = " ".join(texts) if texts else " "
# 空内容填充占位符,避免输入校验失败
if not msg.get("content"):
msg["content"] = " "
self._adapt_messages_for_third_party(payloads)
References
  1. When implementing similar functionality for different cases, refactor the logic into a shared helper function to avoid code duplication.

…tional enable

1 Extract duplicated content convert logic into _adapt_messages_for_third_party shared method
2 Add full isinstance type check to avoid AttributeError
3 Define EMPTY_CONTENT_PLACEHOLDER constant instead magic space
4 Only enable format adapt for non-official OpenAI endpoints, keep native multi-modal support
Fix MindIE 422 validation error without breaking original OpenAI functionality
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Jul 21, 2026
@dourylee

Copy link
Copy Markdown
Author

Refactored as requested:
1 Extracted duplicate conversion logic to shared helper method with full type defense checks.
2 Added class constant for empty placeholder instead of magic value.
3 Added conditional execution: only run adapt logic when endpoint is not official OpenAI, preserve native multi-modal array support for standard OpenAI services.
Tested again with MindIE Qwen3-30B-A3B-w8a8, 422 error resolved; standard GPT series works without regression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant