From c4a0cc19f8dac531da698fbbfe057476687c8586 Mon Sep 17 00:00:00 2001 From: DrSmoothl <1787882683@qq.com> Date: Sat, 14 Mar 2026 00:52:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=9C=8D=E5=8A=A1=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=EF=BC=8C=E4=BC=98=E5=8C=96=E6=B6=88=E6=81=AF=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A7=BB=E9=99=A4=E5=86=97?= =?UTF-8?q?=E4=BD=99=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=9B=B4=E6=96=B0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=A8=A1=E5=9E=8B=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/brain_chat/brain_planner.py | 20 +++---- src/plugin_runtime/capabilities/core.py | 59 +++++++----------- src/plugin_runtime/capabilities/data.py | 57 ++++++++++-------- src/plugin_runtime/capabilities/registry.py | 2 - src/services/config_service.py | 66 --------------------- src/services/message_service.py | 52 ++++++---------- src/services/send_service.py | 28 +-------- 7 files changed, 82 insertions(+), 202 deletions(-) delete mode 100644 src/services/config_service.py diff --git a/src/chat/brain_chat/brain_planner.py b/src/chat/brain_chat/brain_planner.py index 2aa9b293..12b103a0 100644 --- a/src/chat/brain_chat/brain_planner.py +++ b/src/chat/brain_chat/brain_planner.py @@ -13,9 +13,9 @@ from src.config.config import global_config, model_config from src.common.logger import get_logger from src.chat.logger.plan_reply_logger import PlanReplyLogger from src.common.data_models.info_data_model import ActionPlannerInfo +from src.common.utils.utils_action import ActionUtils from src.prompt.prompt_manager import prompt_manager from src.services.message_service import ( - build_readable_actions, build_readable_messages_with_id, get_actions_by_timestamp_with_chat, get_messages_before_time_in_chat, @@ -28,7 +28,7 @@ from src.core.component_registry import component_registry if TYPE_CHECKING: from src.common.data_models.info_data_model import TargetPersonInfo - from src.common.data_models.database_data_model import DatabaseMessages + from src.chat.message_receive.message import SessionMessage logger = get_logger("planner") @@ -51,8 +51,8 @@ class BrainPlanner: self.plan_log: List[Tuple[str, float, List[ActionPlannerInfo]]] = [] def find_message_by_id( - self, message_id: str, message_id_list: List[Tuple[str, "DatabaseMessages"]] - ) -> Optional["DatabaseMessages"]: + self, message_id: str, message_id_list: List[Tuple[str, "SessionMessage"]] + ) -> Optional["SessionMessage"]: # sourcery skip: use-next """ 根据message_id从message_id_list中查找对应的原始消息 @@ -72,7 +72,7 @@ class BrainPlanner: def _parse_single_action( self, action_json: dict, - message_id_list: List[Tuple[str, "DatabaseMessages"]], + message_id_list: List[Tuple[str, "SessionMessage"]], current_available_actions: List[Tuple[str, ActionInfo]], ) -> List[ActionPlannerInfo]: """解析单个action JSON并返回ActionPlannerInfo列表""" @@ -169,7 +169,7 @@ class BrainPlanner: limit=int(global_config.chat.max_context_size * 0.6), filter_intercept_message_level=1, ) - message_id_list: list[Tuple[str, "DatabaseMessages"]] = [] + message_id_list: list[Tuple[str, "SessionMessage"]] = [] chat_content_block, message_id_list = build_readable_messages_with_id( messages=message_list_before_now, timestamp_mode="normal_no_YMD", @@ -251,11 +251,11 @@ class BrainPlanner: self, chat_target_info: Optional["TargetPersonInfo"], current_available_actions: Dict[str, ActionInfo], - message_id_list: List[Tuple[str, "DatabaseMessages"]], + message_id_list: List[Tuple[str, "SessionMessage"]], chat_content_block: str = "", interest: str = "", prompt_key: str = "brain_planner", - ) -> tuple[str, List[Tuple[str, "DatabaseMessages"]]]: + ) -> tuple[str, List[Tuple[str, "SessionMessage"]]]: """构建 Planner LLM 的提示词 (获取模板并填充数据)""" try: # 获取最近执行过的动作 @@ -265,7 +265,7 @@ class BrainPlanner: timestamp_end=time.time(), limit=6, ) - actions_before_now_block = build_readable_actions(actions=actions_before_now) + actions_before_now_block = ActionUtils.build_readable_action_records(actions_before_now) if actions_before_now_block: actions_before_now_block = f"你刚刚选择并执行过的action是:\n{actions_before_now_block}" else: @@ -395,7 +395,7 @@ class BrainPlanner: async def _execute_main_planner( self, prompt: str, - message_id_list: List[Tuple[str, "DatabaseMessages"]], + message_id_list: List[Tuple[str, "SessionMessage"]], filtered_actions: Dict[str, ActionInfo], available_actions: Dict[str, ActionInfo], loop_start_time: float, diff --git a/src/plugin_runtime/capabilities/core.py b/src/plugin_runtime/capabilities/core.py index bea5557b..8bff6d00 100644 --- a/src/plugin_runtime/capabilities/core.py +++ b/src/plugin_runtime/capabilities/core.py @@ -1,10 +1,26 @@ from typing import Any, Dict +from src.config.config import global_config from src.common.logger import get_logger logger = get_logger("plugin_runtime.integration") +def _get_nested_config_value(source: Any, key: str, default: Any = None) -> Any: + current = source + try: + for part in key.split("."): + if isinstance(current, dict) and part in current: + current = current[part] + elif hasattr(current, part): + current = getattr(current, part) + else: + raise KeyError(part) + return current + except Exception: + return default + + class RuntimeCoreCapabilityMixin: async def _cap_send_text(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: from src.services import send_service as send_api @@ -74,8 +90,9 @@ class RuntimeCoreCapabilityMixin: return {"success": False, "error": "缺少必要参数 command 或 stream_id"} try: - result = await send_api.command_to_stream( - command=command, + result = await send_api.custom_to_stream( + message_type="command", + content=command, stream_id=stream_id, storage_message=args.get("storage_message", True), display_message=args.get("display_message", ""), @@ -110,36 +127,6 @@ class RuntimeCoreCapabilityMixin: logger.error(f"[cap.send.custom] 执行失败: {e}", exc_info=True) return {"success": False, "error": str(e)} - async def _cap_send_forward(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import send_service as send_api - - messages = args.get("messages", []) - stream_id: str = args.get("stream_id", "") - if not messages or not stream_id: - return {"success": False, "error": "缺少必要参数 messages 或 stream_id"} - - try: - result = await send_api.forward_to_stream(messages=messages, stream_id=stream_id) - return {"success": result} - except Exception as e: - logger.error(f"[cap.send.forward] 执行失败: {e}", exc_info=True) - return {"success": False, "error": str(e)} - - async def _cap_send_hybrid(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import send_service as send_api - - segments = args.get("segments", []) - stream_id: str = args.get("stream_id", "") - if not segments or not stream_id: - return {"success": False, "error": "缺少必要参数 segments 或 stream_id"} - - try: - result = await send_api.hybrid_to_stream(segments=segments, stream_id=stream_id) - return {"success": result} - except Exception as e: - logger.error(f"[cap.send.hybrid] 执行失败: {e}", exc_info=True) - return {"success": False, "error": str(e)} - async def _cap_llm_generate(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: from src.services import llm_service as llm_api @@ -235,15 +222,13 @@ class RuntimeCoreCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_config_get(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import config_service as config_api - key: str = args.get("key", "") default = args.get("default") if not key: return {"success": False, "value": None, "error": "缺少必要参数 key"} try: - value = config_api.get_global_config(key, default) + value = _get_nested_config_value(global_config, key, default) return {"success": True, "value": value} except Exception as e: return {"success": False, "value": None, "error": str(e)} @@ -261,9 +246,7 @@ class RuntimeCoreCapabilityMixin: return {"success": False, "value": default, "error": f"未找到插件 {plugin_name} 的配置"} if key: - from src.services import config_service as config_api - - value = config_api.get_plugin_config(config, key, default) + value = _get_nested_config_value(config, key, default) return {"success": True, "value": value} return {"success": True, "value": config} diff --git a/src/plugin_runtime/capabilities/data.py b/src/plugin_runtime/capabilities/data.py index a4f34b00..b77a3de1 100644 --- a/src/plugin_runtime/capabilities/data.py +++ b/src/plugin_runtime/capabilities/data.py @@ -34,7 +34,7 @@ class RuntimeDataCapabilityMixin: return EMOJI_DIR / f"emoji_cap_{int(time.time() * 1000000)}.png" async def _cap_database_query(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import database_service as database_api + from src.services import database_service model_name: str = args.get("model_name", "") if not model_name: @@ -49,7 +49,7 @@ class RuntimeDataCapabilityMixin: query_type = args.get("query_type", "get") if query_type == "get": - result = await database_api.db_get( + result = await database_service.db_get( model_class=model_class, filters=args.get("filters"), limit=args.get("limit"), @@ -59,19 +59,19 @@ class RuntimeDataCapabilityMixin: elif query_type == "create": if not (data := args.get("data")): return {"success": False, "error": "create 需要 data"} - result = await database_api.db_save(model_class=model_class, data=data) + result = await database_service.db_save(model_class=model_class, data=data) elif query_type == "update": if not (data := args.get("data")): return {"success": False, "error": "update 需要 data"} - result = await database_api.db_update( + result = await database_service.db_update( model_class=model_class, data=data, filters=args.get("filters"), ) elif query_type == "delete": - result = await database_api.db_delete(model_class=model_class, filters=args.get("filters")) + result = await database_service.db_delete(model_class=model_class, filters=args.get("filters")) elif query_type == "count": - result = await database_api.db_count(model_class=model_class, filters=args.get("filters")) + result = await database_service.db_count(model_class=model_class, filters=args.get("filters")) else: return {"success": False, "error": f"不支持的 query_type: {query_type}"} return {"success": True, "result": result} @@ -80,7 +80,7 @@ class RuntimeDataCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_database_save(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import database_service as database_api + from src.services import database_service model_name: str = args.get("model_name", "") data: Optional[Dict[str, Any]] = args.get("data") @@ -94,7 +94,7 @@ class RuntimeDataCapabilityMixin: if model_class is None: return {"success": False, "error": f"未找到数据模型: {model_name}"} - result = await database_api.db_save( + result = await database_service.db_save( model_class=model_class, data=data, key_field=args.get("key_field"), @@ -106,7 +106,7 @@ class RuntimeDataCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_database_get(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import database_service as database_api + from src.services import database_service model_name: str = args.get("model_name", "") if not model_name: @@ -119,7 +119,7 @@ class RuntimeDataCapabilityMixin: if model_class is None: return {"success": False, "error": f"未找到数据模型: {model_name}"} - result = await database_api.db_get( + result = await database_service.db_get( model_class=model_class, filters=args.get("filters"), limit=args.get("limit"), @@ -132,7 +132,7 @@ class RuntimeDataCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_database_delete(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import database_service as database_api + from src.services import database_service model_name: str = args.get("model_name", "") filters = args.get("filters", {}) @@ -148,14 +148,14 @@ class RuntimeDataCapabilityMixin: if model_class is None: return {"success": False, "error": f"未找到数据模型: {model_name}"} - result = await database_api.db_delete(model_class=model_class, filters=filters) + result = await database_service.db_delete(model_class=model_class, filters=filters) return {"success": True, "result": result} except Exception as e: logger.error(f"[cap.database.delete] 执行失败: {e}", exc_info=True) return {"success": False, "error": str(e)} async def _cap_database_count(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import database_service as database_api + from src.services import database_service model_name: str = args.get("model_name", "") if not model_name: @@ -168,7 +168,7 @@ class RuntimeDataCapabilityMixin: if model_class is None: return {"success": False, "error": f"未找到数据模型: {model_name}"} - result = await database_api.db_count(model_class=model_class, filters=args.get("filters")) + result = await database_service.db_count(model_class=model_class, filters=args.get("filters")) return {"success": True, "count": result} except Exception as e: logger.error(f"[cap.database.count] 执行失败: {e}", exc_info=True) @@ -272,10 +272,10 @@ class RuntimeDataCapabilityMixin: return result async def _cap_message_get_by_time(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import message_service as message_api + from src.services import message_service try: - messages = message_api.get_messages_by_time( + messages = message_service.get_messages_by_time( start_time=float(args.get("start_time", 0.0)), end_time=float(args.get("end_time", 0.0)), limit=args.get("limit", 0), @@ -288,14 +288,14 @@ class RuntimeDataCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_message_get_by_time_in_chat(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import message_service as message_api + from src.services import message_service chat_id: str = args.get("chat_id", "") if not chat_id: return {"success": False, "error": "缺少必要参数 chat_id"} try: - messages = message_api.get_messages_by_time_in_chat( + messages = message_service.get_messages_by_time_in_chat( chat_id=chat_id, start_time=float(args.get("start_time", 0.0)), end_time=float(args.get("end_time", 0.0)), @@ -310,16 +310,21 @@ class RuntimeDataCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_message_get_recent(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import message_service as message_api + from src.services import message_service chat_id: str = args.get("chat_id", "") if not chat_id: return {"success": False, "error": "缺少必要参数 chat_id"} try: - messages = message_api.get_recent_messages( + hours = float(args.get("hours", 24.0)) + if hours < 0: + return {"success": False, "error": "hours 不能是负数"} + current_time = time.time() + messages = message_service.get_messages_by_time_in_chat( chat_id=chat_id, - hours=float(args.get("hours", 24.0)), + start_time=current_time - hours * 3600, + end_time=current_time, limit=args.get("limit", 100), limit_mode=args.get("limit_mode", "latest"), filter_mai=args.get("filter_mai", False), @@ -330,7 +335,7 @@ class RuntimeDataCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_message_count_new(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import message_service as message_api + from src.services import message_service chat_id: str = args.get("chat_id", "") if not chat_id: @@ -339,7 +344,7 @@ class RuntimeDataCapabilityMixin: try: since = args.get("since") start_time = float(since) if since is not None else float(args.get("start_time", 0.0)) - count = message_api.count_new_messages( + count = message_service.count_new_messages( chat_id=chat_id, start_time=start_time, end_time=args.get("end_time"), @@ -350,21 +355,21 @@ class RuntimeDataCapabilityMixin: return {"success": False, "error": str(e)} async def _cap_message_build_readable(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any: - from src.services import message_service as message_api + from src.services import message_service try: messages = args.get("messages") if messages is None: if not (chat_id := args.get("chat_id", "")): return {"success": False, "error": "缺少必要参数: messages 或 chat_id"} - messages = message_api.get_messages_by_time_in_chat( + messages = message_service.get_messages_by_time_in_chat( chat_id=chat_id, start_time=float(args.get("start_time", 0.0)), end_time=float(args.get("end_time", 0.0)), limit=args.get("limit", 0), ) - readable = message_api.build_readable_messages( + readable = message_service.build_readable_messages( messages=messages, replace_bot_name=args.get("replace_bot_name", True), timestamp_mode=args.get("timestamp_mode", "relative"), diff --git a/src/plugin_runtime/capabilities/registry.py b/src/plugin_runtime/capabilities/registry.py index 7890b7e4..abce97dc 100644 --- a/src/plugin_runtime/capabilities/registry.py +++ b/src/plugin_runtime/capabilities/registry.py @@ -18,8 +18,6 @@ def register_capability_impls(manager: "PluginRuntimeManager", supervisor: Plugi cap_service.register_capability("send.image", manager._cap_send_image) cap_service.register_capability("send.command", manager._cap_send_command) cap_service.register_capability("send.custom", manager._cap_send_custom) - cap_service.register_capability("send.forward", manager._cap_send_forward) - cap_service.register_capability("send.hybrid", manager._cap_send_hybrid) cap_service.register_capability("llm.generate", manager._cap_llm_generate) cap_service.register_capability("llm.generate_with_tools", manager._cap_llm_generate_with_tools) diff --git a/src/services/config_service.py b/src/services/config_service.py deleted file mode 100644 index 5cca332e..00000000 --- a/src/services/config_service.py +++ /dev/null @@ -1,66 +0,0 @@ -"""配置服务模块 - -提供配置读取的核心功能。 -""" - -from typing import Any - -from src.common.logger import get_logger -from src.config.config import global_config - -logger = get_logger("config_service") - - -def get_global_config(key: str, default: Any = None) -> Any: - """ - 安全地从全局配置中获取一个值。 - - Args: - key: 命名空间式配置键名,使用嵌套访问,如 "section.subsection.key",大小写敏感 - default: 如果配置不存在时返回的默认值 - - Returns: - Any: 配置值或默认值 - """ - keys = key.split(".") - current = global_config - - try: - for k in keys: - if hasattr(current, k): - current = getattr(current, k) - else: - raise KeyError(f"配置中不存在子空间或键 '{k}'") - return current - except Exception as e: - logger.warning(f"[ConfigService] 获取全局配置 {key} 失败: {e}") - return default - - -def get_plugin_config(plugin_config: dict, key: str, default: Any = None) -> Any: - """ - 从插件配置中获取值,支持嵌套键访问 - - Args: - plugin_config: 插件配置字典 - key: 配置键名,支持嵌套访问如 "section.subsection.key",大小写敏感 - default: 如果配置不存在时返回的默认值 - - Returns: - Any: 配置值或默认值 - """ - keys = key.split(".") - current = plugin_config - - try: - for k in keys: - if isinstance(current, dict) and k in current: - current = current[k] - elif hasattr(current, k): - current = getattr(current, k) - else: - raise KeyError(f"配置中不存在子空间或键 '{k}'") - return current - except Exception as e: - logger.warning(f"[ConfigService] 获取插件配置 {key} 失败: {e}") - return default diff --git a/src/services/message_service.py b/src/services/message_service.py index 966d8709..cdf9ff56 100644 --- a/src/services/message_service.py +++ b/src/services/message_service.py @@ -154,22 +154,6 @@ def get_messages_before_time_in_chat( return _normalize_messages(messages) -def get_recent_messages( - chat_id: str, hours: float = 24.0, limit: int = 100, limit_mode: str = "latest", filter_mai: bool = False -) -> List[SessionMessage]: - if not isinstance(hours, (int, float)) or hours < 0: - raise ValueError("hours 不能是负数") - if not isinstance(limit, int) or limit < 0: - raise ValueError("limit 必须是非负整数") - if not chat_id: - raise ValueError("chat_id 不能为空") - if not isinstance(chat_id, str): - raise ValueError("chat_id 必须是字符串类型") - now = time.time() - start_time = now - hours * 3600 - return get_messages_by_time_in_chat(chat_id, start_time, now, limit, limit_mode, filter_mai) - - # ============================================================================= # 消息计数函数 # ============================================================================= @@ -221,14 +205,14 @@ def build_readable_messages( line = f"{line[:200]}......(内容太长了)" lines.append(line) if show_actions and normalized_messages: - action_lines = build_readable_actions( + if action_lines := ActionUtils.build_readable_action_records( get_actions_by_timestamp_with_chat( normalized_messages[0].session_id, normalized_messages[0].timestamp.timestamp(), normalized_messages[-1].timestamp.timestamp(), - ) - ) - if action_lines: + ), + "relative", + ): lines.append(action_lines) return "\n".join(lines) @@ -260,19 +244,24 @@ def build_readable_messages_with_id( lines.append(line) message_id_list.append((message.message_id, message)) if show_actions and normalized_messages: - action_lines = build_readable_actions( + if action_lines := ActionUtils.build_readable_action_records( get_actions_by_timestamp_with_chat( normalized_messages[0].session_id, normalized_messages[0].timestamp.timestamp(), normalized_messages[-1].timestamp.timestamp(), - ) - ) - if action_lines: + ), + "relative", + ): lines.append(action_lines) return "\n".join(lines), message_id_list -def get_actions_by_timestamp_with_chat(chat_id: str, timestamp_start: float, timestamp_end: float) -> List[MaiActionRecord]: +def get_actions_by_timestamp_with_chat( + chat_id: str, + timestamp_start: float, + timestamp_end: float, + limit: Optional[int] = None, +) -> List[MaiActionRecord]: with get_db_session() as session: statement = ( select(ActionRecord) @@ -281,13 +270,11 @@ def get_actions_by_timestamp_with_chat(chat_id: str, timestamp_start: float, tim .where(col(ActionRecord.timestamp) <= datetime.fromtimestamp(timestamp_end)) .order_by(col(ActionRecord.timestamp)) ) + if limit is not None: + statement = statement.limit(limit) return [MaiActionRecord.from_db_instance(item) for item in session.exec(statement).all()] -def build_readable_actions(actions: List[MaiActionRecord], timestamp_mode: str = "relative") -> str: - return ActionUtils.build_readable_action_records(actions, timestamp_mode) - - def replace_user_references(text: str, platform: str, replace_bot_name: bool = False) -> str: del platform if not text: @@ -312,9 +299,4 @@ def translate_pid_to_description(pid: str) -> str: else None ) image = session.exec(statement).first() if statement is not None else None - description = "" - if image and image.description and image.description.strip(): - description = image.description.strip() - else: - description = "[图片]" - return description + return image.description.strip() if image and image.description and image.description.strip() else "[图片]" diff --git a/src/services/send_service.py b/src/services/send_service.py index 76521283..c6885658 100644 --- a/src/services/send_service.py +++ b/src/services/send_service.py @@ -4,9 +4,9 @@ 提供发送各种类型消息的核心功能。 """ -import traceback import time -from typing import Optional, Union, Dict, List, TYPE_CHECKING +import traceback +from typing import Dict, List, Optional, TYPE_CHECKING from maim_message import BaseMessageInfo, GroupInfo as MaimGroupInfo, MessageBase, Seg, UserInfo as MaimUserInfo @@ -61,7 +61,7 @@ async def _send_to_target( anchor_message: Optional[MaiMessage] = None if reply_message: - anchor_message = db_message_to_mai_message(reply_message) + anchor_message = reply_message.deepcopy() if anchor_message: logger.debug( f"[SendService] 找到匹配的回复消息,发送者: {anchor_message.message_info.user_info.user_id}" @@ -127,11 +127,6 @@ async def _send_to_target( return False -def db_message_to_mai_message(message_obj: "SessionMessage") -> Optional[MaiMessage]: - """将数据库消息重建为 MaiMessage 对象,用于回复引用。""" - return message_obj.deepcopy() - - # ============================================================================= # 公共函数 - 预定义类型的发送函数 # ============================================================================= @@ -197,23 +192,6 @@ async def image_to_stream( ) -async def command_to_stream( - command: Union[str, dict], - stream_id: str, - storage_message: bool = True, - display_message: str = "", -) -> bool: - """向指定流发送命令""" - return await _send_to_target( - message_segment=Seg(type="command", data=command), # type: ignore - stream_id=stream_id, - display_message=display_message, - typing=False, - storage_message=storage_message, - set_reply=False, - ) - - async def custom_to_stream( message_type: str, content: str | Dict,