重构整个插件系统,尝试恢复可启动性,新增插件系统maibot-plugin-sdk依赖
This commit is contained in:
6
src/core/__init__.py
Normal file
6
src/core/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
MaiBot 核心基础设施
|
||||
|
||||
提供与插件系统无关的核心类型定义、配置 schema 等基础设施。
|
||||
这些类型被整个项目共享,包括内部模块、服务层、旧插件系统和新插件运行时。
|
||||
"""
|
||||
120
src/core/announcement_manager.py
Normal file
120
src/core/announcement_manager.py
Normal file
@@ -0,0 +1,120 @@
|
||||
from typing import List, Dict
|
||||
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("global_announcement_manager")
|
||||
|
||||
|
||||
class GlobalAnnouncementManager:
|
||||
def __init__(self) -> None:
|
||||
# 用户禁用的动作,chat_id -> [action_name]
|
||||
self._user_disabled_actions: Dict[str, List[str]] = {}
|
||||
# 用户禁用的命令,chat_id -> [command_name]
|
||||
self._user_disabled_commands: Dict[str, List[str]] = {}
|
||||
# 用户禁用的事件处理器,chat_id -> [handler_name]
|
||||
self._user_disabled_event_handlers: Dict[str, List[str]] = {}
|
||||
# 用户禁用的工具,chat_id -> [tool_name]
|
||||
self._user_disabled_tools: Dict[str, List[str]] = {}
|
||||
|
||||
def disable_specific_chat_action(self, chat_id: str, action_name: str) -> bool:
|
||||
"""禁用特定聊天的某个动作"""
|
||||
if chat_id not in self._user_disabled_actions:
|
||||
self._user_disabled_actions[chat_id] = []
|
||||
if action_name in self._user_disabled_actions[chat_id]:
|
||||
logger.warning(f"动作 {action_name} 已经被禁用")
|
||||
return False
|
||||
self._user_disabled_actions[chat_id].append(action_name)
|
||||
return True
|
||||
|
||||
def enable_specific_chat_action(self, chat_id: str, action_name: str) -> bool:
|
||||
"""启用特定聊天的某个动作"""
|
||||
if chat_id in self._user_disabled_actions:
|
||||
try:
|
||||
self._user_disabled_actions[chat_id].remove(action_name)
|
||||
return True
|
||||
except ValueError:
|
||||
logger.warning(f"动作 {action_name} 不在禁用列表中")
|
||||
return False
|
||||
return False
|
||||
|
||||
def disable_specific_chat_command(self, chat_id: str, command_name: str) -> bool:
|
||||
"""禁用特定聊天的某个命令"""
|
||||
if chat_id not in self._user_disabled_commands:
|
||||
self._user_disabled_commands[chat_id] = []
|
||||
if command_name in self._user_disabled_commands[chat_id]:
|
||||
logger.warning(f"命令 {command_name} 已经被禁用")
|
||||
return False
|
||||
self._user_disabled_commands[chat_id].append(command_name)
|
||||
return True
|
||||
|
||||
def enable_specific_chat_command(self, chat_id: str, command_name: str) -> bool:
|
||||
"""启用特定聊天的某个命令"""
|
||||
if chat_id in self._user_disabled_commands:
|
||||
try:
|
||||
self._user_disabled_commands[chat_id].remove(command_name)
|
||||
return True
|
||||
except ValueError:
|
||||
logger.warning(f"命令 {command_name} 不在禁用列表中")
|
||||
return False
|
||||
return False
|
||||
|
||||
def disable_specific_chat_event_handler(self, chat_id: str, handler_name: str) -> bool:
|
||||
"""禁用特定聊天的某个事件处理器"""
|
||||
if chat_id not in self._user_disabled_event_handlers:
|
||||
self._user_disabled_event_handlers[chat_id] = []
|
||||
if handler_name in self._user_disabled_event_handlers[chat_id]:
|
||||
logger.warning(f"事件处理器 {handler_name} 已经被禁用")
|
||||
return False
|
||||
self._user_disabled_event_handlers[chat_id].append(handler_name)
|
||||
return True
|
||||
|
||||
def enable_specific_chat_event_handler(self, chat_id: str, handler_name: str) -> bool:
|
||||
"""启用特定聊天的某个事件处理器"""
|
||||
if chat_id in self._user_disabled_event_handlers:
|
||||
try:
|
||||
self._user_disabled_event_handlers[chat_id].remove(handler_name)
|
||||
return True
|
||||
except ValueError:
|
||||
logger.warning(f"事件处理器 {handler_name} 不在禁用列表中")
|
||||
return False
|
||||
return False
|
||||
|
||||
def disable_specific_chat_tool(self, chat_id: str, tool_name: str) -> bool:
|
||||
"""禁用特定聊天的某个工具"""
|
||||
if chat_id not in self._user_disabled_tools:
|
||||
self._user_disabled_tools[chat_id] = []
|
||||
if tool_name in self._user_disabled_tools[chat_id]:
|
||||
logger.warning(f"工具 {tool_name} 已经被禁用")
|
||||
return False
|
||||
self._user_disabled_tools[chat_id].append(tool_name)
|
||||
return True
|
||||
|
||||
def enable_specific_chat_tool(self, chat_id: str, tool_name: str) -> bool:
|
||||
"""启用特定聊天的某个工具"""
|
||||
if chat_id in self._user_disabled_tools:
|
||||
try:
|
||||
self._user_disabled_tools[chat_id].remove(tool_name)
|
||||
return True
|
||||
except ValueError:
|
||||
logger.warning(f"工具 {tool_name} 不在禁用列表中")
|
||||
return False
|
||||
return False
|
||||
|
||||
def get_disabled_chat_actions(self, chat_id: str) -> List[str]:
|
||||
"""获取特定聊天禁用的所有动作"""
|
||||
return self._user_disabled_actions.get(chat_id, []).copy()
|
||||
|
||||
def get_disabled_chat_commands(self, chat_id: str) -> List[str]:
|
||||
"""获取特定聊天禁用的所有命令"""
|
||||
return self._user_disabled_commands.get(chat_id, []).copy()
|
||||
|
||||
def get_disabled_chat_event_handlers(self, chat_id: str) -> List[str]:
|
||||
"""获取特定聊天禁用的所有事件处理器"""
|
||||
return self._user_disabled_event_handlers.get(chat_id, []).copy()
|
||||
|
||||
def get_disabled_chat_tools(self, chat_id: str) -> List[str]:
|
||||
"""获取特定聊天禁用的所有工具"""
|
||||
return self._user_disabled_tools.get(chat_id, []).copy()
|
||||
|
||||
|
||||
global_announcement_manager = GlobalAnnouncementManager()
|
||||
242
src/core/component_registry.py
Normal file
242
src/core/component_registry.py
Normal file
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
核心组件注册表
|
||||
|
||||
面向最终架构的组件管理:
|
||||
- Action:注册 ActionInfo + 执行器(本地 callable 或 IPC 路由)
|
||||
- Command:注册正则模式 + 执行器
|
||||
- Tool:注册工具定义 + 执行器
|
||||
|
||||
不依赖任何插件基类,组件执行器是纯 async callable。
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Pattern, Tuple, Union
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.core.types import (
|
||||
ActionActivationType,
|
||||
ActionInfo,
|
||||
CommandInfo,
|
||||
ComponentInfo,
|
||||
ComponentType,
|
||||
ToolInfo,
|
||||
)
|
||||
|
||||
logger = get_logger("component_registry")
|
||||
|
||||
# 执行器类型
|
||||
ActionExecutor = Callable[..., Awaitable[Any]]
|
||||
CommandExecutor = Callable[..., Awaitable[Tuple[bool, Optional[str], bool]]]
|
||||
ToolExecutor = Callable[..., Awaitable[Any]]
|
||||
|
||||
|
||||
class ComponentRegistry:
|
||||
"""核心组件注册表
|
||||
|
||||
管理 action、command、tool 三类组件。
|
||||
每个组件由「元信息 + 执行器」构成,执行器是 async callable,
|
||||
不需要继承任何基类。
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Action 注册
|
||||
self._actions: Dict[str, ActionInfo] = {}
|
||||
self._action_executors: Dict[str, ActionExecutor] = {}
|
||||
self._default_actions: Dict[str, ActionInfo] = {}
|
||||
|
||||
# Command 注册
|
||||
self._commands: Dict[str, CommandInfo] = {}
|
||||
self._command_executors: Dict[str, CommandExecutor] = {}
|
||||
self._command_patterns: Dict[Pattern, str] = {}
|
||||
|
||||
# Tool 注册
|
||||
self._tools: Dict[str, ToolInfo] = {}
|
||||
self._tool_executors: Dict[str, ToolExecutor] = {}
|
||||
self._llm_available_tools: Dict[str, ToolInfo] = {}
|
||||
|
||||
# 插件配置(plugin_name -> config dict)
|
||||
self._plugin_configs: Dict[str, dict] = {}
|
||||
|
||||
logger.info("核心组件注册表初始化完成")
|
||||
|
||||
# ========== Action ==========
|
||||
|
||||
def register_action(
|
||||
self,
|
||||
info: ActionInfo,
|
||||
executor: ActionExecutor,
|
||||
) -> bool:
|
||||
"""注册 action
|
||||
|
||||
Args:
|
||||
info: action 元信息
|
||||
executor: 执行器,async callable
|
||||
"""
|
||||
name = info.name
|
||||
if name in self._actions:
|
||||
logger.warning(f"Action {name} 已存在,跳过注册")
|
||||
return False
|
||||
|
||||
self._actions[name] = info
|
||||
self._action_executors[name] = executor
|
||||
|
||||
if info.enabled:
|
||||
self._default_actions[name] = info
|
||||
|
||||
logger.debug(f"注册 Action: {name}")
|
||||
return True
|
||||
|
||||
def get_action_info(self, name: str) -> Optional[ActionInfo]:
|
||||
return self._actions.get(name)
|
||||
|
||||
def get_action_executor(self, name: str) -> Optional[ActionExecutor]:
|
||||
return self._action_executors.get(name)
|
||||
|
||||
def get_default_actions(self) -> Dict[str, ActionInfo]:
|
||||
return self._default_actions.copy()
|
||||
|
||||
def get_all_actions(self) -> Dict[str, ActionInfo]:
|
||||
return self._actions.copy()
|
||||
|
||||
def remove_action(self, name: str) -> bool:
|
||||
if name not in self._actions:
|
||||
return False
|
||||
del self._actions[name]
|
||||
self._action_executors.pop(name, None)
|
||||
self._default_actions.pop(name, None)
|
||||
logger.debug(f"移除 Action: {name}")
|
||||
return True
|
||||
|
||||
# ========== Command ==========
|
||||
|
||||
def register_command(
|
||||
self,
|
||||
info: CommandInfo,
|
||||
executor: CommandExecutor,
|
||||
) -> bool:
|
||||
"""注册 command"""
|
||||
name = info.name
|
||||
if name in self._commands:
|
||||
logger.warning(f"Command {name} 已存在,跳过注册")
|
||||
return False
|
||||
|
||||
self._commands[name] = info
|
||||
self._command_executors[name] = executor
|
||||
|
||||
if info.enabled and info.command_pattern:
|
||||
pattern = re.compile(info.command_pattern, re.IGNORECASE | re.DOTALL)
|
||||
self._command_patterns[pattern] = name
|
||||
|
||||
logger.debug(f"注册 Command: {name}")
|
||||
return True
|
||||
|
||||
def find_command_by_text(
|
||||
self, text: str
|
||||
) -> Optional[Tuple[CommandExecutor, dict, CommandInfo]]:
|
||||
"""根据文本查找匹配的命令
|
||||
|
||||
Returns:
|
||||
(executor, matched_groups, command_info) 或 None
|
||||
"""
|
||||
candidates = [p for p in self._command_patterns if p.match(text)]
|
||||
if not candidates:
|
||||
return None
|
||||
if len(candidates) > 1:
|
||||
logger.warning(f"文本 '{text[:50]}' 匹配到多个命令模式,使用第一个")
|
||||
pattern = candidates[0]
|
||||
name = self._command_patterns[pattern]
|
||||
return (
|
||||
self._command_executors[name],
|
||||
pattern.match(text).groupdict(), # type: ignore
|
||||
self._commands[name],
|
||||
)
|
||||
|
||||
def remove_command(self, name: str) -> bool:
|
||||
if name not in self._commands:
|
||||
return False
|
||||
del self._commands[name]
|
||||
self._command_executors.pop(name, None)
|
||||
self._command_patterns = {k: v for k, v in self._command_patterns.items() if v != name}
|
||||
logger.debug(f"移除 Command: {name}")
|
||||
return True
|
||||
|
||||
# ========== Tool ==========
|
||||
|
||||
def register_tool(
|
||||
self,
|
||||
info: ToolInfo,
|
||||
executor: ToolExecutor,
|
||||
) -> bool:
|
||||
"""注册 tool"""
|
||||
name = info.name
|
||||
if name in self._tools:
|
||||
logger.warning(f"Tool {name} 已存在,跳过注册")
|
||||
return False
|
||||
|
||||
self._tools[name] = info
|
||||
self._tool_executors[name] = executor
|
||||
|
||||
if info.enabled:
|
||||
self._llm_available_tools[name] = info
|
||||
|
||||
logger.debug(f"注册 Tool: {name}")
|
||||
return True
|
||||
|
||||
def get_tool_info(self, name: str) -> Optional[ToolInfo]:
|
||||
return self._tools.get(name)
|
||||
|
||||
def get_tool_executor(self, name: str) -> Optional[ToolExecutor]:
|
||||
return self._tool_executors.get(name)
|
||||
|
||||
def get_llm_available_tools(self) -> Dict[str, ToolInfo]:
|
||||
return self._llm_available_tools.copy()
|
||||
|
||||
def get_all_tools(self) -> Dict[str, ToolInfo]:
|
||||
return self._tools.copy()
|
||||
|
||||
def remove_tool(self, name: str) -> bool:
|
||||
if name not in self._tools:
|
||||
return False
|
||||
del self._tools[name]
|
||||
self._tool_executors.pop(name, None)
|
||||
self._llm_available_tools.pop(name, None)
|
||||
logger.debug(f"移除 Tool: {name}")
|
||||
return True
|
||||
|
||||
# ========== 通用查询 ==========
|
||||
|
||||
def get_component_info(self, name: str, component_type: ComponentType) -> Optional[ComponentInfo]:
|
||||
"""获取组件元信息"""
|
||||
match component_type:
|
||||
case ComponentType.ACTION:
|
||||
return self._actions.get(name)
|
||||
case ComponentType.COMMAND:
|
||||
return self._commands.get(name)
|
||||
case ComponentType.TOOL:
|
||||
return self._tools.get(name)
|
||||
case _:
|
||||
return None
|
||||
|
||||
def get_components_by_type(self, component_type: ComponentType) -> Dict[str, ComponentInfo]:
|
||||
"""获取某类型的所有组件"""
|
||||
match component_type:
|
||||
case ComponentType.ACTION:
|
||||
return dict(self._actions)
|
||||
case ComponentType.COMMAND:
|
||||
return dict(self._commands)
|
||||
case ComponentType.TOOL:
|
||||
return dict(self._tools)
|
||||
case _:
|
||||
return {}
|
||||
|
||||
# ========== 插件配置 ==========
|
||||
|
||||
def set_plugin_config(self, plugin_name: str, config: dict) -> None:
|
||||
self._plugin_configs[plugin_name] = config
|
||||
|
||||
def get_plugin_config(self, plugin_name: str) -> Optional[dict]:
|
||||
return self._plugin_configs.get(plugin_name)
|
||||
|
||||
|
||||
# 全局单例
|
||||
component_registry = ComponentRegistry()
|
||||
273
src/core/config_types.py
Normal file
273
src/core/config_types.py
Normal file
@@ -0,0 +1,273 @@
|
||||
"""
|
||||
插件系统配置类型定义
|
||||
|
||||
提供插件配置的类型定义,支持 WebUI 可视化配置编辑。
|
||||
"""
|
||||
|
||||
from typing import Any, Optional, List, Dict, Union
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfigField:
|
||||
"""
|
||||
配置字段定义
|
||||
|
||||
用于定义插件配置项的元数据,支持类型验证、UI 渲染等功能。
|
||||
|
||||
基础示例:
|
||||
ConfigField(type=str, default="", description="API密钥")
|
||||
|
||||
完整示例:
|
||||
ConfigField(
|
||||
type=str,
|
||||
default="",
|
||||
description="API密钥",
|
||||
input_type="password",
|
||||
placeholder="请输入API密钥",
|
||||
required=True,
|
||||
hint="从服务商控制台获取",
|
||||
order=1
|
||||
)
|
||||
"""
|
||||
|
||||
# === 基础字段(必需) ===
|
||||
type: type # 字段类型: str, int, float, bool, list, dict
|
||||
default: Any # 默认值
|
||||
description: str # 字段描述(也用作默认标签)
|
||||
|
||||
# === 验证相关 ===
|
||||
example: Optional[str] = None # 示例值(用于生成配置文件注释)
|
||||
required: bool = False # 是否必需
|
||||
choices: Optional[List[Any]] = field(default_factory=list) # 可选值列表(用于下拉选择)
|
||||
min: Optional[float] = None # 最小值(数字类型)
|
||||
max: Optional[float] = None # 最大值(数字类型)
|
||||
step: Optional[float] = None # 步进值(数字类型)
|
||||
pattern: Optional[str] = None # 正则验证(字符串类型)
|
||||
max_length: Optional[int] = None # 最大长度(字符串类型)
|
||||
|
||||
# === UI 显示控制 ===
|
||||
label: Optional[str] = None # 显示标签(默认使用 description)
|
||||
placeholder: Optional[str] = None # 输入框占位符
|
||||
hint: Optional[str] = None # 字段下方的提示文字
|
||||
icon: Optional[str] = None # 字段图标名称
|
||||
hidden: bool = False # 是否在 UI 中隐藏
|
||||
disabled: bool = False # 是否禁用编辑
|
||||
order: int = 0 # 排序权重(数字越小越靠前)
|
||||
|
||||
# === 输入控件类型 ===
|
||||
# 可选值: text, password, textarea, number, color, code, file, json
|
||||
# 不指定时根据 type 和 choices 自动推断
|
||||
input_type: Optional[str] = None
|
||||
|
||||
# === textarea 专用 ===
|
||||
rows: int = 3 # 文本域行数
|
||||
|
||||
# === 分组与布局 ===
|
||||
group: Optional[str] = None # 字段分组(在 section 内再细分)
|
||||
|
||||
# === 条件显示 ===
|
||||
depends_on: Optional[str] = None # 依赖的字段路径,如 "section.field"
|
||||
depends_value: Any = None # 依赖字段需要的值(当依赖字段等于此值时显示)
|
||||
|
||||
# === 列表类型专用 ===
|
||||
item_type: Optional[str] = None # 数组元素类型: "string", "number", "object"
|
||||
item_fields: Optional[Dict[str, Any]] = None # 当 item_type="object" 时,定义对象的字段结构
|
||||
min_items: Optional[int] = None # 数组最小元素数量
|
||||
max_items: Optional[int] = None # 数组最大元素数量
|
||||
|
||||
def get_ui_type(self) -> str:
|
||||
"""
|
||||
获取 UI 控件类型
|
||||
|
||||
如果指定了 input_type 则直接返回,否则根据 type 和 choices 自动推断。
|
||||
|
||||
Returns:
|
||||
控件类型字符串
|
||||
"""
|
||||
if self.input_type:
|
||||
return self.input_type
|
||||
|
||||
# 根据 type 和 choices 自动推断
|
||||
if self.type is bool:
|
||||
return "switch"
|
||||
elif self.type in (int, float):
|
||||
if self.min is not None and self.max is not None:
|
||||
return "slider"
|
||||
return "number"
|
||||
elif self.type is str:
|
||||
if self.choices:
|
||||
return "select"
|
||||
return "text"
|
||||
elif self.type is list:
|
||||
return "list"
|
||||
elif self.type is dict:
|
||||
return "json"
|
||||
else:
|
||||
return "text"
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""
|
||||
转换为可序列化的字典(用于 API 传输)
|
||||
|
||||
Returns:
|
||||
包含所有配置信息的字典
|
||||
"""
|
||||
return {
|
||||
"type": self.type.__name__ if isinstance(self.type, type) else str(self.type),
|
||||
"default": self.default,
|
||||
"description": self.description,
|
||||
"example": self.example,
|
||||
"required": self.required,
|
||||
"choices": self.choices if self.choices else None,
|
||||
"min": self.min,
|
||||
"max": self.max,
|
||||
"step": self.step,
|
||||
"pattern": self.pattern,
|
||||
"max_length": self.max_length,
|
||||
"label": self.label or self.description,
|
||||
"placeholder": self.placeholder,
|
||||
"hint": self.hint,
|
||||
"icon": self.icon,
|
||||
"hidden": self.hidden,
|
||||
"disabled": self.disabled,
|
||||
"order": self.order,
|
||||
"input_type": self.input_type,
|
||||
"ui_type": self.get_ui_type(),
|
||||
"rows": self.rows,
|
||||
"group": self.group,
|
||||
"depends_on": self.depends_on,
|
||||
"depends_value": self.depends_value,
|
||||
"item_type": self.item_type,
|
||||
"item_fields": self.item_fields,
|
||||
"min_items": self.min_items,
|
||||
"max_items": self.max_items,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfigSection:
|
||||
"""
|
||||
配置节定义
|
||||
|
||||
用于描述配置文件中一个 section 的元数据。
|
||||
|
||||
示例:
|
||||
ConfigSection(
|
||||
title="API配置",
|
||||
description="外部API连接参数",
|
||||
icon="cloud",
|
||||
order=1
|
||||
)
|
||||
"""
|
||||
|
||||
title: str # 显示标题
|
||||
description: Optional[str] = None # 详细描述
|
||||
icon: Optional[str] = None # 图标名称
|
||||
collapsed: bool = False # 默认是否折叠
|
||||
order: int = 0 # 排序权重
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""转换为可序列化的字典"""
|
||||
return {
|
||||
"title": self.title,
|
||||
"description": self.description,
|
||||
"icon": self.icon,
|
||||
"collapsed": self.collapsed,
|
||||
"order": self.order,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfigTab:
|
||||
"""
|
||||
配置标签页定义
|
||||
|
||||
用于将多个 section 组织到一个标签页中。
|
||||
|
||||
示例:
|
||||
ConfigTab(
|
||||
id="general",
|
||||
title="通用设置",
|
||||
icon="settings",
|
||||
sections=["plugin", "api"]
|
||||
)
|
||||
"""
|
||||
|
||||
id: str # 标签页 ID
|
||||
title: str # 显示标题
|
||||
sections: List[str] = field(default_factory=list) # 包含的 section 名称列表
|
||||
icon: Optional[str] = None # 图标名称
|
||||
order: int = 0 # 排序权重
|
||||
badge: Optional[str] = None # 角标文字(如 "Beta", "New")
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""转换为可序列化的字典"""
|
||||
return {
|
||||
"id": self.id,
|
||||
"title": self.title,
|
||||
"sections": self.sections,
|
||||
"icon": self.icon,
|
||||
"order": self.order,
|
||||
"badge": self.badge,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfigLayout:
|
||||
"""
|
||||
配置页面布局定义
|
||||
|
||||
用于定义插件配置页面的整体布局结构。
|
||||
|
||||
布局类型:
|
||||
- "auto": 自动布局,sections 作为折叠面板显示
|
||||
- "tabs": 标签页布局
|
||||
- "pages": 分页布局(左侧导航 + 右侧内容)
|
||||
|
||||
简单示例(标签页布局):
|
||||
ConfigLayout(
|
||||
type="tabs",
|
||||
tabs=[
|
||||
ConfigTab(id="basic", title="基础", sections=["plugin", "api"]),
|
||||
ConfigTab(id="advanced", title="高级", sections=["debug"]),
|
||||
]
|
||||
)
|
||||
"""
|
||||
|
||||
type: str = "auto" # 布局类型: auto, tabs, pages
|
||||
tabs: List[ConfigTab] = field(default_factory=list) # 标签页列表
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""转换为可序列化的字典"""
|
||||
return {
|
||||
"type": self.type,
|
||||
"tabs": [tab.to_dict() for tab in self.tabs],
|
||||
}
|
||||
|
||||
|
||||
def section_meta(
|
||||
title: str, description: Optional[str] = None, icon: Optional[str] = None, collapsed: bool = False, order: int = 0
|
||||
) -> Union[str, ConfigSection]:
|
||||
"""
|
||||
便捷函数:创建 section 元数据
|
||||
|
||||
可以在 config_section_descriptions 中使用,提供比纯字符串更丰富的信息。
|
||||
|
||||
Args:
|
||||
title: 显示标题
|
||||
description: 详细描述
|
||||
icon: 图标名称
|
||||
collapsed: 默认是否折叠
|
||||
order: 排序权重
|
||||
|
||||
Returns:
|
||||
ConfigSection 实例
|
||||
|
||||
示例:
|
||||
config_section_descriptions = {
|
||||
"api": section_meta("API配置", icon="cloud", order=1),
|
||||
"debug": section_meta("调试设置", collapsed=True, order=99),
|
||||
}
|
||||
"""
|
||||
return ConfigSection(title=title, description=description, icon=icon, collapsed=collapsed, order=order)
|
||||
216
src/core/event_bus.py
Normal file
216
src/core/event_bus.py
Normal file
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
核心事件总线
|
||||
|
||||
面向最终架构的事件系统:
|
||||
- 内部 handler 直接注册 async callable
|
||||
- IPC 插件通过 plugin_runtime 桥接
|
||||
- 不依赖任何插件基类
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, TYPE_CHECKING
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.core.types import EventType, MaiMessages
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.common.data_models.llm_data_model import LLMGenerationDataModel
|
||||
|
||||
logger = get_logger("event_bus")
|
||||
|
||||
# Handler 签名:接收 MaiMessages,返回 (continue, modified_message)
|
||||
EventHandler = Callable[[Optional[MaiMessages]], Awaitable[Tuple[bool, Optional[MaiMessages]]]]
|
||||
|
||||
|
||||
class EventBus:
|
||||
"""核心事件总线
|
||||
|
||||
支持两种 handler:
|
||||
- 拦截型(intercept=True):同步顺序执行,可修改消息、可中断流程
|
||||
- 非拦截型(intercept=False):异步并发执行,fire-and-forget
|
||||
|
||||
handler 是纯 async callable,不需要继承任何基类。
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# event_type -> [(handler, name, weight, intercept)]
|
||||
self._handlers: Dict[EventType | str, List[_HandlerEntry]] = {}
|
||||
self._running_tasks: Dict[str, List[asyncio.Task]] = {}
|
||||
|
||||
# 预注册所有内置事件类型
|
||||
for event in EventType:
|
||||
self._handlers[event] = []
|
||||
|
||||
def subscribe(
|
||||
self,
|
||||
event_type: EventType | str,
|
||||
handler: EventHandler,
|
||||
name: str,
|
||||
weight: int = 0,
|
||||
intercept: bool = False,
|
||||
) -> None:
|
||||
"""注册事件 handler
|
||||
|
||||
Args:
|
||||
event_type: 事件类型
|
||||
handler: async callable,签名 (Optional[MaiMessages]) -> (bool, Optional[MaiMessages])
|
||||
name: handler 标识名
|
||||
weight: 权重,越大越先执行
|
||||
intercept: 是否为拦截型(同步执行,可中断流程)
|
||||
"""
|
||||
if event_type not in self._handlers:
|
||||
self._handlers[event_type] = []
|
||||
|
||||
entry = _HandlerEntry(handler=handler, name=name, weight=weight, intercept=intercept)
|
||||
self._handlers[event_type].append(entry)
|
||||
self._handlers[event_type].sort(key=lambda e: e.weight, reverse=True)
|
||||
logger.debug(f"注册事件 handler: {name} -> {event_type} (weight={weight}, intercept={intercept})")
|
||||
|
||||
def unsubscribe(self, event_type: EventType | str, name: str) -> bool:
|
||||
"""取消注册事件 handler"""
|
||||
handlers = self._handlers.get(event_type, [])
|
||||
for i, entry in enumerate(handlers):
|
||||
if entry.name == name:
|
||||
del handlers[i]
|
||||
logger.debug(f"取消注册事件 handler: {name} <- {event_type}")
|
||||
return True
|
||||
return False
|
||||
|
||||
async def emit(
|
||||
self,
|
||||
event_type: EventType | str,
|
||||
message: Optional[MaiMessages] = None,
|
||||
) -> Tuple[bool, Optional[MaiMessages]]:
|
||||
"""触发事件
|
||||
|
||||
按权重顺序执行所有 handler:
|
||||
- 拦截型 handler 同步执行,可修改消息和中断流程
|
||||
- 非拦截型 handler 异步 fire-and-forget
|
||||
|
||||
Args:
|
||||
event_type: 事件类型
|
||||
message: 事件消息(可选)
|
||||
|
||||
Returns:
|
||||
(continue_flag, modified_message)
|
||||
- continue_flag: False 表示某个拦截型 handler 要求中断
|
||||
- modified_message: 被拦截型 handler 修改后的消息
|
||||
"""
|
||||
handlers = self._handlers.get(event_type, [])
|
||||
if not handlers:
|
||||
return True, None
|
||||
|
||||
continue_flag = True
|
||||
current_message = message.deepcopy() if message else None
|
||||
|
||||
for entry in handlers:
|
||||
if entry.intercept:
|
||||
try:
|
||||
should_continue, modified = await entry.handler(current_message)
|
||||
if modified is not None:
|
||||
current_message = modified
|
||||
if not should_continue:
|
||||
continue_flag = False
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"拦截型 handler {entry.name} 执行异常: {e}", exc_info=True)
|
||||
else:
|
||||
self._fire_and_forget(entry, event_type, current_message)
|
||||
|
||||
# 桥接到 IPC 插件运行时
|
||||
continue_flag, current_message = await self._bridge_to_ipc_runtime(
|
||||
event_type, continue_flag, current_message
|
||||
)
|
||||
|
||||
return continue_flag, current_message
|
||||
|
||||
async def cancel_handler_tasks(self, handler_name: str) -> None:
|
||||
"""取消某个 handler 的所有运行中任务"""
|
||||
tasks = self._running_tasks.pop(handler_name, [])
|
||||
remaining = [t for t in tasks if not t.done()]
|
||||
if remaining:
|
||||
for t in remaining:
|
||||
t.cancel()
|
||||
await asyncio.gather(*remaining, return_exceptions=True)
|
||||
logger.info(f"已取消 handler {handler_name} 的 {len(remaining)} 个任务")
|
||||
|
||||
# --- 内部方法 ---
|
||||
|
||||
def _fire_and_forget(
|
||||
self,
|
||||
entry: "_HandlerEntry",
|
||||
event_type: EventType | str,
|
||||
message: Optional[MaiMessages],
|
||||
) -> None:
|
||||
"""创建异步任务执行非拦截型 handler"""
|
||||
try:
|
||||
task = asyncio.create_task(entry.handler(message))
|
||||
task.set_name(entry.name)
|
||||
task.add_done_callback(lambda t: self._task_done_callback(t, entry.name))
|
||||
self._running_tasks.setdefault(entry.name, []).append(task)
|
||||
except Exception as e:
|
||||
logger.error(f"创建 handler 任务 {entry.name} 失败: {e}", exc_info=True)
|
||||
|
||||
def _task_done_callback(self, task: asyncio.Task, handler_name: str) -> None:
|
||||
"""异步任务完成回调"""
|
||||
try:
|
||||
if task.cancelled():
|
||||
return
|
||||
exc = task.exception()
|
||||
if exc:
|
||||
logger.error(f"handler {handler_name} 异步任务异常: {exc}")
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
task_list = self._running_tasks.get(handler_name, [])
|
||||
try:
|
||||
task_list.remove(task)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
async def _bridge_to_ipc_runtime(
|
||||
self,
|
||||
event_type: EventType | str,
|
||||
continue_flag: bool,
|
||||
message: Optional[MaiMessages],
|
||||
) -> Tuple[bool, Optional[MaiMessages]]:
|
||||
"""将事件桥接到 IPC 插件运行时"""
|
||||
if not continue_flag:
|
||||
return continue_flag, message
|
||||
|
||||
try:
|
||||
from src.plugin_runtime.integration import get_plugin_runtime_manager
|
||||
|
||||
prm = get_plugin_runtime_manager()
|
||||
if not prm.is_running:
|
||||
return continue_flag, message
|
||||
|
||||
event_value = event_type.value if isinstance(event_type, EventType) else str(event_type)
|
||||
message_dict = message.to_dict() if message and hasattr(message, "to_dict") else None
|
||||
|
||||
new_continue, _ = await prm.bridge_event(
|
||||
event_type_value=event_value,
|
||||
message_dict=message_dict,
|
||||
)
|
||||
if not new_continue:
|
||||
continue_flag = False
|
||||
except Exception as e:
|
||||
logger.warning(f"桥接事件到 IPC 运行时失败: {e}")
|
||||
|
||||
return continue_flag, message
|
||||
|
||||
|
||||
class _HandlerEntry:
|
||||
"""内部 handler 条目"""
|
||||
|
||||
__slots__ = ("handler", "name", "weight", "intercept")
|
||||
|
||||
def __init__(self, handler: EventHandler, name: str, weight: int, intercept: bool):
|
||||
self.handler = handler
|
||||
self.name = name
|
||||
self.weight = weight
|
||||
self.intercept = intercept
|
||||
|
||||
|
||||
# 全局单例
|
||||
event_bus = EventBus()
|
||||
416
src/core/types.py
Normal file
416
src/core/types.py
Normal file
@@ -0,0 +1,416 @@
|
||||
import copy
|
||||
import warnings
|
||||
from enum import Enum
|
||||
from typing import Dict, Any, List, Optional, Tuple
|
||||
from dataclasses import dataclass, field
|
||||
from maim_message import Seg
|
||||
|
||||
from src.llm_models.payload_content.tool_option import ToolParamType as ToolParamType
|
||||
from src.llm_models.payload_content.tool_option import ToolCall as ToolCall
|
||||
from src.common.data_models.message_data_model import ReplyContentType as ReplyContentType
|
||||
from src.common.data_models.message_data_model import ReplyContent as ReplyContent
|
||||
from src.common.data_models.message_data_model import ForwardNode as ForwardNode
|
||||
from src.common.data_models.message_data_model import ReplySetModel as ReplySetModel
|
||||
|
||||
|
||||
# 组件类型枚举
|
||||
class ComponentType(Enum):
|
||||
"""组件类型枚举"""
|
||||
|
||||
ACTION = "action" # 动作组件
|
||||
COMMAND = "command" # 命令组件
|
||||
TOOL = "tool" # 服务组件(预留)
|
||||
SCHEDULER = "scheduler" # 定时任务组件(预留)
|
||||
EVENT_HANDLER = "event_handler" # 事件处理组件(预留)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
|
||||
|
||||
# 动作激活类型枚举
|
||||
class ActionActivationType(Enum):
|
||||
"""动作激活类型枚举"""
|
||||
|
||||
NEVER = "never" # 从不激活(默认关闭)
|
||||
ALWAYS = "always" # 默认参与到planner
|
||||
RANDOM = "random" # 随机启用action到planner
|
||||
KEYWORD = "keyword" # 关键词触发启用action到planner
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
# 聊天模式枚举
|
||||
class ChatMode(Enum):
|
||||
"""聊天模式枚举"""
|
||||
|
||||
FOCUS = "focus" # Focus聊天模式
|
||||
NORMAL = "normal" # Normal聊天模式
|
||||
PRIORITY = "priority" # 优先级聊天模式
|
||||
ALL = "all" # 所有聊天模式
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
# 事件类型枚举
|
||||
class EventType(Enum):
|
||||
"""
|
||||
事件类型枚举类
|
||||
"""
|
||||
|
||||
ON_START = "on_start" # 启动事件,用于调用按时任务
|
||||
ON_STOP = "on_stop" # 停止事件,用于调用按时任务
|
||||
ON_MESSAGE_PRE_PROCESS = "on_message_pre_process"
|
||||
ON_MESSAGE = "on_message"
|
||||
ON_PLAN = "on_plan"
|
||||
POST_LLM = "post_llm"
|
||||
AFTER_LLM = "after_llm"
|
||||
POST_SEND_PRE_PROCESS = "post_send_pre_process"
|
||||
POST_SEND = "post_send"
|
||||
AFTER_SEND = "after_send"
|
||||
UNKNOWN = "unknown" # 未知事件类型
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
|
||||
|
||||
@dataclass
|
||||
class PythonDependency:
|
||||
"""Python包依赖信息"""
|
||||
|
||||
package_name: str # 包名称
|
||||
version: str = "" # 版本要求,例如: ">=1.0.0", "==2.1.3", ""表示任意版本
|
||||
optional: bool = False # 是否为可选依赖
|
||||
description: str = "" # 依赖描述
|
||||
install_name: str = "" # 安装时的包名(如果与import名不同)
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.install_name:
|
||||
self.install_name = self.package_name
|
||||
|
||||
def get_pip_requirement(self) -> str:
|
||||
"""获取pip安装格式的依赖字符串"""
|
||||
if self.version:
|
||||
return f"{self.install_name}{self.version}"
|
||||
return self.install_name
|
||||
|
||||
|
||||
@dataclass
|
||||
class ComponentInfo:
|
||||
"""组件信息"""
|
||||
|
||||
name: str # 组件名称
|
||||
component_type: ComponentType # 组件类型
|
||||
description: str = "" # 组件描述
|
||||
enabled: bool = True # 是否启用
|
||||
plugin_name: str = "" # 所属插件名称
|
||||
is_built_in: bool = False # 是否为内置组件
|
||||
metadata: Dict[str, Any] = field(default_factory=dict) # 额外元数据
|
||||
|
||||
def __post_init__(self):
|
||||
if self.metadata is None:
|
||||
self.metadata = {}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ActionInfo(ComponentInfo):
|
||||
"""动作组件信息"""
|
||||
|
||||
action_parameters: Dict[str, str] = field(
|
||||
default_factory=dict
|
||||
) # 动作参数与描述,例如 {"param1": "描述1", "param2": "描述2"}
|
||||
action_require: List[str] = field(default_factory=list) # 动作需求说明
|
||||
associated_types: List[str] = field(default_factory=list) # 关联的消息类型
|
||||
# 激活类型相关
|
||||
focus_activation_type: ActionActivationType = ActionActivationType.ALWAYS # 已弃用
|
||||
normal_activation_type: ActionActivationType = ActionActivationType.ALWAYS # 已弃用
|
||||
activation_type: ActionActivationType = ActionActivationType.ALWAYS
|
||||
random_activation_probability: float = 0.0
|
||||
activation_keywords: List[str] = field(default_factory=list) # 激活关键词列表
|
||||
keyword_case_sensitive: bool = False
|
||||
# 模式和并行设置
|
||||
parallel_action: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
if self.activation_keywords is None:
|
||||
self.activation_keywords = []
|
||||
if self.action_parameters is None:
|
||||
self.action_parameters = {}
|
||||
if self.action_require is None:
|
||||
self.action_require = []
|
||||
if self.associated_types is None:
|
||||
self.associated_types = []
|
||||
self.component_type = ComponentType.ACTION
|
||||
|
||||
|
||||
@dataclass
|
||||
class CommandInfo(ComponentInfo):
|
||||
"""命令组件信息"""
|
||||
|
||||
command_pattern: str = "" # 命令匹配模式(正则表达式)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.component_type = ComponentType.COMMAND
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolInfo(ComponentInfo):
|
||||
"""工具组件信息"""
|
||||
|
||||
tool_parameters: List[Tuple[str, ToolParamType, str, bool, List[str] | None]] = field(
|
||||
default_factory=list
|
||||
) # 工具参数定义
|
||||
tool_description: str = "" # 工具描述
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.component_type = ComponentType.TOOL
|
||||
|
||||
def get_llm_definition(self) -> dict:
|
||||
"""生成 LLM function-calling 所需的工具定义"""
|
||||
return {
|
||||
"name": self.name,
|
||||
"description": self.tool_description,
|
||||
"parameters": self.tool_parameters,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class EventHandlerInfo(ComponentInfo):
|
||||
"""事件处理器组件信息"""
|
||||
|
||||
event_type: EventType | str = EventType.ON_MESSAGE # 监听事件类型
|
||||
intercept_message: bool = False # 是否拦截消息处理(默认不拦截)
|
||||
weight: int = 0 # 事件处理器权重,决定执行顺序
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.component_type = ComponentType.EVENT_HANDLER
|
||||
|
||||
|
||||
@dataclass
|
||||
class PluginInfo:
|
||||
"""插件信息"""
|
||||
|
||||
display_name: str # 插件显示名称
|
||||
name: str # 插件名称
|
||||
description: str # 插件描述
|
||||
version: str = "1.0.0" # 插件版本
|
||||
author: str = "" # 插件作者
|
||||
enabled: bool = True # 是否启用
|
||||
is_built_in: bool = False # 是否为内置插件
|
||||
components: List[ComponentInfo] = field(default_factory=list) # 包含的组件列表
|
||||
dependencies: List[str] = field(default_factory=list) # 依赖的其他插件
|
||||
python_dependencies: List[PythonDependency] = field(default_factory=list) # Python包依赖
|
||||
config_file: str = "" # 配置文件路径
|
||||
metadata: Dict[str, Any] = field(default_factory=dict) # 额外元数据
|
||||
# 新增:manifest相关信息
|
||||
manifest_data: Dict[str, Any] = field(default_factory=dict) # manifest文件数据
|
||||
license: str = "" # 插件许可证
|
||||
homepage_url: str = "" # 插件主页
|
||||
repository_url: str = "" # 插件仓库地址
|
||||
keywords: List[str] = field(default_factory=list) # 插件关键词
|
||||
categories: List[str] = field(default_factory=list) # 插件分类
|
||||
min_host_version: str = "" # 最低主机版本要求
|
||||
max_host_version: str = "" # 最高主机版本要求
|
||||
|
||||
def __post_init__(self):
|
||||
if self.components is None:
|
||||
self.components = []
|
||||
if self.dependencies is None:
|
||||
self.dependencies = []
|
||||
if self.python_dependencies is None:
|
||||
self.python_dependencies = []
|
||||
if self.metadata is None:
|
||||
self.metadata = {}
|
||||
if self.manifest_data is None:
|
||||
self.manifest_data = {}
|
||||
if self.keywords is None:
|
||||
self.keywords = []
|
||||
if self.categories is None:
|
||||
self.categories = []
|
||||
|
||||
def get_missing_packages(self) -> List[PythonDependency]:
|
||||
"""检查缺失的Python包"""
|
||||
missing = []
|
||||
for dep in self.python_dependencies:
|
||||
try:
|
||||
__import__(dep.package_name)
|
||||
except ImportError:
|
||||
if not dep.optional:
|
||||
missing.append(dep)
|
||||
return missing
|
||||
|
||||
def get_pip_requirements(self) -> List[str]:
|
||||
"""获取所有pip安装格式的依赖"""
|
||||
return [dep.get_pip_requirement() for dep in self.python_dependencies]
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModifyFlag:
|
||||
modify_message_segments: bool = False
|
||||
modify_plain_text: bool = False
|
||||
modify_llm_prompt: bool = False
|
||||
modify_llm_response_content: bool = False
|
||||
modify_llm_response_reasoning: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class MaiMessages:
|
||||
"""MaiM插件消息"""
|
||||
|
||||
message_segments: List[Seg] = field(default_factory=list)
|
||||
"""消息段列表,支持多段消息"""
|
||||
|
||||
message_base_info: Dict[str, Any] = field(default_factory=dict)
|
||||
"""消息基本信息,包含平台,用户信息等数据"""
|
||||
|
||||
plain_text: str = ""
|
||||
"""纯文本消息内容"""
|
||||
|
||||
raw_message: Optional[str] = None
|
||||
"""原始消息内容"""
|
||||
|
||||
is_group_message: bool = False
|
||||
"""是否为群组消息"""
|
||||
|
||||
is_private_message: bool = False
|
||||
"""是否为私聊消息"""
|
||||
|
||||
stream_id: Optional[str] = None
|
||||
"""流ID,用于标识消息流"""
|
||||
|
||||
llm_prompt: Optional[str] = None
|
||||
"""LLM提示词"""
|
||||
|
||||
llm_response_content: Optional[str] = None
|
||||
"""LLM响应内容"""
|
||||
|
||||
llm_response_reasoning: Optional[str] = None
|
||||
"""LLM响应推理内容"""
|
||||
|
||||
llm_response_model: Optional[str] = None
|
||||
"""LLM响应模型名称"""
|
||||
|
||||
llm_response_tool_call: Optional[List[ToolCall]] = None
|
||||
"""LLM使用的工具调用"""
|
||||
|
||||
action_usage: Optional[List[str]] = None
|
||||
"""使用的Action"""
|
||||
|
||||
additional_data: Dict[Any, Any] = field(default_factory=dict)
|
||||
"""附加数据,可以存储额外信息"""
|
||||
|
||||
_modify_flags: ModifyFlag = field(default_factory=ModifyFlag)
|
||||
|
||||
def __post_init__(self):
|
||||
if self.message_segments is None:
|
||||
self.message_segments = []
|
||||
|
||||
def deepcopy(self):
|
||||
return copy.deepcopy(self)
|
||||
|
||||
def modify_message_segments(self, new_segments: List[Seg], suppress_warning: bool = False):
|
||||
"""
|
||||
修改消息段列表
|
||||
|
||||
Warning:
|
||||
在生成了plain_text的情况下调用此方法,可能会导致plain_text内容与消息段不一致
|
||||
|
||||
Args:
|
||||
new_segments (List[Seg]): 新的消息段列表
|
||||
"""
|
||||
if self.plain_text and not suppress_warning:
|
||||
warnings.warn(
|
||||
"修改消息段后,plain_text可能与消息段内容不一致,建议同时更新plain_text",
|
||||
UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.message_segments = new_segments
|
||||
self._modify_flags.modify_message_segments = True
|
||||
|
||||
def modify_llm_prompt(self, new_prompt: str, suppress_warning: bool = False):
|
||||
"""
|
||||
修改LLM提示词
|
||||
|
||||
Warning:
|
||||
在没有生成llm_prompt的情况下调用此方法,可能会导致修改无效
|
||||
|
||||
Args:
|
||||
new_prompt (str): 新的提示词内容
|
||||
"""
|
||||
if self.llm_prompt is None and not suppress_warning:
|
||||
warnings.warn(
|
||||
"当前llm_prompt为空,此时调用方法可能导致修改无效",
|
||||
UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.llm_prompt = new_prompt
|
||||
self._modify_flags.modify_llm_prompt = True
|
||||
|
||||
def modify_plain_text(self, new_text: str, suppress_warning: bool = False):
|
||||
"""
|
||||
修改生成的plain_text内容
|
||||
|
||||
Warning:
|
||||
在未生成plain_text的情况下调用此方法,可能会导致plain_text为空或者修改无效
|
||||
|
||||
Args:
|
||||
new_text (str): 新的纯文本内容
|
||||
"""
|
||||
if not self.plain_text and not suppress_warning:
|
||||
warnings.warn(
|
||||
"当前plain_text为空,此时调用方法可能导致修改无效",
|
||||
UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.plain_text = new_text
|
||||
self._modify_flags.modify_plain_text = True
|
||||
|
||||
def modify_llm_response_content(self, new_content: str, suppress_warning: bool = False):
|
||||
"""
|
||||
修改生成的llm_response_content内容
|
||||
|
||||
Warning:
|
||||
在未生成llm_response_content的情况下调用此方法,可能会导致llm_response_content为空或者修改无效
|
||||
|
||||
Args:
|
||||
new_content (str): 新的LLM响应内容
|
||||
"""
|
||||
if not self.llm_response_content and not suppress_warning:
|
||||
warnings.warn(
|
||||
"当前llm_response_content为空,此时调用方法可能导致修改无效",
|
||||
UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.llm_response_content = new_content
|
||||
self._modify_flags.modify_llm_response_content = True
|
||||
|
||||
def modify_llm_response_reasoning(self, new_reasoning: str, suppress_warning: bool = False):
|
||||
"""
|
||||
修改生成的llm_response_reasoning内容
|
||||
|
||||
Warning:
|
||||
在未生成llm_response_reasoning的情况下调用此方法,可能会导致llm_response_reasoning为空或者修改无效
|
||||
|
||||
Args:
|
||||
new_reasoning (str): 新的LLM响应推理内容
|
||||
"""
|
||||
if not self.llm_response_reasoning and not suppress_warning:
|
||||
warnings.warn(
|
||||
"当前llm_response_reasoning为空,此时调用方法可能导致修改无效",
|
||||
UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.llm_response_reasoning = new_reasoning
|
||||
self._modify_flags.modify_llm_response_reasoning = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class CustomEventHandlerResult:
|
||||
message: str = ""
|
||||
timestamp: float = 0.0
|
||||
extra_info: Optional[Dict] = None
|
||||
Reference in New Issue
Block a user