diff --git a/astrbot/core/agent/runners/tool_loop_agent_runner.py b/astrbot/core/agent/runners/tool_loop_agent_runner.py index 1e04001e4..82ab11b05 100644 --- a/astrbot/core/agent/runners/tool_loop_agent_runner.py +++ b/astrbot/core/agent/runners/tool_loop_agent_runner.py @@ -81,7 +81,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): "func_tool": self.req.func_tool, "model": self.req.model, # NOTE: in fact, this arg is None in most cases "session_id": self.req.session_id, - "extra_content_blocks": self.req.extra_content_blocks, + "extra_user_content_parts": self.req.extra_user_content_parts, } if self.streaming: diff --git a/astrbot/core/provider/entities.py b/astrbot/core/provider/entities.py index 5f794442e..5ddca16c9 100644 --- a/astrbot/core/provider/entities.py +++ b/astrbot/core/provider/entities.py @@ -14,6 +14,7 @@ import astrbot.core.message.components as Comp from astrbot import logger from astrbot.core.agent.message import ( AssistantMessageSegment, + ContentPart, ToolCall, ToolCallMessageSegment, ) @@ -92,8 +93,10 @@ class ProviderRequest: """会话 ID""" image_urls: list[str] = field(default_factory=list) """图片 URL 列表""" - extra_content_blocks: list[dict] = field(default_factory=list) - """额外的内容块列表,用于在用户消息后添加额外的文本块(如系统提醒、指令等)""" + extra_user_content_parts: list[dict] | list[ContentPart] = field( + default_factory=list + ) + """额外的用户消息内容部分列表,用于在用户消息后添加额外的内容块(如系统提醒、指令等)。支持 dict 或 ContentPart 对象""" func_tool: ToolSet | None = None """可用的函数工具""" contexts: list[dict] = field(default_factory=list) @@ -179,7 +182,14 @@ class ProviderRequest: content_blocks.append({"type": "text", "text": "[图片]"}) # 2. 额外的内容块(系统提醒、指令等) - content_blocks.extend(self.extra_content_blocks) + if self.extra_user_content_parts: + for part in self.extra_user_content_parts: + if hasattr(part, "model_dump"): + # ContentPart 对象,需要 model_dump + content_blocks.append(part.model_dump()) + else: + # 已经是 dict + content_blocks.append(part) # 3. 图片内容 if self.image_urls: @@ -203,7 +213,7 @@ class ProviderRequest: if ( len(content_blocks) == 1 and content_blocks[0]["type"] == "text" - and not self.extra_content_blocks + and not self.extra_user_content_parts and not self.image_urls ): return {"role": "user", "content": content_blocks[0]["text"]} diff --git a/astrbot/core/provider/provider.py b/astrbot/core/provider/provider.py index b81e24da6..fdcc6f238 100644 --- a/astrbot/core/provider/provider.py +++ b/astrbot/core/provider/provider.py @@ -103,7 +103,7 @@ class Provider(AbstractProvider): system_prompt: str | None = None, tool_calls_result: ToolCallsResult | list[ToolCallsResult] | None = None, model: str | None = None, - extra_content_blocks: list[dict] | None = None, + extra_user_content_parts: list[dict] | None = None, **kwargs, ) -> LLMResponse: """获得 LLM 的文本对话结果。会使用当前的模型进行对话。 @@ -115,7 +115,7 @@ class Provider(AbstractProvider): tools: tool set contexts: 上下文,和 prompt 二选一使用 tool_calls_result: 回传给 LLM 的工具调用结果。参考: https://platform.openai.com/docs/guides/function-calling - extra_content_blocks: 额外的内容块列表,用于在用户消息后添加额外的文本块(如系统提醒、指令等) + extra_user_content_parts: 额外的内容块列表,用于在用户消息后添加额外的文本块(如系统提醒、指令等) kwargs: 其他参数 Notes: diff --git a/astrbot/core/provider/sources/anthropic_source.py b/astrbot/core/provider/sources/anthropic_source.py index 788047375..a4ad9e832 100644 --- a/astrbot/core/provider/sources/anthropic_source.py +++ b/astrbot/core/provider/sources/anthropic_source.py @@ -296,7 +296,7 @@ class ProviderAnthropic(Provider): system_prompt=None, tool_calls_result=None, model=None, - extra_content_blocks=None, + extra_user_content_parts=None, **kwargs, ) -> LLMResponse: if contexts is None: @@ -304,7 +304,7 @@ class ProviderAnthropic(Provider): new_record = None if prompt is not None: new_record = await self.assemble_context( - prompt, image_urls, extra_content_blocks + prompt, image_urls, extra_user_content_parts ) context_query = self._ensure_message_to_dicts(contexts) if new_record: @@ -353,7 +353,7 @@ class ProviderAnthropic(Provider): system_prompt=None, tool_calls_result=None, model=None, - extra_content_blocks=None, + extra_user_content_parts=None, **kwargs, ): if contexts is None: @@ -361,7 +361,7 @@ class ProviderAnthropic(Provider): new_record = None if prompt is not None: new_record = await self.assemble_context( - prompt, image_urls, extra_content_blocks + prompt, image_urls, extra_user_content_parts ) context_query = self._ensure_message_to_dicts(contexts) if new_record: @@ -398,7 +398,7 @@ class ProviderAnthropic(Provider): self, text: str, image_urls: list[str] | None = None, - extra_content_blocks: list[dict] | None = None, + extra_user_content_parts: list[dict] | None = None, ): """组装上下文,支持文本和图片""" content = [] @@ -409,15 +409,17 @@ class ProviderAnthropic(Provider): elif image_urls: # 如果没有文本但有图片,添加占位文本 content.append({"type": "text", "text": "[图片]"}) - elif extra_content_blocks: + elif extra_user_content_parts: # 如果只有额外内容块,也需要添加占位文本 content.append({"type": "text", "text": " "}) # 2. 额外的内容块(系统提醒、指令等) - if extra_content_blocks: + if extra_user_content_parts: # 过滤出文本块,因为 Anthropic 主要支持文本和图片 text_blocks = [ - block for block in extra_content_blocks if block.get("type") == "text" + block + for block in extra_user_content_parts + if block.get("type") == "text" ] content.extend(text_blocks) @@ -460,7 +462,7 @@ class ProviderAnthropic(Provider): # 如果只有主文本且没有额外内容块和图片,返回简单格式以保持向后兼容 if ( text - and not extra_content_blocks + and not extra_user_content_parts and not image_urls and len(content) == 1 and content[0]["type"] == "text" diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index 918bb1f87..0dfe048de 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -680,7 +680,7 @@ class ProviderGoogleGenAI(Provider): system_prompt=None, tool_calls_result=None, model=None, - extra_content_blocks=None, + extra_user_content_parts=None, **kwargs, ) -> LLMResponse: if contexts is None: @@ -688,7 +688,7 @@ class ProviderGoogleGenAI(Provider): new_record = None if prompt is not None: new_record = await self.assemble_context( - prompt, image_urls, extra_content_blocks + prompt, image_urls, extra_user_content_parts ) context_query = self._ensure_message_to_dicts(contexts) if new_record: @@ -735,7 +735,7 @@ class ProviderGoogleGenAI(Provider): system_prompt=None, tool_calls_result=None, model=None, - extra_content_blocks=None, + extra_user_content_parts=None, **kwargs, ) -> AsyncGenerator[LLMResponse, None]: if contexts is None: @@ -743,7 +743,7 @@ class ProviderGoogleGenAI(Provider): new_record = None if prompt is not None: new_record = await self.assemble_context( - prompt, image_urls, extra_content_blocks + prompt, image_urls, extra_user_content_parts ) context_query = self._ensure_message_to_dicts(contexts) if new_record: @@ -807,7 +807,7 @@ class ProviderGoogleGenAI(Provider): self, text: str, image_urls: list[str] | None = None, - extra_content_blocks: list[dict] | None = None, + extra_user_content_parts: list[dict] | None = None, ): """组装上下文。""" # 构建内容块列表 @@ -819,13 +819,13 @@ class ProviderGoogleGenAI(Provider): elif image_urls: # 如果没有文本但有图片,添加占位文本 content_blocks.append({"type": "text", "text": "[图片]"}) - elif extra_content_blocks: + elif extra_user_content_parts: # 如果只有额外内容块,也需要添加占位文本 content_blocks.append({"type": "text", "text": " "}) # 2. 额外的内容块(系统提醒、指令等) - if extra_content_blocks: - content_blocks.extend(extra_content_blocks) + if extra_user_content_parts: + content_blocks.extend(extra_user_content_parts) # 3. 图片内容 if image_urls: @@ -851,7 +851,7 @@ class ProviderGoogleGenAI(Provider): # 如果只有主文本且没有额外内容块和图片,返回简单格式以保持向后兼容 if ( text - and not extra_content_blocks + and not extra_user_content_parts and not image_urls and len(content_blocks) == 1 and content_blocks[0]["type"] == "text" diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 8a9346cef..5a7baf53f 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -348,7 +348,7 @@ class ProviderOpenAIOfficial(Provider): system_prompt: str | None = None, tool_calls_result: ToolCallsResult | list[ToolCallsResult] | None = None, model: str | None = None, - extra_content_blocks: list[dict] | None = None, + extra_user_content_parts: list[dict] | None = None, **kwargs, ) -> tuple: """准备聊天所需的有效载荷和上下文""" @@ -357,7 +357,7 @@ class ProviderOpenAIOfficial(Provider): new_record = None if prompt is not None: new_record = await self.assemble_context( - prompt, image_urls, extra_content_blocks + prompt, image_urls, extra_user_content_parts ) context_query = self._ensure_message_to_dicts(contexts) if new_record: @@ -479,7 +479,7 @@ class ProviderOpenAIOfficial(Provider): system_prompt=None, tool_calls_result=None, model=None, - extra_content_blocks=None, + extra_user_content_parts=None, **kwargs, ) -> LLMResponse: payloads, context_query = await self._prepare_chat_payload( @@ -489,7 +489,7 @@ class ProviderOpenAIOfficial(Provider): system_prompt, tool_calls_result, model=model, - extra_content_blocks=extra_content_blocks, + extra_user_content_parts=extra_user_content_parts, **kwargs, ) @@ -629,7 +629,7 @@ class ProviderOpenAIOfficial(Provider): self, text: str, image_urls: list[str] | None = None, - extra_content_blocks: list[dict] | None = None, + extra_user_content_parts: list[dict] | None = None, ) -> dict: """组装成符合 OpenAI 格式的 role 为 user 的消息段""" # 构建内容块列表 @@ -641,13 +641,13 @@ class ProviderOpenAIOfficial(Provider): elif image_urls: # 如果没有文本但有图片,添加占位文本 content_blocks.append({"type": "text", "text": "[图片]"}) - elif extra_content_blocks: + elif extra_user_content_parts: # 如果只有额外内容块,也需要添加占位文本 content_blocks.append({"type": "text", "text": " "}) # 2. 额外的内容块(系统提醒、指令等) - if extra_content_blocks: - content_blocks.extend(extra_content_blocks) + if extra_user_content_parts: + content_blocks.extend(extra_user_content_parts) # 3. 图片内容 if image_urls: @@ -673,7 +673,7 @@ class ProviderOpenAIOfficial(Provider): # 如果只有主文本且没有额外内容块和图片,返回简单格式以保持向后兼容 if ( text - and not extra_content_blocks + and not extra_user_content_parts and not image_urls and len(content_blocks) == 1 and content_blocks[0]["type"] == "text" diff --git a/packages/astrbot/process_llm_request.py b/packages/astrbot/process_llm_request.py index f787970c7..28d0a34f4 100644 --- a/packages/astrbot/process_llm_request.py +++ b/packages/astrbot/process_llm_request.py @@ -7,6 +7,7 @@ from astrbot.api import logger, sp, star from astrbot.api.event import AstrMessageEvent from astrbot.api.message_components import Image, Reply from astrbot.api.provider import Provider, ProviderRequest +from astrbot.core.agent.message import TextPart from astrbot.core.provider.func_tool_manager import ToolSet @@ -85,11 +86,8 @@ class ProcessLLMRequest: req.image_urls, ) if caption: - req.extra_content_blocks.append( - { - "type": "text", - "text": f"{caption}", - } + req.extra_user_content_parts.append( + TextPart(text=f"{caption}") ) req.image_urls = [] except Exception as e: @@ -231,17 +229,17 @@ class ProcessLLMRequest: except BaseException as e: logger.error(f"处理引用图片失败: {e}") - # 3. 将所有部分组合成文本并添加到 extra_content_blocks 中 + # 3. 将所有部分组合成文本并添加到 extra_user_content_parts 中 # 确保引用内容被正确的标签包裹 quoted_content = "\n".join(content_parts) # 确保所有内容都在标签内 quoted_text = f"\n{quoted_content}\n" - req.extra_content_blocks.append({"type": "text", "text": quoted_text}) + req.extra_user_content_parts.append(TextPart(text=quoted_text)) # 统一包裹所有系统提醒 if system_parts: system_content = ( "" + "\n".join(system_parts) + "" ) - req.extra_content_blocks.append({"type": "text", "text": system_content}) + req.extra_user_content_parts.append(TextPart(text=system_content))