fix:修复无法退出专注模式的问题,移除无用插件鸡肋
This commit is contained in:
@@ -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
|
|
||||||
@@ -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()
|
|
||||||
@@ -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",
|
|
||||||
]
|
|
||||||
@@ -588,12 +588,18 @@ class HeartFChatting:
|
|||||||
return False, "", ""
|
return False, "", ""
|
||||||
|
|
||||||
# 处理动作并获取结果
|
# 处理动作并获取结果
|
||||||
result = await action_handler.handle_action()
|
success, reply_text = await action_handler.handle_action()
|
||||||
if len(result) == 3:
|
if len(result) == 3:
|
||||||
success, reply_text, command = result
|
success, reply_text, command = result
|
||||||
else:
|
else:
|
||||||
success, reply_text = result
|
success, reply_text = result
|
||||||
command = ""
|
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}'")
|
logger.debug(f"{self.log_prefix} 麦麦执行了'{action}', 返回结果'{success}', '{reply_text}', '{command}'")
|
||||||
|
|
||||||
return success, reply_text, command
|
return success, reply_text, command
|
||||||
|
|||||||
@@ -287,13 +287,10 @@ class ExitFocusChatAction(BaseAction):
|
|||||||
logger.info(f"{self.log_prefix} 决定退出专注聊天: {self.reasoning}")
|
logger.info(f"{self.log_prefix} 决定退出专注聊天: {self.reasoning}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 转换状态 - 这里返回特殊的命令标识
|
# 标记状态切换请求
|
||||||
status_message = ""
|
|
||||||
|
|
||||||
# 通过返回值中的特殊标识来通知系统执行状态切换
|
|
||||||
# 系统会识别这个返回值并执行相应的状态切换逻辑
|
|
||||||
self._mark_state_change()
|
self._mark_state_change()
|
||||||
|
|
||||||
|
status_message = "决定退出专注聊天模式"
|
||||||
return True, status_message
|
return True, status_message
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -304,7 +301,7 @@ class ExitFocusChatAction(BaseAction):
|
|||||||
"""标记状态切换请求"""
|
"""标记状态切换请求"""
|
||||||
# 通过action_data传递状态切换命令
|
# 通过action_data传递状态切换命令
|
||||||
self.action_data["_system_command"] = "stop_focus_chat"
|
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
|
@register_plugin
|
||||||
|
|||||||
Reference in New Issue
Block a user