From dce48d38cf8c6a46493fecbc99273f1fa534bd14 Mon Sep 17 00:00:00 2001 From: Windpicker-owo <3431391539@qq.com> Date: Tue, 23 Sep 2025 00:17:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9C=A8=E6=8F=92=E4=BB=B6=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=B8=AD=E6=B7=BB=E5=8A=A0=20Chatter=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 component_types.py 中新增了用于 CHATTER 的 ComponentType。 - 实现了 ChatterInfo 类,用于存储 Chatter 组件的相关信息。 - 增强了 ComponentRegistry,以支持 Chatter 组件的注册与管理。 - 创建了 ChatterManager,用于管理 Chatter 实例并处理聊天流。 - 开发了 BaseChatter 抽象类,用于定义 Chatter 的行为规范。 - 实现了 AffinityChatter,作为具备兴趣评分与关系构建功能的具体 Chatter 组件。 - 添加了一个内置的 Chatter 插件,并附带完整文档与使用示例。 - 更新了 PluginManager,在插件概览中加入 Chatter 组件的统计信息。 --- src/chat/affinity_flow/__init__.py | 4 +- src/chat/affinity_flow/chatter.py | 5 +- src/chat/chatter_manager.py | 136 ++++++++++++ src/chat/message_manager/message_manager.py | 19 +- src/chat/planner_actions/planner.py | 24 +- .../data_models/message_manager_data_model.py | 38 ++++ src/plugin_system/base/base_chatter.py | 57 +++++ src/plugin_system/base/component_types.py | 14 +- src/plugin_system/core/component_registry.py | 155 +++++++++---- src/plugin_system/core/plugin_manager.py | 9 +- src/plugins/built_in/chatter/README.md | 125 +++++++++++ src/plugins/built_in/chatter/__init__.py | 8 + src/plugins/built_in/chatter/_manifest.json | 23 ++ .../built_in/chatter/affinity_chatter.py | 206 ++++++++++++++++++ src/plugins/built_in/chatter/plugin.py | 46 ++++ 15 files changed, 807 insertions(+), 62 deletions(-) create mode 100644 src/chat/chatter_manager.py create mode 100644 src/plugin_system/base/base_chatter.py create mode 100644 src/plugins/built_in/chatter/README.md create mode 100644 src/plugins/built_in/chatter/__init__.py create mode 100644 src/plugins/built_in/chatter/_manifest.json create mode 100644 src/plugins/built_in/chatter/affinity_chatter.py create mode 100644 src/plugins/built_in/chatter/plugin.py diff --git a/src/chat/affinity_flow/__init__.py b/src/chat/affinity_flow/__init__.py index 59f35bacd..1991738a9 100644 --- a/src/chat/affinity_flow/__init__.py +++ b/src/chat/affinity_flow/__init__.py @@ -3,6 +3,8 @@ 提供全局的AFC管理器实例 """ -from src.chat.affinity_flow.afc_manager import afc_manager +# Avoid importing submodules at package import time to prevent circular imports. +# Consumers should import specific submodules directly, for example: +# from src.chat.affinity_flow.afc_manager import afc_manager __all__ = ["afc_manager", "AFCManager", "AffinityFlowChatter"] diff --git a/src/chat/affinity_flow/chatter.py b/src/chat/affinity_flow/chatter.py index fa3445924..92e46963c 100644 --- a/src/chat/affinity_flow/chatter.py +++ b/src/chat/affinity_flow/chatter.py @@ -11,6 +11,7 @@ from typing import Dict from src.chat.planner_actions.action_manager import ActionManager from src.chat.planner_actions.planner import ActionPlanner from src.common.data_models.message_manager_data_model import StreamContext +from src.plugin_system.base.base_chatter import BaseChatter from src.plugin_system.base.component_types import ChatMode from src.common.logger import get_logger @@ -18,7 +19,7 @@ from src.common.logger import get_logger logger = get_logger("affinity_chatter") -class AffinityFlowChatter: +class AffinityFlowChatter(BaseChatter): """单个亲和力聊天处理器""" def __init__(self, stream_id: str, planner: ActionPlanner, action_manager: ActionManager): @@ -44,7 +45,7 @@ class AffinityFlowChatter: } self.last_activity_time = time.time() - async def process_stream_context(self, context: StreamContext) -> Dict[str, any]: + async def execute(self, context: StreamContext) -> dict: """ 处理StreamContext对象 diff --git a/src/chat/chatter_manager.py b/src/chat/chatter_manager.py new file mode 100644 index 000000000..6b09938a0 --- /dev/null +++ b/src/chat/chatter_manager.py @@ -0,0 +1,136 @@ +from typing import Dict, List, Optional, Any +import time +from src.plugin_system.base.base_chatter import BaseChatter +from src.common.data_models.message_manager_data_model import StreamContext +from src.chat.planner_actions.planner import ActionPlanner +from src.chat.planner_actions.action_manager import ActionManager +from src.plugin_system.base.component_types import ChatType, ComponentType +from src.common.logger import get_logger + +logger = get_logger("chatter_manager") + +class ChatterManager: + def __init__(self, action_manager: ActionManager): + self.action_manager = action_manager + self.chatter_classes: Dict[ChatType, List[type]] = {} + self.instances: Dict[str, BaseChatter] = {} + + # 管理器统计 + self.stats = { + "chatters_registered": 0, + "streams_processed": 0, + "successful_executions": 0, + "failed_executions": 0, + } + + def _auto_register_from_component_registry(self): + """从组件注册表自动注册已注册的chatter组件""" + try: + from src.plugin_system.core.component_registry import component_registry + # 获取所有CHATTER类型的组件 + chatter_components = component_registry.get_enabled_chatter_registry() + for chatter_name, chatter_class in chatter_components.items(): + self.register_chatter(chatter_class) + logger.info(f"自动注册chatter组件: {chatter_name}") + except Exception as e: + logger.warning(f"自动注册chatter组件时发生错误: {e}") + + def register_chatter(self, chatter_class: type): + """注册聊天处理器类""" + for chat_type in chatter_class.chat_types: + if chat_type not in self.chatter_classes: + self.chatter_classes[chat_type] = [] + self.chatter_classes[chat_type].append(chatter_class) + logger.info(f"注册聊天处理器 {chatter_class.__name__} 支持 {chat_type.value} 聊天类型") + + self.stats["chatters_registered"] += 1 + + def get_chatter_class(self, chat_type: ChatType) -> Optional[type]: + """获取指定聊天类型的聊天处理器类""" + if chat_type in self.chatter_classes: + return self.chatter_classes[chat_type][0] + return None + + def get_supported_chat_types(self) -> List[ChatType]: + """获取支持的聊天类型列表""" + return list(self.chatter_classes.keys()) + + def get_registered_chatters(self) -> Dict[ChatType, List[type]]: + """获取已注册的聊天处理器""" + return self.chatter_classes.copy() + + def get_stream_instance(self, stream_id: str) -> Optional[BaseChatter]: + """获取指定流的聊天处理器实例""" + return self.instances.get(stream_id) + + def cleanup_inactive_instances(self, max_inactive_minutes: int = 60): + """清理不活跃的实例""" + current_time = time.time() + max_inactive_seconds = max_inactive_minutes * 60 + + inactive_streams = [] + for stream_id, instance in self.instances.items(): + if hasattr(instance, 'get_activity_time'): + activity_time = instance.get_activity_time() + if (current_time - activity_time) > max_inactive_seconds: + inactive_streams.append(stream_id) + + for stream_id in inactive_streams: + del self.instances[stream_id] + logger.info(f"清理不活跃聊天流实例: {stream_id}") + + async def process_stream_context(self, stream_id: str, context: StreamContext) -> dict: + """处理流上下文""" + chat_type = context.chat_type + logger.debug(f"处理流 {stream_id},聊天类型: {chat_type.value}") + if not self.chatter_classes: + self._auto_register_from_component_registry() + + # 获取适合该聊天类型的chatter + chatter_class = self.get_chatter_class(chat_type) + if not chatter_class: + # 如果没有找到精确匹配,尝试查找支持ALL类型的chatter + from src.plugin_system.base.component_types import ChatType + all_chatter_class = self.get_chatter_class(ChatType.ALL) + if all_chatter_class: + chatter_class = all_chatter_class + logger.info(f"流 {stream_id} 使用通用chatter (类型: {chat_type.value})") + else: + raise ValueError(f"No chatter registered for chat type {chat_type}") + + if stream_id not in self.instances: + planner = ActionPlanner(stream_id, self.action_manager) + self.instances[stream_id] = chatter_class(stream_id=stream_id, planner=planner, action_manager=self.action_manager) + logger.info(f"创建新的聊天流实例: {stream_id} 使用 {chatter_class.__name__} (类型: {chat_type.value})") + + self.stats["streams_processed"] += 1 + try: + result = await self.instances[stream_id].execute(context) + self.stats["successful_executions"] += 1 + + # 记录处理结果 + success = result.get("success", False) + actions_count = result.get("actions_count", 0) + logger.debug(f"流 {stream_id} 处理完成: 成功={success}, 动作数={actions_count}") + + return result + except Exception as e: + self.stats["failed_executions"] += 1 + logger.error(f"处理流 {stream_id} 时发生错误: {e}") + raise + + def get_stats(self) -> Dict[str, Any]: + """获取管理器统计信息""" + stats = self.stats.copy() + stats["active_instances"] = len(self.instances) + stats["registered_chatter_types"] = len(self.chatter_classes) + return stats + + def reset_stats(self): + """重置统计信息""" + self.stats = { + "chatters_registered": 0, + "streams_processed": 0, + "successful_executions": 0, + "failed_executions": 0, + } \ No newline at end of file diff --git a/src/chat/message_manager/message_manager.py b/src/chat/message_manager/message_manager.py index 050ad5b0a..d0e8c62c3 100644 --- a/src/chat/message_manager/message_manager.py +++ b/src/chat/message_manager/message_manager.py @@ -11,7 +11,8 @@ from typing import Dict, Optional, Any, TYPE_CHECKING from src.common.logger import get_logger from src.common.data_models.database_data_model import DatabaseMessages from src.common.data_models.message_manager_data_model import StreamContext, MessageManagerStats, StreamStats -from src.chat.affinity_flow.afc_manager import afc_manager +from src.chat.chatter_manager import ChatterManager +from src.chat.planner_actions.action_manager import ActionManager if TYPE_CHECKING: from src.common.data_models.message_manager_data_model import StreamContext @@ -31,6 +32,10 @@ class MessageManager: # 统计信息 self.stats = MessageManagerStats() + # 初始化chatter manager + self.action_manager = ActionManager() + self.chatter_manager = ChatterManager(self.action_manager) + async def start(self): """启动消息管理器""" if self.is_running: @@ -125,15 +130,23 @@ class MessageManager: # 直接使用StreamContext对象进行处理 if unread_messages: try: - # 发送到AFC处理器,传递StreamContext对象 - results = await afc_manager.process_stream_context(stream_id, context) + # 记录当前chat type用于调试 + logger.debug(f"聊天流 {stream_id} 检测到的chat type: {context.chat_type.value}") + + # 发送到chatter manager,传递StreamContext对象 + results = await self.chatter_manager.process_stream_context(stream_id, context) # 处理结果,标记消息为已读 if results.get("success", False): self._clear_all_unread_messages(context) + logger.debug(f"聊天流 {stream_id} 处理成功,清除了 {len(unread_messages)} 条未读消息") + else: + logger.warning(f"聊天流 {stream_id} 处理失败: {results.get('error_message', '未知错误')}") except Exception as e: logger.error(f"处理聊天流 {stream_id} 时发生异常,将清除所有未读消息: {e}") + # 出现异常时也清除未读消息,避免重复处理 + self._clear_all_unread_messages(context) raise logger.debug(f"聊天流 {stream_id} 消息处理完成") diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index f1d39610a..9c4013a83 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -4,20 +4,24 @@ """ from dataclasses import asdict -from typing import Dict, List, Optional, Tuple +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple -from src.chat.planner_actions.action_manager import ActionManager +from src.plugin_system.base.component_types import ChatMode from src.chat.planner_actions.plan_executor import PlanExecutor from src.chat.planner_actions.plan_filter import PlanFilter from src.chat.planner_actions.plan_generator import PlanGenerator from src.chat.affinity_flow.interest_scoring import InterestScoringSystem from src.chat.affinity_flow.relationship_tracker import UserRelationshipTracker -from src.common.data_models.info_data_model import Plan -from src.common.data_models.message_manager_data_model import StreamContext + + from src.common.logger import get_logger from src.config.config import global_config -from src.plugin_system.base.component_types import ChatMode -import src.chat.planner_actions.planner_prompts #noga # noqa: F401 + +if TYPE_CHECKING: + from src.chat.planner_actions.action_manager import ActionManager + from src.common.data_models.message_manager_data_model import StreamContext + from src.common.data_models.info_data_model import Plan + # 导入提示词模块以确保其被初始化 from src.chat.planner_actions import planner_prompts # noqa @@ -35,7 +39,7 @@ class ActionPlanner: 4. 完整的规划流程:生成→筛选→执行的完整三阶段流程 """ - def __init__(self, chat_id: str, action_manager: ActionManager): + def __init__(self, chat_id: str, action_manager: "ActionManager"): """ 初始化增强版ActionPlanner。 @@ -85,7 +89,7 @@ class ActionPlanner: } async def plan( - self, mode: ChatMode = ChatMode.FOCUS, context: StreamContext = None + self, mode: ChatMode = ChatMode.FOCUS, context: "StreamContext" = None ) -> Tuple[List[Dict], Optional[Dict]]: """ 执行完整的增强版规划流程。 @@ -109,7 +113,7 @@ class ActionPlanner: self.planner_stats["failed_plans"] += 1 return [], None - async def _enhanced_plan_flow(self, mode: ChatMode, context: StreamContext) -> Tuple[List[Dict], Optional[Dict]]: + async def _enhanced_plan_flow(self, mode: ChatMode, context: "StreamContext") -> Tuple[List[Dict], Optional[Dict]]: """执行增强版规划流程""" try: # 1. 生成初始 Plan @@ -204,7 +208,7 @@ class ActionPlanner: self.planner_stats["replies_generated"] += reply_count self.planner_stats["other_actions_executed"] += other_count - def _build_return_result(self, plan: Plan) -> Tuple[List[Dict], Optional[Dict]]: + def _build_return_result(self, plan: "Plan") -> Tuple[List[Dict], Optional[Dict]]: """构建返回结果""" final_actions = plan.decided_actions or [] final_target_message = next((act.action_message for act in final_actions if act.action_message), None) diff --git a/src/common/data_models/message_manager_data_model.py b/src/common/data_models/message_manager_data_model.py index 27ed03759..5ba8d6c42 100644 --- a/src/common/data_models/message_manager_data_model.py +++ b/src/common/data_models/message_manager_data_model.py @@ -10,6 +10,7 @@ from enum import Enum from typing import List, Optional, TYPE_CHECKING from . import BaseDataModel +from src.plugin_system.base.component_types import ChatType if TYPE_CHECKING: from .database_data_model import DatabaseMessages @@ -28,6 +29,7 @@ class StreamContext(BaseDataModel): """聊天流上下文信息""" stream_id: str + chat_type: ChatType = ChatType.PRIVATE # 聊天类型,默认为私聊 unread_messages: List["DatabaseMessages"] = field(default_factory=list) history_messages: List["DatabaseMessages"] = field(default_factory=list) last_check_time: float = field(default_factory=time.time) @@ -39,6 +41,42 @@ class StreamContext(BaseDataModel): message.is_read = False self.unread_messages.append(message) + # 自动检测和更新chat type + self._detect_chat_type(message) + + def _detect_chat_type(self, message: "DatabaseMessages"): + """根据消息内容自动检测聊天类型""" + # 只有在第一次添加消息时才检测聊天类型,避免后续消息改变类型 + if len(self.unread_messages) == 1: # 只有这条消息 + # 如果消息包含群组信息,则为群聊 + if hasattr(message, 'chat_info_group_id') and message.chat_info_group_id: + self.chat_type = ChatType.GROUP + elif hasattr(message, 'chat_info_group_name') and message.chat_info_group_name: + self.chat_type = ChatType.GROUP + else: + self.chat_type = ChatType.PRIVATE + + def update_chat_type(self, chat_type: ChatType): + """手动更新聊天类型""" + self.chat_type = chat_type + + def is_group_chat(self) -> bool: + """检查是否为群聊""" + return self.chat_type == ChatType.GROUP + + def is_private_chat(self) -> bool: + """检查是否为私聊""" + return self.chat_type == ChatType.PRIVATE + + def get_chat_type_display(self) -> str: + """获取聊天类型的显示名称""" + if self.chat_type == ChatType.GROUP: + return "群聊" + elif self.chat_type == ChatType.PRIVATE: + return "私聊" + else: + return "未知类型" + def get_unread_messages(self) -> List["DatabaseMessages"]: """获取未读消息""" return [msg for msg in self.unread_messages if not msg.is_read] diff --git a/src/plugin_system/base/base_chatter.py b/src/plugin_system/base/base_chatter.py new file mode 100644 index 000000000..3f46b0c3a --- /dev/null +++ b/src/plugin_system/base/base_chatter.py @@ -0,0 +1,57 @@ +from abc import ABC, abstractmethod +from typing import List, Optional, TYPE_CHECKING +from src.common.data_models.message_manager_data_model import StreamContext +from .component_types import ChatType +from src.plugin_system.base.component_types import ChatterInfo, ComponentType + +if TYPE_CHECKING: + from src.chat.planner_actions.action_manager import ActionManager + from src.chat.planner_actions.planner import ActionPlanner + +class BaseChatter(ABC): + chatter_name: str = "" + """Chatter组件的名称""" + chatter_description: str = "" + """Chatter组件的描述""" + chat_types: List[ChatType] = [ChatType.PRIVATE, ChatType.GROUP] + + def __init__(self, stream_id: str, planner: 'ActionPlanner', action_manager: 'ActionManager'): + """ + 初始化聊天处理器 + + Args: + stream_id: 聊天流ID + planner: 动作规划器 + action_manager: 动作管理器 + """ + self.stream_id = stream_id + self.planner = planner + self.action_manager = action_manager + + @abstractmethod + async def execute(self, context: StreamContext) -> dict: + """ + 执行聊天处理流程 + + Args: + context: StreamContext对象,包含聊天流的所有消息信息 + + Returns: + 处理结果字典 + """ + pass + + @classmethod + def get_chatter_info(cls) -> "ChatterInfo": + """从类属性生成ChatterInfo + Returns: + ChatterInfo对象 + """ + + return ChatterInfo( + name=cls.chatter_name, + description=cls.chatter_description or "No description provided.", + chat_type_allow=cls.chat_types[0], + component_type=ComponentType.CHATTER, + ) + diff --git a/src/plugin_system/base/component_types.py b/src/plugin_system/base/component_types.py index a939d0ab5..98870044e 100644 --- a/src/plugin_system/base/component_types.py +++ b/src/plugin_system/base/component_types.py @@ -17,6 +17,7 @@ class ComponentType(Enum): TOOL = "tool" # 工具组件 SCHEDULER = "scheduler" # 定时任务组件(预留) EVENT_HANDLER = "event_handler" # 事件处理组件 + CHATTER = "chatter" # 聊天处理器组件 def __str__(self) -> str: return self.value @@ -54,8 +55,8 @@ class ChatMode(Enum): class ChatType(Enum): """聊天类型枚举,用于限制插件在不同聊天环境中的使用""" - GROUP = "group" # 仅群聊可用 PRIVATE = "private" # 仅私聊可用 + GROUP = "group" # 仅群聊可用 ALL = "all" # 群聊和私聊都可用 def __str__(self): @@ -210,6 +211,17 @@ class EventHandlerInfo(ComponentInfo): self.component_type = ComponentType.EVENT_HANDLER +@dataclass +class ChatterInfo(ComponentInfo): + """聊天处理器组件信息""" + + chat_type_allow: ChatType = ChatType.ALL # 允许的聊天类型 + + def __post_init__(self): + super().__post_init__() + self.component_type = ComponentType.CHATTER + + @dataclass class EventInfo(ComponentInfo): """事件组件信息""" diff --git a/src/plugin_system/core/component_registry.py b/src/plugin_system/core/component_registry.py index d22b2dbb1..29adead53 100644 --- a/src/plugin_system/core/component_registry.py +++ b/src/plugin_system/core/component_registry.py @@ -1,7 +1,7 @@ from pathlib import Path import re -from typing import Dict, List, Optional, Any, Pattern, Tuple, Union, Type +from typing import TYPE_CHECKING, Dict, List, Optional, Any, Pattern, Tuple, Union, Type from src.common.logger import get_logger from src.plugin_system.base.component_types import ( @@ -11,14 +11,17 @@ from src.plugin_system.base.component_types import ( CommandInfo, PlusCommandInfo, EventHandlerInfo, + ChatterInfo, PluginInfo, ComponentType, ) + from src.plugin_system.base.base_command import BaseCommand from src.plugin_system.base.base_action import BaseAction from src.plugin_system.base.base_tool import BaseTool from src.plugin_system.base.base_events_handler import BaseEventHandler from src.plugin_system.base.plus_command import PlusCommand +from src.plugin_system.base.base_chatter import BaseChatter logger = get_logger("component_registry") @@ -31,42 +34,45 @@ class ComponentRegistry: def __init__(self): # 命名空间式组件名构成法 f"{component_type}.{component_name}" - self._plus_command_registry: Dict[str, Type[PlusCommand]] = {} - self._components: Dict[str, ComponentInfo] = {} + self._components: Dict[str, 'ComponentInfo'] = {} """组件注册表 命名空间式组件名 -> 组件信息""" - self._components_by_type: Dict[ComponentType, Dict[str, ComponentInfo]] = {types: {} for types in ComponentType} + self._components_by_type: Dict['ComponentType', Dict[str, 'ComponentInfo']] = {types: {} for types in ComponentType} """类型 -> 组件原名称 -> 组件信息""" self._components_classes: Dict[ - str, Type[Union[BaseCommand, BaseAction, BaseTool, BaseEventHandler, PlusCommand]] + str, Type[Union['BaseCommand', 'BaseAction', 'BaseTool', 'BaseEventHandler', 'PlusCommand', 'BaseChatter']] ] = {} """命名空间式组件名 -> 组件类""" # 插件注册表 - self._plugins: Dict[str, PluginInfo] = {} + self._plugins: Dict[str, 'PluginInfo'] = {} """插件名 -> 插件信息""" # Action特定注册表 - self._action_registry: Dict[str, Type[BaseAction]] = {} + self._action_registry: Dict[str, Type['BaseAction']] = {} """Action注册表 action名 -> action类""" - self._default_actions: Dict[str, ActionInfo] = {} + self._default_actions: Dict[str, 'ActionInfo'] = {} """默认动作集,即启用的Action集,用于重置ActionManager状态""" # Command特定注册表 - self._command_registry: Dict[str, Type[BaseCommand]] = {} + self._command_registry: Dict[str, Type['BaseCommand']] = {} """Command类注册表 command名 -> command类""" self._command_patterns: Dict[Pattern, str] = {} """编译后的正则 -> command名""" # 工具特定注册表 - self._tool_registry: Dict[str, Type[BaseTool]] = {} # 工具名 -> 工具类 - self._llm_available_tools: Dict[str, Type[BaseTool]] = {} # llm可用的工具名 -> 工具类 + self._tool_registry: Dict[str, Type['BaseTool']] = {} # 工具名 -> 工具类 + self._llm_available_tools: Dict[str, Type['BaseTool']] = {} # llm可用的工具名 -> 工具类 # EventHandler特定注册表 - self._event_handler_registry: Dict[str, Type[BaseEventHandler]] = {} + self._event_handler_registry: Dict[str, Type['BaseEventHandler']] = {} """event_handler名 -> event_handler类""" - self._enabled_event_handlers: Dict[str, Type[BaseEventHandler]] = {} + self._enabled_event_handlers: Dict[str, Type['BaseEventHandler']] = {} """启用的事件处理器 event_handler名 -> event_handler类""" + self._chatter_registry: Dict[str, Type['BaseChatter']] = {} + """chatter名 -> chatter类""" + self._enabled_chatter_registry: Dict[str, Type['BaseChatter']] = {} + """启用的chatter名 -> chatter类""" logger.info("组件注册中心初始化完成") # == 注册方法 == @@ -93,7 +99,7 @@ class ComponentRegistry: def register_component( self, component_info: ComponentInfo, - component_class: Type[Union[BaseCommand, BaseAction, BaseEventHandler, BaseTool]], + component_class: Type[Union['BaseCommand', 'BaseAction', 'BaseEventHandler', 'BaseTool', 'BaseChatter']], ) -> bool: """注册组件 @@ -151,6 +157,10 @@ class ComponentRegistry: assert isinstance(component_info, EventHandlerInfo) assert issubclass(component_class, BaseEventHandler) ret = self._register_event_handler_component(component_info, component_class) + case ComponentType.CHATTER: + assert isinstance(component_info, ChatterInfo) + assert issubclass(component_class, BaseChatter) + ret = self._register_chatter_component(component_info, component_class) case _: logger.warning(f"未知组件类型: {component_type}") @@ -162,7 +172,7 @@ class ComponentRegistry: ) return True - def _register_action_component(self, action_info: ActionInfo, action_class: Type[BaseAction]) -> bool: + def _register_action_component(self, action_info: 'ActionInfo', action_class: Type['BaseAction']) -> bool: """注册Action组件到Action特定注册表""" if not (action_name := action_info.name): logger.error(f"Action组件 {action_class.__name__} 必须指定名称") @@ -182,7 +192,7 @@ class ComponentRegistry: return True - def _register_command_component(self, command_info: CommandInfo, command_class: Type[BaseCommand]) -> bool: + def _register_command_component(self, command_info: 'CommandInfo', command_class: Type['BaseCommand']) -> bool: """注册Command组件到Command特定注册表""" if not (command_name := command_info.name): logger.error(f"Command组件 {command_class.__name__} 必须指定名称") @@ -209,7 +219,7 @@ class ComponentRegistry: return True def _register_plus_command_component( - self, plus_command_info: PlusCommandInfo, plus_command_class: Type[PlusCommand] + self, plus_command_info: 'PlusCommandInfo', plus_command_class: Type['PlusCommand'] ) -> bool: """注册PlusCommand组件到特定注册表""" plus_command_name = plus_command_info.name @@ -223,7 +233,7 @@ class ComponentRegistry: # 创建专门的PlusCommand注册表(如果还没有) if not hasattr(self, "_plus_command_registry"): - self._plus_command_registry: Dict[str, Type[PlusCommand]] = {} + self._plus_command_registry: Dict[str, Type['PlusCommand']] = {} plus_command_class.plugin_name = plus_command_info.plugin_name # 设置插件配置 @@ -233,7 +243,7 @@ class ComponentRegistry: logger.debug(f"已注册PlusCommand组件: {plus_command_name}") return True - def _register_tool_component(self, tool_info: ToolInfo, tool_class: Type[BaseTool]) -> bool: + def _register_tool_component(self, tool_info: 'ToolInfo', tool_class: Type['BaseTool']) -> bool: """注册Tool组件到Tool特定注册表""" tool_name = tool_info.name @@ -249,7 +259,7 @@ class ComponentRegistry: return True def _register_event_handler_component( - self, handler_info: EventHandlerInfo, handler_class: Type[BaseEventHandler] + self, handler_info: 'EventHandlerInfo', handler_class: Type['BaseEventHandler'] ) -> bool: if not (handler_name := handler_info.name): logger.error(f"EventHandler组件 {handler_class.__name__} 必须指定名称") @@ -275,9 +285,34 @@ class ComponentRegistry: handler_class, self.get_plugin_config(handler_info.plugin_name) or {} ) + def _register_chatter_component(self, chatter_info: 'ChatterInfo', chatter_class: Type['BaseChatter']) -> bool: + """注册Chatter组件到Chatter特定注册表""" + chatter_name = chatter_info.name + + if not chatter_name: + logger.error(f"Chatter组件 {chatter_class.__name__} 必须指定名称") + return False + if not isinstance(chatter_info, ChatterInfo) or not issubclass(chatter_class, BaseChatter): + logger.error(f"注册失败: {chatter_name} 不是有效的Chatter") + return False + + chatter_class.plugin_name = chatter_info.plugin_name + # 设置插件配置 + chatter_class.plugin_config = self.get_plugin_config(chatter_info.plugin_name) or {} + + self._chatter_registry[chatter_name] = chatter_class + + if not chatter_info.enabled: + logger.warning(f"Chatter组件 {chatter_name} 未启用") + return True # 未启用,但是也是注册成功 + self._enabled_chatter_registry[chatter_name] = chatter_class + + logger.debug(f"已注册Chatter组件: {chatter_name}") + return True + # === 组件移除相关 === - async def remove_component(self, component_name: str, component_type: ComponentType, plugin_name: str) -> bool: + async def remove_component(self, component_name: str, component_type: 'ComponentType', plugin_name: str) -> bool: target_component_class = self.get_component_class(component_name, component_type) if not target_component_class: logger.warning(f"组件 {component_name} 未注册,无法移除") @@ -325,6 +360,12 @@ class ComponentRegistry: except Exception as e: logger.warning(f"移除EventHandler事件订阅时出错: {e}") + case ComponentType.CHATTER: + # 移除Chatter注册 + if hasattr(self, '_chatter_registry'): + self._chatter_registry.pop(component_name, None) + logger.debug(f"已移除Chatter组件: {component_name}") + case _: logger.warning(f"未知的组件类型: {component_type}") return False @@ -443,8 +484,8 @@ class ComponentRegistry: # === 组件查询方法 === def get_component_info( - self, component_name: str, component_type: Optional[ComponentType] = None - ) -> Optional[ComponentInfo]: + self, component_name: str, component_type: Optional['ComponentType'] = None + ) -> Optional['ComponentInfo']: # sourcery skip: class-extract-method """获取组件信息,支持自动命名空间解析 @@ -488,8 +529,8 @@ class ComponentRegistry: def get_component_class( self, component_name: str, - component_type: Optional[ComponentType] = None, - ) -> Optional[Union[Type[BaseCommand], Type[BaseAction], Type[BaseEventHandler], Type[BaseTool]]]: + component_type: Optional['ComponentType'] = None, + ) -> Optional[Union[Type['BaseCommand'], Type['BaseAction'], Type['BaseEventHandler'], Type['BaseTool']]]: """获取组件类,支持自动命名空间解析 Args: @@ -506,7 +547,7 @@ class ComponentRegistry: # 2. 如果指定了组件类型,构造命名空间化的名称查找 if component_type: namespaced_name = f"{component_type.value}.{component_name}" - return self._components_classes.get(namespaced_name) + return self._components_classes.get(namespaced_name) # type: ignore[valid-type] # 3. 如果没有指定类型,尝试在所有命名空间中查找 candidates = [] @@ -531,22 +572,22 @@ class ComponentRegistry: # 4. 都没找到 return None - def get_components_by_type(self, component_type: ComponentType) -> Dict[str, ComponentInfo]: + def get_components_by_type(self, component_type: 'ComponentType') -> Dict[str, 'ComponentInfo']: """获取指定类型的所有组件""" return self._components_by_type.get(component_type, {}).copy() - def get_enabled_components_by_type(self, component_type: ComponentType) -> Dict[str, ComponentInfo]: + def get_enabled_components_by_type(self, component_type: 'ComponentType') -> Dict[str, 'ComponentInfo']: """获取指定类型的所有启用组件""" components = self.get_components_by_type(component_type) return {name: info for name, info in components.items() if info.enabled} # === Action特定查询方法 === - def get_action_registry(self) -> Dict[str, Type[BaseAction]]: + def get_action_registry(self) -> Dict[str, Type['BaseAction']]: """获取Action注册表""" return self._action_registry.copy() - def get_registered_action_info(self, action_name: str) -> Optional[ActionInfo]: + def get_registered_action_info(self, action_name: str) -> Optional['ActionInfo']: """获取Action信息""" info = self.get_component_info(action_name, ComponentType.ACTION) return info if isinstance(info, ActionInfo) else None @@ -557,11 +598,11 @@ class ComponentRegistry: # === Command特定查询方法 === - def get_command_registry(self) -> Dict[str, Type[BaseCommand]]: + def get_command_registry(self) -> Dict[str, Type['BaseCommand']]: """获取Command注册表""" return self._command_registry.copy() - def get_registered_command_info(self, command_name: str) -> Optional[CommandInfo]: + def get_registered_command_info(self, command_name: str) -> Optional['CommandInfo']: """获取Command信息""" info = self.get_component_info(command_name, ComponentType.COMMAND) return info if isinstance(info, CommandInfo) else None @@ -570,7 +611,7 @@ class ComponentRegistry: """获取Command模式注册表""" return self._command_patterns.copy() - def find_command_by_text(self, text: str) -> Optional[Tuple[Type[BaseCommand], dict, CommandInfo]]: + def find_command_by_text(self, text: str) -> Optional[Tuple[Type['BaseCommand'], dict, 'CommandInfo']]: # sourcery skip: use-named-expression, use-next """根据文本查找匹配的命令 @@ -597,15 +638,15 @@ class ComponentRegistry: return None # === Tool 特定查询方法 === - def get_tool_registry(self) -> Dict[str, Type[BaseTool]]: + def get_tool_registry(self) -> Dict[str, Type['BaseTool']]: """获取Tool注册表""" return self._tool_registry.copy() - def get_llm_available_tools(self) -> Dict[str, Type[BaseTool]]: + def get_llm_available_tools(self) -> Dict[str, Type['BaseTool']]: """获取LLM可用的Tool列表""" return self._llm_available_tools.copy() - def get_registered_tool_info(self, tool_name: str) -> Optional[ToolInfo]: + def get_registered_tool_info(self, tool_name: str) -> Optional['ToolInfo']: """获取Tool信息 Args: @@ -618,13 +659,13 @@ class ComponentRegistry: return info if isinstance(info, ToolInfo) else None # === PlusCommand 特定查询方法 === - def get_plus_command_registry(self) -> Dict[str, Type[PlusCommand]]: + def get_plus_command_registry(self) -> Dict[str, Type['PlusCommand']]: """获取PlusCommand注册表""" if not hasattr(self, "_plus_command_registry"): pass return self._plus_command_registry.copy() - def get_registered_plus_command_info(self, command_name: str) -> Optional[PlusCommandInfo]: + def get_registered_plus_command_info(self, command_name: str) -> Optional['PlusCommandInfo']: """获取PlusCommand信息 Args: @@ -638,26 +679,44 @@ class ComponentRegistry: # === EventHandler 特定查询方法 === - def get_event_handler_registry(self) -> Dict[str, Type[BaseEventHandler]]: + def get_event_handler_registry(self) -> Dict[str, Type['BaseEventHandler']]: """获取事件处理器注册表""" return self._event_handler_registry.copy() - def get_registered_event_handler_info(self, handler_name: str) -> Optional[EventHandlerInfo]: + def get_registered_event_handler_info(self, handler_name: str) -> Optional['EventHandlerInfo']: """获取事件处理器信息""" info = self.get_component_info(handler_name, ComponentType.EVENT_HANDLER) return info if isinstance(info, EventHandlerInfo) else None - def get_enabled_event_handlers(self) -> Dict[str, Type[BaseEventHandler]]: + def get_enabled_event_handlers(self) -> Dict[str, Type['BaseEventHandler']]: """获取启用的事件处理器""" return self._enabled_event_handlers.copy() + # === Chatter 特定查询方法 === + def get_chatter_registry(self) -> Dict[str, Type['BaseChatter']]: + """获取Chatter注册表""" + if not hasattr(self, '_chatter_registry'): + self._chatter_registry: Dict[str, Type[BaseChatter]] = {} + return self._chatter_registry.copy() + + def get_enabled_chatter_registry(self) -> Dict[str, Type['BaseChatter']]: + """获取启用的Chatter注册表""" + if not hasattr(self, '_enabled_chatter_registry'): + self._enabled_chatter_registry: Dict[str, Type[BaseChatter]] = {} + return self._enabled_chatter_registry.copy() + + def get_registered_chatter_info(self, chatter_name: str) -> Optional['ChatterInfo']: + """获取Chatter信息""" + info = self.get_component_info(chatter_name, ComponentType.CHATTER) + return info if isinstance(info, ChatterInfo) else None + # === 插件查询方法 === - def get_plugin_info(self, plugin_name: str) -> Optional[PluginInfo]: + def get_plugin_info(self, plugin_name: str) -> Optional['PluginInfo']: """获取插件信息""" return self._plugins.get(plugin_name) - def get_all_plugins(self) -> Dict[str, PluginInfo]: + def get_all_plugins(self) -> Dict[str, 'PluginInfo']: """获取所有插件""" return self._plugins.copy() @@ -665,7 +724,7 @@ class ComponentRegistry: # """获取所有启用的插件""" # return {name: info for name, info in self._plugins.items() if info.enabled} - def get_plugin_components(self, plugin_name: str) -> List[ComponentInfo]: + def get_plugin_components(self, plugin_name: str) -> List['ComponentInfo']: """获取插件的所有组件""" plugin_info = self.get_plugin_info(plugin_name) return plugin_info.components if plugin_info else [] @@ -709,6 +768,7 @@ class ComponentRegistry: tool_components: int = 0 events_handlers: int = 0 plus_command_components: int = 0 + chatter_components: int = 0 for component in self._components.values(): if component.component_type == ComponentType.ACTION: action_components += 1 @@ -720,12 +780,15 @@ class ComponentRegistry: events_handlers += 1 elif component.component_type == ComponentType.PLUS_COMMAND: plus_command_components += 1 + elif component.component_type == ComponentType.CHATTER: + chatter_components += 1 return { "action_components": action_components, "command_components": command_components, "tool_components": tool_components, "event_handlers": events_handlers, "plus_command_components": plus_command_components, + "chatter_components": chatter_components, "total_components": len(self._components), "total_plugins": len(self._plugins), "components_by_type": { @@ -734,6 +797,10 @@ class ComponentRegistry: "enabled_components": len([c for c in self._components.values() if c.enabled]), "enabled_plugins": len([p for p in self._plugins.values() if p.enabled]), } + }, + "enabled_components": len([c for c in self._components.values() if c.enabled]), + "enabled_plugins": len([p for p in self._plugins.values() if p.enabled]), + } # === 组件移除相关 === diff --git a/src/plugin_system/core/plugin_manager.py b/src/plugin_system/core/plugin_manager.py index b572c0c1a..cc7a54d4c 100644 --- a/src/plugin_system/core/plugin_manager.py +++ b/src/plugin_system/core/plugin_manager.py @@ -378,13 +378,14 @@ class PluginManager: tool_count = stats.get("tool_components", 0) event_handler_count = stats.get("event_handlers", 0) plus_command_count = stats.get("plus_command_components", 0) + chatter_count = stats.get("chatter_components", 0) total_components = stats.get("total_components", 0) # 📋 显示插件加载总览 if total_registered > 0: logger.info("🎉 插件系统加载完成!") logger.info( - f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count})" + f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count})" ) # 显示详细的插件列表 @@ -440,6 +441,12 @@ class PluginManager: if plus_command_components: plus_command_names = [c.name for c in plus_command_components] logger.info(f" ⚡ PlusCommand组件: {', '.join(plus_command_names)}") + chatter_components = [ + c for c in plugin_info.components if c.component_type == ComponentType.CHATTER + ] + if chatter_components: + chatter_names = [c.name for c in chatter_components] + logger.info(f" 🗣️ Chatter组件: {', '.join(chatter_names)}") if event_handler_components: event_handler_names = [c.name for c in event_handler_components] logger.info(f" 📢 EventHandler组件: {', '.join(event_handler_names)}") diff --git a/src/plugins/built_in/chatter/README.md b/src/plugins/built_in/chatter/README.md new file mode 100644 index 000000000..d965d8215 --- /dev/null +++ b/src/plugins/built_in/chatter/README.md @@ -0,0 +1,125 @@ +# 亲和力聊天处理器插件 + +## 概述 + +这是一个内置的chatter插件,实现了基于亲和力流的智能聊天处理器,具有兴趣度评分和人物关系构建功能。 + +## 功能特性 + +- **智能兴趣度评分**: 自动识别和评估用户兴趣话题 +- **人物关系系统**: 根据互动历史建立和维持用户关系 +- **多聊天类型支持**: 支持私聊和群聊场景 +- **插件化架构**: 完全集成到插件系统中 + +## 组件架构 + +### BaseChatter (抽象基类) +- 位置: `src/plugin_system/base/base_chatter.py` +- 功能: 定义所有chatter组件的基础接口 +- 必须实现的方法: `execute(context: StreamContext) -> dict` + +### ChatterManager (管理器) +- 位置: `src/chat/chatter_manager.py` +- 功能: 管理和调度所有chatter组件 +- 特性: 自动从插件系统注册和发现chatter组件 + +### AffinityChatter (具体实现) +- 位置: `src/plugins/built_in/chatter/affinity_chatter.py` +- 功能: 亲和力流聊天处理器的具体实现 +- 支持的聊天类型: PRIVATE, GROUP + +## 使用方法 + +### 1. 基本使用 + +```python +from src.chat.chatter_manager import ChatterManager +from src.chat.planner_actions.action_manager import ActionManager + +# 初始化 +action_manager = ActionManager() +chatter_manager = ChatterManager(action_manager) + +# 处理消息流 +result = await chatter_manager.process_stream_context(stream_id, context) +``` + +### 2. 创建自定义Chatter + +```python +from src.plugin_system.base.base_chatter import BaseChatter +from src.plugin_system.base.component_types import ChatType, ComponentType +from src.plugin_system.base.component_types import ChatterInfo + +class CustomChatter(BaseChatter): + chat_types = [ChatType.PRIVATE] # 只支持私聊 + + async def execute(self, context: StreamContext) -> dict: + # 实现你的聊天逻辑 + return {"success": True, "message": "处理完成"} + +# 在插件中注册 +async def on_load(self): + chatter_info = ChatterInfo( + name="custom_chatter", + component_type=ComponentType.CHATTER, + description="自定义聊天处理器", + enabled=True, + plugin_name=self.name, + chat_type_allow=ChatType.PRIVATE + ) + + ComponentRegistry.register_component( + component_info=chatter_info, + component_class=CustomChatter + ) +``` + +## 配置 + +### 插件配置文件 +- 位置: `src/plugins/built_in/chatter/_manifest.json` +- 包含插件信息和组件配置 + +### 聊天类型 +- `PRIVATE`: 私聊 +- `GROUP`: 群聊 +- `ALL`: 所有类型 + +## 核心概念 + +### 1. 兴趣值系统 +- 自动识别同类话题 +- 兴趣值会根据聊天频率增减 +- 支持新话题的自动学习 + +### 2. 人物关系系统 +- 根据互动质量建立关系分 +- 不同关系分对应不同的回复风格 +- 支持情感化的交流 + +### 3. 执行流程 +1. 接收StreamContext +2. 使用ActionPlanner进行规划 +3. 执行相应的Action +4. 返回处理结果 + +## 扩展开发 + +### 添加新的Chatter类型 +1. 继承BaseChatter类 +2. 实现execute方法 +3. 在插件中注册组件 +4. 配置支持的聊天类型 + +### 集成现有功能 +- 使用ActionPlanner进行动作规划 +- 通过ActionManager执行动作 +- 利用现有的记忆和知识系统 + +## 注意事项 + +1. 所有chatter组件必须实现`execute`方法 +2. 插件注册时需要指定支持的聊天类型 +3. 组件名称不能包含点号(.) +4. 确保在插件卸载时正确清理资源 \ No newline at end of file diff --git a/src/plugins/built_in/chatter/__init__.py b/src/plugins/built_in/chatter/__init__.py new file mode 100644 index 000000000..c1c657070 --- /dev/null +++ b/src/plugins/built_in/chatter/__init__.py @@ -0,0 +1,8 @@ +""" +亲和力聊天处理器插件 +""" + +from .plugin import AffinityChatterPlugin +from .affinity_chatter import AffinityChatter + +__all__ = ["AffinityChatterPlugin", "AffinityChatter"] \ No newline at end of file diff --git a/src/plugins/built_in/chatter/_manifest.json b/src/plugins/built_in/chatter/_manifest.json new file mode 100644 index 000000000..253365b87 --- /dev/null +++ b/src/plugins/built_in/chatter/_manifest.json @@ -0,0 +1,23 @@ +{ + "manifest_version": 1, + "name": "affinity_chatter", + "display_name": "Affinity Flow Chatter", + "description": "Built-in chatter plugin for affinity flow with interest scoring and relationship building", + "version": "1.0.0", + "author": "MoFox", + "plugin_class": "AffinityChatterPlugin", + "enabled": true, + "is_built_in": true, + "components": [ + { + "name": "affinity_chatter", + "type": "chatter", + "description": "Affinity flow chatter with intelligent interest scoring and relationship building", + "enabled": true, + "chat_type_allow": ["all"] + } + ], + "host_application": { "min_version": "0.8.0" }, + "keywords": ["chatter", "affinity", "conversation"], + "categories": ["Chat", "AI"] +} \ No newline at end of file diff --git a/src/plugins/built_in/chatter/affinity_chatter.py b/src/plugins/built_in/chatter/affinity_chatter.py new file mode 100644 index 000000000..12eb7e016 --- /dev/null +++ b/src/plugins/built_in/chatter/affinity_chatter.py @@ -0,0 +1,206 @@ +""" +亲和力聊天处理器 +基于现有的AffinityFlowChatter重构为插件化组件 +""" + +import time +import traceback +from datetime import datetime +from typing import Dict, Any + +from src.plugin_system.base.base_chatter import BaseChatter +from src.plugin_system.base.component_types import ChatType, ChatMode +from src.common.data_models.message_manager_data_model import StreamContext +from src.chat.planner_actions.planner import ActionPlanner +from src.chat.planner_actions.action_manager import ActionManager +from src.common.logger import get_logger + +logger = get_logger("affinity_chatter") + + +class AffinityChatter(BaseChatter): + """亲和力聊天处理器""" + chatter_name: str = "AffinityChatter" + chatter_description: str = "基于亲和力模型的智能聊天处理器,支持多种聊天类型" + chat_types: list[ChatType] = [ChatType.ALL] # 支持所有聊天类型 + + def __init__(self, stream_id: str, planner: ActionPlanner, action_manager: ActionManager): + """ + 初始化亲和力聊天处理器 + + Args: + stream_id: 聊天流ID + planner: 动作规划器 + action_manager: 动作管理器 + """ + super().__init__(stream_id, planner, action_manager) + + # 处理器统计 + self.stats = { + "messages_processed": 0, + "plans_created": 0, + "actions_executed": 0, + "successful_executions": 0, + "failed_executions": 0, + } + self.last_activity_time = time.time() + + async def execute(self, context: StreamContext) -> dict: + """ + 处理StreamContext对象 + + Args: + context: StreamContext对象,包含聊天流的所有消息信息 + + Returns: + 处理结果字典 + """ + try: + unread_messages = context.get_unread_messages() + + # 使用增强版规划器处理消息 + actions, target_message = await self.planner.plan(mode=ChatMode.FOCUS, context=context) + self.stats["plans_created"] += 1 + + # 执行动作(如果规划器返回了动作) + execution_result = {"executed_count": len(actions) if actions else 0} + if actions: + logger.debug(f"聊天流 {self.stream_id} 生成了 {len(actions)} 个动作") + + # 更新统计 + self.stats["messages_processed"] += 1 + self.stats["actions_executed"] += execution_result.get("executed_count", 0) + self.stats["successful_executions"] += 1 + self.last_activity_time = time.time() + + result = { + "success": True, + "stream_id": self.stream_id, + "plan_created": True, + "actions_count": len(actions) if actions else 0, + "has_target_message": target_message is not None, + "unread_messages_processed": len(unread_messages), + **execution_result, + } + + logger.info( + f"聊天流 {self.stream_id} StreamContext处理成功: 动作数={result['actions_count']}, 未读消息={result['unread_messages_processed']}" + ) + + return result + + except Exception as e: + logger.error(f"亲和力聊天处理器 {self.stream_id} 处理StreamContext时出错: {e}\n{traceback.format_exc()}") + self.stats["failed_executions"] += 1 + self.last_activity_time = time.time() + + return { + "success": False, + "stream_id": self.stream_id, + "error_message": str(e), + "executed_count": 0, + } + + def get_stats(self) -> Dict[str, Any]: + """ + 获取处理器统计信息 + + Returns: + 统计信息字典 + """ + return self.stats.copy() + + def get_planner_stats(self) -> Dict[str, Any]: + """ + 获取规划器统计信息 + + Returns: + 规划器统计信息字典 + """ + return self.planner.get_planner_stats() + + def get_interest_scoring_stats(self) -> Dict[str, Any]: + """ + 获取兴趣度评分统计信息 + + Returns: + 兴趣度评分统计信息字典 + """ + return self.planner.get_interest_scoring_stats() + + def get_relationship_stats(self) -> Dict[str, Any]: + """ + 获取用户关系统计信息 + + Returns: + 用户关系统计信息字典 + """ + return self.planner.get_relationship_stats() + + def get_user_relationship(self, user_id: str) -> float: + """ + 获取用户关系分 + + Args: + user_id: 用户ID + + Returns: + 用户关系分 (0.0-1.0) + """ + return self.planner.get_user_relationship(user_id) + + def update_interest_keywords(self, new_keywords: dict): + """ + 更新兴趣关键词 + + Args: + new_keywords: 新的兴趣关键词字典 + """ + self.planner.update_interest_keywords(new_keywords) + logger.info(f"聊天流 {self.stream_id} 已更新兴趣关键词: {list(new_keywords.keys())}") + + def reset_stats(self): + """重置统计信息""" + self.stats = { + "messages_processed": 0, + "plans_created": 0, + "actions_executed": 0, + "successful_executions": 0, + "failed_executions": 0, + } + + def is_active(self, max_inactive_minutes: int = 60) -> bool: + """ + 检查处理器是否活跃 + + Args: + max_inactive_minutes: 最大不活跃分钟数 + + Returns: + 是否活跃 + """ + current_time = time.time() + max_inactive_seconds = max_inactive_minutes * 60 + return (current_time - self.last_activity_time) < max_inactive_seconds + + def get_activity_time(self) -> float: + """ + 获取最后活动时间 + + Returns: + 最后活动时间戳 + """ + return self.last_activity_time + + def __str__(self) -> str: + """字符串表示""" + return f"AffinityChatter(stream_id={self.stream_id}, messages={self.stats['messages_processed']})" + + def __repr__(self) -> str: + """详细字符串表示""" + return ( + f"AffinityChatter(stream_id={self.stream_id}, " + f"messages_processed={self.stats['messages_processed']}, " + f"plans_created={self.stats['plans_created']}, " + f"last_activity={datetime.fromtimestamp(self.last_activity_time)})" + ) \ No newline at end of file diff --git a/src/plugins/built_in/chatter/plugin.py b/src/plugins/built_in/chatter/plugin.py new file mode 100644 index 000000000..201b021f5 --- /dev/null +++ b/src/plugins/built_in/chatter/plugin.py @@ -0,0 +1,46 @@ +""" +亲和力聊天处理器插件 +""" + +from typing import List, Tuple, Type + +from src.plugin_system.apis.plugin_register_api import register_plugin +from src.plugin_system.base.base_plugin import BasePlugin +from src.plugin_system.base.component_types import ComponentInfo, ChatterInfo, ComponentType, ChatType +from src.common.logger import get_logger + +logger = get_logger("affinity_chatter_plugin") + + +@register_plugin +class AffinityChatterPlugin(BasePlugin): + """亲和力聊天处理器插件 + + - 延迟导入 `AffinityChatter` 并通过组件注册器注册为聊天处理器 + - 提供 `get_plugin_components` 以兼容插件注册机制 + """ + + plugin_name: str = "affinity_chatter" + enable_plugin: bool = True + dependencies: list[str] = [] + python_dependencies: list[str] = [] + config_file_name: str = "" + + # 简单的 config_schema 占位(如果将来需要配置可扩展) + config_schema = {} + + def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: + """返回插件包含的组件列表(ChatterInfo, AffinityChatter) + + 这里采用延迟导入 AffinityChatter 来避免循环依赖和启动顺序问题。 + 如果导入失败则返回空列表以让注册过程继续而不崩溃。 + """ + try: + # 延迟导入以避免循环导入 + from .affinity_chatter import AffinityChatter + + return [(AffinityChatter.get_chatter_info(), AffinityChatter)] + + except Exception as e: + logger.error(f"加载 AffinityChatter 时出错: {e}") + return []