From 3725760b06b7ad37bc07cd8028946ab14f485d09 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 12 Jun 2025 20:54:43 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E9=80=80=E5=87=BA=E4=B8=93=E6=B3=A8=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E7=A7=BB=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E6=8F=92=E4=BB=B6=E9=B8=A1=E8=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/actions/base_action.py | 126 ------------------ src/chat/actions/plugin_action.py | 139 -------------------- src/chat/actions/plugin_api/__init__.py | 17 --- src/chat/focus_chat/heartFC_chat.py | 8 +- src/plugins/built_in/core_actions/plugin.py | 11 +- 5 files changed, 11 insertions(+), 290 deletions(-) delete mode 100644 src/chat/actions/base_action.py delete mode 100644 src/chat/actions/plugin_action.py delete mode 100644 src/chat/actions/plugin_api/__init__.py diff --git a/src/chat/actions/base_action.py b/src/chat/actions/base_action.py deleted file mode 100644 index 3bb132fae..000000000 --- a/src/chat/actions/base_action.py +++ /dev/null @@ -1,126 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Tuple, Dict, Type -from src.common.logger import get_logger - -logger = get_logger("base_action") - -# 全局动作注册表 -_ACTION_REGISTRY: Dict[str, Type["BaseAction"]] = {} -_DEFAULT_ACTIONS: Dict[str, str] = {} - - -# 动作激活类型枚举 -class ActionActivationType: - ALWAYS = "always" # 默认参与到planner - LLM_JUDGE = "llm_judge" # LLM判定是否启动该action到planner - RANDOM = "random" # 随机启用action到planner - KEYWORD = "keyword" # 关键词触发启用action到planner - - -# 聊天模式枚举 -class ChatMode: - FOCUS = "focus" # Focus聊天模式 - NORMAL = "normal" # Normal聊天模式 - ALL = "all" # 所有聊天模式 - - -def register_action(cls): - """ - 动作注册装饰器 - - 用法: - @register_action - class MyAction(BaseAction): - action_name = "my_action" - action_description = "我的动作" - focus_activation_type = ActionActivationType.ALWAYS - normal_activation_type = ActionActivationType.ALWAYS - mode_enable = ChatMode.ALL - parallel_action = False - ... - """ - # 检查类是否有必要的属性 - if not hasattr(cls, "action_name") or not hasattr(cls, "action_description"): - logger.error(f"动作类 {cls.__name__} 缺少必要的属性: action_name 或 action_description") - return cls - - action_name = cls.action_name - action_description = cls.action_description - is_enabled = getattr(cls, "enable_plugin", True) # 默认启用插件 - - if not action_name or not action_description: - logger.error(f"动作类 {cls.__name__} 的 action_name 或 action_description 为空") - return cls - - # 将动作类注册到全局注册表 - _ACTION_REGISTRY[action_name] = cls - - # 如果启用插件,添加到默认动作集 - if is_enabled: - _DEFAULT_ACTIONS[action_name] = action_description - - logger.info(f"已注册动作: {action_name} -> {cls.__name__},插件启用: {is_enabled}") - return cls - - -class BaseAction(ABC): - """动作基类接口 - - 所有具体的动作类都应该继承这个基类,并实现handle_action方法。 - """ - - def __init__(self, action_data: dict, reasoning: str, cycle_timers: dict, thinking_id: str): - """初始化动作 - - Args: - action_data: 动作数据 - reasoning: 执行该动作的理由 - cycle_timers: 计时器字典 - thinking_id: 思考ID - """ - # 每个动作必须实现 - self.action_name: str = "base_action" - self.action_description: str = "基础动作" - self.action_parameters: dict = {} - self.action_require: list[str] = [] - - # 动作激活类型设置 - # Focus模式下的激活类型,默认为always - self.focus_activation_type: str = ActionActivationType.ALWAYS - # Normal模式下的激活类型,默认为always - self.normal_activation_type: str = ActionActivationType.ALWAYS - - # 随机激活的概率(0.0-1.0),用于RANDOM激活类型 - self.random_activation_probability: float = 0.3 - # LLM判定的提示词,用于LLM_JUDGE激活类型 - self.llm_judge_prompt: str = "" - # 关键词触发列表,用于KEYWORD激活类型 - self.activation_keywords: list[str] = [] - # 关键词匹配是否区分大小写 - self.keyword_case_sensitive: bool = False - - # 模式启用设置:指定在哪些聊天模式下启用此动作 - # 可选值: "focus"(仅Focus模式), "normal"(仅Normal模式), "all"(所有模式) - self.mode_enable: str = ChatMode.ALL - - # 并行执行设置:仅在Normal模式下生效,设置为True的动作可以与回复动作并行执行 - # 而不是替代回复动作,适用于图片生成、TTS、禁言等不需要覆盖回复的动作 - self.parallel_action: bool = False - - self.associated_types: list[str] = [] - - self.enable_plugin: bool = True # 是否启用插件,默认启用 - - self.action_data = action_data - self.reasoning = reasoning - self.cycle_timers = cycle_timers - self.thinking_id = thinking_id - - @abstractmethod - async def handle_action(self) -> Tuple[bool, str]: - """处理动作的抽象方法,需要被子类实现 - - Returns: - Tuple[bool, str]: (是否执行成功, 回复文本) - """ - pass diff --git a/src/chat/actions/plugin_action.py b/src/chat/actions/plugin_action.py deleted file mode 100644 index cc715ad4f..000000000 --- a/src/chat/actions/plugin_action.py +++ /dev/null @@ -1,139 +0,0 @@ -from typing import Tuple, Dict, Any, Optional -from src.chat.actions.base_action import BaseAction, register_action, ActionActivationType, ChatMode # noqa F401 -from src.common.logger import get_logger -import os -import inspect -import toml # 导入 toml 库 -from abc import abstractmethod - -# 导入新插件系统的API模块 -from src.plugin_system.apis.message_api import MessageAPI -from src.plugin_system.apis.llm_api import LLMAPI -from src.plugin_system.apis.database_api import DatabaseAPI -from src.plugin_system.apis.config_api import ConfigAPI -from src.plugin_system.apis.utils_api import UtilsAPI -from src.plugin_system.apis.stream_api import StreamAPI -from src.plugin_system.apis.hearflow_api import HearflowAPI - -# 以下为类型注解需要 -from src.chat.message_receive.chat_stream import ChatStream # noqa -from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor # noqa -from src.chat.focus_chat.replyer.default_replyer import DefaultReplyer # noqa -from src.chat.focus_chat.info.obs_info import ObsInfo # noqa - -logger = get_logger("plugin_action") - - -class PluginAction(BaseAction, MessageAPI, LLMAPI, DatabaseAPI, ConfigAPI, UtilsAPI, StreamAPI, HearflowAPI): - """插件动作基类(旧版兼容) - - 封装了主程序内部依赖,提供简化的API接口给插件开发者 - - ⚠️ 此类已弃用,建议使用新的插件系统: - - 新基类:src.plugin_system.base.BaseAction - - 新API:src.plugin_system.plugin_api - - 新注册:@register_component 装饰器 - """ - - action_config_file_name: Optional[str] = None # 插件可以覆盖此属性来指定配置文件名 - - # 默认激活类型设置,插件可以覆盖 - focus_activation_type = ActionActivationType.ALWAYS - normal_activation_type = ActionActivationType.ALWAYS - random_activation_probability: float = 0.3 - llm_judge_prompt: str = "" - activation_keywords: list[str] = [] - keyword_case_sensitive: bool = False - - # 默认模式启用设置 - 插件动作默认在所有模式下可用,插件可以覆盖 - mode_enable = ChatMode.ALL - - def __init__( - self, - action_data: dict, - reasoning: str, - cycle_timers: dict, - thinking_id: str, - global_config: Optional[dict] = None, - **kwargs, - ): - """初始化插件动作基类""" - super().__init__(action_data, reasoning, cycle_timers, thinking_id) - - # 存储内部服务和对象引用 - self._services = {} - self.config: Dict[str, Any] = {} # 用于存储插件自身的配置 - - # 从kwargs提取必要的内部服务 - if "observations" in kwargs: - self._services["observations"] = kwargs["observations"] - if "expressor" in kwargs: - self._services["expressor"] = kwargs["expressor"] - if "chat_stream" in kwargs: - self._services["chat_stream"] = kwargs["chat_stream"] - if "replyer" in kwargs: - self._services["replyer"] = kwargs["replyer"] - - self.log_prefix = kwargs.get("log_prefix", "") - self._load_plugin_config() # 初始化时加载插件配置 - - def _load_plugin_config(self): - """ - 加载插件自身的配置文件。 - 配置文件应与插件模块在同一目录下。 - 插件可以通过覆盖 `action_config_file_name` 类属性来指定文件名。 - 如果 `action_config_file_name` 未指定,则不加载配置。 - 仅支持 TOML (.toml) 格式。 - """ - if not self.action_config_file_name: - logger.debug( - f"{self.log_prefix} 插件 {self.__class__.__name__} 未指定 action_config_file_name,不加载插件配置。" - ) - return - - try: - plugin_module_path = inspect.getfile(self.__class__) - plugin_dir = os.path.dirname(plugin_module_path) - config_file_path = os.path.join(plugin_dir, self.action_config_file_name) - - if not os.path.exists(config_file_path): - logger.warning( - f"{self.log_prefix} 插件 {self.__class__.__name__} 的配置文件 {config_file_path} 不存在。" - ) - return - - file_ext = os.path.splitext(self.action_config_file_name)[1].lower() - - if file_ext == ".toml": - with open(config_file_path, "r", encoding="utf-8") as f: - self.config = toml.load(f) or {} - logger.info(f"{self.log_prefix} 插件 {self.__class__.__name__} 的配置已从 {config_file_path} 加载。") - else: - logger.warning( - f"{self.log_prefix} 不支持的插件配置文件格式: {file_ext}。仅支持 .toml。插件配置未加载。" - ) - self.config = {} # 确保未加载时为空字典 - return - - except Exception as e: - logger.error( - f"{self.log_prefix} 加载插件 {self.__class__.__name__} 的配置文件 {self.action_config_file_name} 时出错: {e}" - ) - self.config = {} # 出错时确保 config 是一个空字典 - - @abstractmethod - async def process(self) -> Tuple[bool, str]: - """插件处理逻辑,子类必须实现此方法 - - Returns: - Tuple[bool, str]: (是否执行成功, 回复文本) - """ - pass - - async def handle_action(self) -> Tuple[bool, str]: - """实现BaseAction的抽象方法,调用子类的process方法 - - Returns: - Tuple[bool, str]: (是否执行成功, 回复文本) - """ - return await self.process() diff --git a/src/chat/actions/plugin_api/__init__.py b/src/chat/actions/plugin_api/__init__.py deleted file mode 100644 index db85ee2f2..000000000 --- a/src/chat/actions/plugin_api/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from src.chat.actions.plugin_api.message_api import MessageAPI -from src.chat.actions.plugin_api.llm_api import LLMAPI -from src.chat.actions.plugin_api.database_api import DatabaseAPI -from src.chat.actions.plugin_api.config_api import ConfigAPI -from src.chat.actions.plugin_api.utils_api import UtilsAPI -from src.chat.actions.plugin_api.stream_api import StreamAPI -from src.chat.actions.plugin_api.hearflow_api import HearflowAPI - -__all__ = [ - "MessageAPI", - "LLMAPI", - "DatabaseAPI", - "ConfigAPI", - "UtilsAPI", - "StreamAPI", - "HearflowAPI", -] diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index 98e43cf49..ec452dafc 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -588,12 +588,18 @@ class HeartFChatting: return False, "", "" # 处理动作并获取结果 - result = await action_handler.handle_action() + success, reply_text = await action_handler.handle_action() if len(result) == 3: success, reply_text, command = result else: success, reply_text = result command = "" + + # 检查action_data中是否有系统命令,优先使用系统命令 + if "_system_command" in action_data: + command = action_data["_system_command"] + logger.debug(f"{self.log_prefix} 从action_data中获取系统命令: {command}") + logger.debug(f"{self.log_prefix} 麦麦执行了'{action}', 返回结果'{success}', '{reply_text}', '{command}'") return success, reply_text, command diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 734308471..b575c0306 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -287,13 +287,10 @@ class ExitFocusChatAction(BaseAction): logger.info(f"{self.log_prefix} 决定退出专注聊天: {self.reasoning}") try: - # 转换状态 - 这里返回特殊的命令标识 - status_message = "" - - # 通过返回值中的特殊标识来通知系统执行状态切换 - # 系统会识别这个返回值并执行相应的状态切换逻辑 + # 标记状态切换请求 self._mark_state_change() - + + status_message = "决定退出专注聊天模式" return True, status_message except Exception as e: @@ -304,7 +301,7 @@ class ExitFocusChatAction(BaseAction): """标记状态切换请求""" # 通过action_data传递状态切换命令 self.action_data["_system_command"] = "stop_focus_chat" - logger.debug(f"{self.log_prefix} 已标记状态切换命令: stop_focus_chat") + logger.info(f"{self.log_prefix} 已标记状态切换命令: stop_focus_chat") @register_plugin