diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index b1594d608a..4ef5403b86 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -50,6 +50,28 @@ ) class ProviderOpenAIOfficial(Provider): _ERROR_TEXT_CANDIDATE_MAX_CHARS = 4096 + # 空内容占位常量,方便统一修改 + EMPTY_CONTENT_PLACEHOLDER = " " + + # 新增:统一转换消息格式,带完整类型防御 + def _adapt_messages_for_third_party(self, payloads: dict) -> None: + messages = payloads.get("messages", []) + for msg in messages: + # 过滤非字典消息,防止 .get 报错 + if not isinstance(msg, dict): + continue + content = msg.get("content") + # 处理多模态数组content + if isinstance(content, list): + text_parts = [] + for block in content: + # 仅处理标准text块,跳过图片/音频等 + if isinstance(block, dict) and block.get("type") == "text": + text_parts.append(block.get("text", "")) + msg["content"] = " ".join(text_parts) if text_parts else self.EMPTY_CONTENT_PLACEHOLDER + # 空内容填充占位符 + if not content: + msg["content"] = self.EMPTY_CONTENT_PLACEHOLDER @classmethod def _truncate_error_text_candidate(cls, text: str) -> str: @@ -535,6 +557,11 @@ async def _query( *, request_max_retries: int | None = None, ) -> LLMResponse: + # 仅非原生OpenAI服务执行格式适配(MindIE、国产推理等) + base_url = self.client.base_url.host or "" + if "openai.com" not in base_url: + self._adapt_messages_for_third_party(payloads) + if tools: model = payloads.get("model", "").lower() omit_empty_param_field = "gemini" in model @@ -593,6 +620,11 @@ async def _query_stream( *, request_max_retries: int | None = None, ) -> AsyncGenerator[LLMResponse, None]: + # 仅非原生OpenAI服务执行格式适配(MindIE、国产推理等) + base_url = self.client.base_url.host or "" + if "openai.com" not in base_url: + self._adapt_messages_for_third_party(payloads) + """流式查询API,逐步返回结果""" if tools: model = payloads.get("model", "").lower()