127 lines
4.4 KiB
Python
127 lines
4.4 KiB
Python
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
|