Feat:添加对Action插件的支持,现在可以编写插件

This commit is contained in:
SengokuCola
2025-05-16 00:43:46 +08:00
parent ac6f96f805
commit cda9879bb2
28 changed files with 934 additions and 662 deletions

View File

@@ -0,0 +1,319 @@
from typing import Dict, List, Optional, Callable, Coroutine, Type, Any
from src.chat.focus_chat.planners.actions.base_action import BaseAction, _ACTION_REGISTRY
from src.chat.heart_flow.observation.observation import Observation
from src.chat.focus_chat.expressors.default_expressor import DefaultExpressor
from src.chat.message_receive.chat_stream import ChatStream
from src.chat.focus_chat.heartFC_Cycleinfo import CycleDetail
from src.common.logger_manager import get_logger
import importlib
import pkgutil
import os
# 导入动作类,确保装饰器被执行
import src.chat.focus_chat.planners.actions # noqa
logger = get_logger("action_manager")
# 定义动作信息类型
ActionInfo = Dict[str, Any]
class ActionManager:
"""
动作管理器,用于管理各种类型的动作
"""
def __init__(self):
"""初始化动作管理器"""
# 所有注册的动作集合
self._registered_actions: Dict[str, ActionInfo] = {}
# 当前正在使用的动作集合,默认加载默认动作
self._using_actions: Dict[str, ActionInfo] = {}
# 临时备份原始使用中的动作
self._original_actions_backup: Optional[Dict[str, ActionInfo]] = None
# 默认动作集,仅作为快照,用于恢复默认
self._default_actions: Dict[str, ActionInfo] = {}
# 加载所有已注册动作
self._load_registered_actions()
# 加载插件动作
self._load_plugin_actions()
# 初始化时将默认动作加载到使用中的动作
self._using_actions = self._default_actions.copy()
def _load_registered_actions(self) -> None:
"""
加载所有通过装饰器注册的动作
"""
try:
# 从_ACTION_REGISTRY获取所有已注册动作
for action_name, action_class in _ACTION_REGISTRY.items():
# 获取动作相关信息
# 不读取插件动作和基类
if action_name == "base_action" or action_name == "plugin_action":
continue
action_description: str = getattr(action_class, "action_description", "")
action_parameters: dict[str:str] = getattr(action_class, "action_parameters", {})
action_require: list[str] = getattr(action_class, "action_require", [])
is_default: bool = getattr(action_class, "default", False)
if action_name and action_description:
# 创建动作信息字典
action_info = {
"description": action_description,
"parameters": action_parameters,
"require": action_require,
}
# 添加到所有已注册的动作
self._registered_actions[action_name] = action_info
# 添加到默认动作(如果是默认动作)
if is_default:
self._default_actions[action_name] = action_info
logger.info(f"所有注册动作: {list(self._registered_actions.keys())}")
logger.info(f"默认动作: {list(self._default_actions.keys())}")
for action_name, action_info in self._default_actions.items():
logger.info(f"动作名称: {action_name}, 动作信息: {action_info}")
except Exception as e:
logger.error(f"加载已注册动作失败: {e}")
def _load_plugin_actions(self) -> None:
"""
加载所有插件目录中的动作
"""
try:
# 检查插件目录是否存在
plugin_path = "src.plugins"
plugin_dir = plugin_path.replace('.', os.path.sep)
if not os.path.exists(plugin_dir):
logger.info(f"插件目录 {plugin_dir} 不存在,跳过插件动作加载")
return
# 导入插件包
try:
plugins_package = importlib.import_module(plugin_path)
except ImportError as e:
logger.error(f"导入插件包失败: {e}")
return
# 遍历插件包中的所有子包
for _, plugin_name, is_pkg in pkgutil.iter_modules(plugins_package.__path__, plugins_package.__name__ + '.'):
if not is_pkg:
continue
# 检查插件是否有actions子包
plugin_actions_path = f"{plugin_name}.actions"
try:
# 尝试导入插件的actions包
importlib.import_module(plugin_actions_path)
logger.info(f"成功加载插件动作模块: {plugin_actions_path}")
except ImportError as e:
logger.debug(f"插件 {plugin_name} 没有actions子包或导入失败: {e}")
continue
# 再次从_ACTION_REGISTRY获取所有动作包括刚刚从插件加载的
self._load_registered_actions()
except Exception as e:
logger.error(f"加载插件动作失败: {e}")
def create_action(
self,
action_name: str,
action_data: dict,
reasoning: str,
cycle_timers: dict,
thinking_id: str,
observations: List[Observation],
expressor: DefaultExpressor,
chat_stream: ChatStream,
current_cycle: CycleDetail,
log_prefix: str,
on_consecutive_no_reply_callback: Callable[[], Coroutine[None, None, None]],
# total_no_reply_count: int = 0,
# total_waiting_time: float = 0.0,
shutting_down: bool = False,
) -> Optional[BaseAction]:
"""
创建动作处理器实例
Args:
action_name: 动作名称
action_data: 动作数据
reasoning: 执行理由
cycle_timers: 计时器字典
thinking_id: 思考ID
observations: 观察列表
expressor: 表达器
chat_stream: 聊天流
current_cycle: 当前循环信息
log_prefix: 日志前缀
on_consecutive_no_reply_callback: 连续不回复回调
total_no_reply_count: 连续不回复计数
total_waiting_time: 累计等待时间
shutting_down: 是否正在关闭
Returns:
Optional[BaseAction]: 创建的动作处理器实例如果动作名称未注册则返回None
"""
# 检查动作是否在当前使用的动作集中
if action_name not in self._using_actions:
logger.warning(f"当前不可用的动作类型: {action_name}")
return None
handler_class = _ACTION_REGISTRY.get(action_name)
if not handler_class:
logger.warning(f"未注册的动作类型: {action_name}")
return None
try:
# 创建动作实例
instance = handler_class(
action_name=action_name,
action_data=action_data,
reasoning=reasoning,
cycle_timers=cycle_timers,
thinking_id=thinking_id,
observations=observations,
expressor=expressor,
chat_stream=chat_stream,
current_cycle=current_cycle,
log_prefix=log_prefix,
on_consecutive_no_reply_callback=on_consecutive_no_reply_callback,
# total_no_reply_count=total_no_reply_count,
# total_waiting_time=total_waiting_time,
shutting_down=shutting_down,
)
return instance
except Exception as e:
logger.error(f"创建动作处理器实例失败: {e}")
return None
def get_registered_actions(self) -> Dict[str, ActionInfo]:
"""获取所有已注册的动作集"""
return self._registered_actions.copy()
def get_default_actions(self) -> Dict[str, ActionInfo]:
"""获取默认动作集"""
return self._default_actions.copy()
def get_using_actions(self) -> Dict[str, ActionInfo]:
"""获取当前正在使用的动作集"""
return self._using_actions.copy()
def add_action_to_using(self, action_name: str) -> bool:
"""
添加已注册的动作到当前使用的动作集
Args:
action_name: 动作名称
Returns:
bool: 添加是否成功
"""
if action_name not in self._registered_actions:
logger.warning(f"添加失败: 动作 {action_name} 未注册")
return False
if action_name in self._using_actions:
logger.info(f"动作 {action_name} 已经在使用中")
return True
self._using_actions[action_name] = self._registered_actions[action_name]
logger.info(f"添加动作 {action_name} 到使用集")
return True
def remove_action_from_using(self, action_name: str) -> bool:
"""
从当前使用的动作集中移除指定动作
Args:
action_name: 动作名称
Returns:
bool: 移除是否成功
"""
if action_name not in self._using_actions:
logger.warning(f"移除失败: 动作 {action_name} 不在当前使用的动作集中")
return False
del self._using_actions[action_name]
logger.info(f"已从使用集中移除动作 {action_name}")
return True
def add_action(self, action_name: str, description: str, parameters: Dict = None, require: List = None) -> bool:
"""
添加新的动作到注册集
Args:
action_name: 动作名称
description: 动作描述
parameters: 动作参数定义,默认为空字典
require: 动作依赖项,默认为空列表
Returns:
bool: 添加是否成功
"""
if action_name in self._registered_actions:
return False
if parameters is None:
parameters = {}
if require is None:
require = []
action_info = {"description": description, "parameters": parameters, "require": require}
self._registered_actions[action_name] = action_info
return True
def remove_action(self, action_name: str) -> bool:
"""从注册集移除指定动作"""
if action_name not in self._registered_actions:
return False
del self._registered_actions[action_name]
# 如果在使用集中也存在,一并移除
if action_name in self._using_actions:
del self._using_actions[action_name]
return True
def temporarily_remove_actions(self, actions_to_remove: List[str]) -> None:
"""临时移除使用集中的指定动作,备份原始使用集"""
if self._original_actions_backup is None:
self._original_actions_backup = self._using_actions.copy()
for name in actions_to_remove:
self._using_actions.pop(name, None)
def restore_actions(self) -> None:
"""恢复之前备份的原始使用集"""
if self._original_actions_backup is not None:
self._using_actions = self._original_actions_backup.copy()
self._original_actions_backup = None
def restore_default_actions(self) -> None:
"""恢复默认动作集到使用集"""
self._using_actions = self._default_actions.copy()
self._original_actions_backup = None
def get_action(self, action_name: str) -> Optional[Type[BaseAction]]:
"""
获取指定动作的处理器类
Args:
action_name: 动作名称
Returns:
Optional[Type[BaseAction]]: 动作处理器类如果不存在则返回None
"""
return _ACTION_REGISTRY.get(action_name)