From 7c1dbecea584095e9d9d5f0fc84bf12356d7349b Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Fri, 26 Dec 2025 21:47:02 +0800 Subject: [PATCH] refactor: unify extra_user_content_parts type to ContentPart across providers and update related handling --- astrbot/core/agent/runners/tool_loop_agent_runner.py | 4 ++-- astrbot/core/provider/entities.py | 11 ++--------- astrbot/core/provider/provider.py | 4 ++-- astrbot/core/provider/sources/anthropic_source.py | 7 +++---- astrbot/core/provider/sources/gemini_source.py | 6 ++++-- astrbot/core/provider/sources/openai_source.py | 9 +++++---- 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/astrbot/core/agent/runners/tool_loop_agent_runner.py b/astrbot/core/agent/runners/tool_loop_agent_runner.py index 82ab11b05..88e302ad7 100644 --- a/astrbot/core/agent/runners/tool_loop_agent_runner.py +++ b/astrbot/core/agent/runners/tool_loop_agent_runner.py @@ -77,11 +77,11 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]): async def _iter_llm_responses(self) -> T.AsyncGenerator[LLMResponse, None]: """Yields chunks *and* a final LLMResponse.""" payload = { - "contexts": self.run_context.messages, + "contexts": self.run_context.messages, # list[Message] "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_user_content_parts": self.req.extra_user_content_parts, + "extra_user_content_parts": self.req.extra_user_content_parts, # list[ContentPart] } if self.streaming: diff --git a/astrbot/core/provider/entities.py b/astrbot/core/provider/entities.py index 5ddca16c9..8f1bc442e 100644 --- a/astrbot/core/provider/entities.py +++ b/astrbot/core/provider/entities.py @@ -93,9 +93,7 @@ class ProviderRequest: """会话 ID""" image_urls: list[str] = field(default_factory=list) """图片 URL 列表""" - extra_user_content_parts: list[dict] | list[ContentPart] = field( - default_factory=list - ) + extra_user_content_parts: list[ContentPart] = field(default_factory=list) """额外的用户消息内容部分列表,用于在用户消息后添加额外的内容块(如系统提醒、指令等)。支持 dict 或 ContentPart 对象""" func_tool: ToolSet | None = None """可用的函数工具""" @@ -184,12 +182,7 @@ class ProviderRequest: # 2. 额外的内容块(系统提醒、指令等) 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) + content_blocks.append(part.model_dump()) # 3. 图片内容 if self.image_urls: diff --git a/astrbot/core/provider/provider.py b/astrbot/core/provider/provider.py index fdcc6f238..6fb6d8953 100644 --- a/astrbot/core/provider/provider.py +++ b/astrbot/core/provider/provider.py @@ -4,7 +4,7 @@ import os from collections.abc import AsyncGenerator from typing import TypeAlias, Union -from astrbot.core.agent.message import Message +from astrbot.core.agent.message import ContentPart, Message from astrbot.core.agent.tool import ToolSet from astrbot.core.provider.entities import ( LLMResponse, @@ -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_user_content_parts: list[dict] | None = None, + extra_user_content_parts: list[ContentPart] | None = None, **kwargs, ) -> LLMResponse: """获得 LLM 的文本对话结果。会使用当前的模型进行对话。 diff --git a/astrbot/core/provider/sources/anthropic_source.py b/astrbot/core/provider/sources/anthropic_source.py index a4ad9e832..2552736d3 100644 --- a/astrbot/core/provider/sources/anthropic_source.py +++ b/astrbot/core/provider/sources/anthropic_source.py @@ -11,6 +11,7 @@ from anthropic.types.usage import Usage from astrbot import logger from astrbot.api.provider import Provider +from astrbot.core.agent.message import ContentPart from astrbot.core.provider.entities import LLMResponse, TokenUsage from astrbot.core.provider.func_tool_manager import ToolSet from astrbot.core.utils.io import download_image_by_url @@ -398,7 +399,7 @@ class ProviderAnthropic(Provider): self, text: str, image_urls: list[str] | None = None, - extra_user_content_parts: list[dict] | None = None, + extra_user_content_parts: list[ContentPart] | None = None, ): """组装上下文,支持文本和图片""" content = [] @@ -417,9 +418,7 @@ class ProviderAnthropic(Provider): if extra_user_content_parts: # 过滤出文本块,因为 Anthropic 主要支持文本和图片 text_blocks = [ - block - for block in extra_user_content_parts - if block.get("type") == "text" + block for block in extra_user_content_parts if block.type == "text" ] content.extend(text_blocks) diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index 0dfe048de..282e1289e 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -13,6 +13,7 @@ from google.genai.errors import APIError import astrbot.core.message.components as Comp from astrbot import logger from astrbot.api.provider import Provider +from astrbot.core.agent.message import ContentPart from astrbot.core.message.message_event_result import MessageChain from astrbot.core.provider.entities import LLMResponse, TokenUsage from astrbot.core.provider.func_tool_manager import ToolSet @@ -807,7 +808,7 @@ class ProviderGoogleGenAI(Provider): self, text: str, image_urls: list[str] | None = None, - extra_user_content_parts: list[dict] | None = None, + extra_user_content_parts: list[ContentPart] | None = None, ): """组装上下文。""" # 构建内容块列表 @@ -825,7 +826,8 @@ class ProviderGoogleGenAI(Provider): # 2. 额外的内容块(系统提醒、指令等) if extra_user_content_parts: - content_blocks.extend(extra_user_content_parts) + for part in extra_user_content_parts: + content_blocks.append(part.model_dump()) # 3. 图片内容 if image_urls: diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 5a7baf53f..df17b64b5 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -17,7 +17,7 @@ from openai.types.completion_usage import CompletionUsage import astrbot.core.message.components as Comp from astrbot import logger from astrbot.api.provider import Provider -from astrbot.core.agent.message import Message +from astrbot.core.agent.message import ContentPart, Message from astrbot.core.agent.tool import ToolSet from astrbot.core.message.message_event_result import MessageChain from astrbot.core.provider.entities import LLMResponse, TokenUsage, ToolCallsResult @@ -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_user_content_parts: list[dict] | None = None, + extra_user_content_parts: list[ContentPart] | None = None, **kwargs, ) -> tuple: """准备聊天所需的有效载荷和上下文""" @@ -629,7 +629,7 @@ class ProviderOpenAIOfficial(Provider): self, text: str, image_urls: list[str] | None = None, - extra_user_content_parts: list[dict] | None = None, + extra_user_content_parts: list[ContentPart] | None = None, ) -> dict: """组装成符合 OpenAI 格式的 role 为 user 的消息段""" # 构建内容块列表 @@ -647,7 +647,8 @@ class ProviderOpenAIOfficial(Provider): # 2. 额外的内容块(系统提醒、指令等) if extra_user_content_parts: - content_blocks.extend(extra_user_content_parts) + for part in extra_user_content_parts: + content_blocks.append(part.model_dump()) # 3. 图片内容 if image_urls: