refactor(plugin_system): 废弃旧版Command系统并重构注册中心

本次提交完全移除了对旧版 `BaseCommand` 系统的支持,统一使用 `PlusCommand`。所有旧版命令现在通过一个兼容性适配器在加载时自动转换为 `PlusCommand`,简化了命令处理流程和代码库。

主要变更:
- **移除旧版命令处理**: 删除了 `ChatBot` 中专门处理旧版 `BaseCommand` 的方法 (`_process_commands_with_new_system`) 和相关逻辑,现在所有命令都通过 `PlusCommand` 的处理流程。
- **重构组件注册中心**: 对 `ComponentRegistry` 进行了大规模重构和清理:
    - 添加了大量文档字符串和类型提示,显著提升了代码的可读性和可维护性。
    - 废弃了特定于 `BaseCommand` 的注册表和查找方法 (`_command_registry`, `_command_patterns`, `find_command_by_text`)。
    - 实现了 `unregister_plugin` 和 `remove_component` 方法,支持插件和组件在运行时的动态卸载。
    - 统一并简化了各类组件的注册、查询和状态管理逻辑,使其更加一致和健壮。

BREAKING CHANGE: 废弃了 `BaseCommand` 类。所有自定义命令现在必须继承自 `PlusCommand`。虽然系统提供了向后兼容的适配器,但强烈建议将现有命令迁移到 `PlusCommand` 以获得全部功能和最佳性能。直接依赖旧版 `BaseCommand` 注册和查找机制的代码将无法工作。
This commit is contained in:
minecraft1024a
2025-11-22 12:35:37 +08:00
parent f3ae22d622
commit 94b4123039
3 changed files with 879 additions and 1075 deletions

View File

@@ -197,80 +197,6 @@ class ChatBot:
logger.error(f"处理PlusCommand时出错: {e}") logger.error(f"处理PlusCommand时出错: {e}")
return False, None, True # 出错时继续处理消息 return False, None, True # 出错时继续处理消息
async def _process_commands_with_new_system(self, message: DatabaseMessages, chat: ChatStream):
# sourcery skip: use-named-expression
"""使用新插件系统处理命令"""
try:
text = message.processed_plain_text or ""
# 使用新的组件注册中心查找命令
command_result = component_registry.find_command_by_text(text)
if command_result:
command_class, matched_groups, command_info = command_result
plugin_name = command_info.plugin_name
command_name = command_info.name
if (
chat
and chat.stream_id
and command_name
in global_announcement_manager.get_disabled_chat_commands(chat.stream_id)
):
logger.info("用户禁用的命令,跳过处理")
return False, None, True
message.is_command = True
# 获取插件配置
plugin_config = component_registry.get_plugin_config(plugin_name)
# 创建命令实例
command_instance: BaseCommand = command_class(message, plugin_config)
command_instance.set_matched_groups(matched_groups)
# 为插件实例设置 chat_stream 运行时属性
setattr(command_instance, "chat_stream", chat)
try:
# 检查聊天类型限制
if not command_instance.is_chat_type_allowed():
is_group = chat.group_info is not None
logger.info(
f"命令 {command_class.__name__} 不支持当前聊天类型: {'群聊' if is_group else '私聊'}"
)
return False, None, True # 跳过此命令,继续处理其他消息
# 执行命令
success, response, intercept_message = await command_instance.execute()
# 记录命令执行结果
if success:
logger.info(f"命令执行成功: {command_class.__name__} (拦截: {intercept_message})")
else:
logger.warning(f"命令执行失败: {command_class.__name__} - {response}")
# 根据命令的拦截设置决定是否继续处理消息
return True, response, not intercept_message # 找到命令根据intercept_message决定是否继续
except Exception as e:
logger.error(f"执行命令时出错: {command_class.__name__} - {e}")
logger.error(traceback.format_exc())
try:
await command_instance.send_text(f"命令执行出错: {e!s}")
except Exception as send_error:
logger.error(f"发送错误消息失败: {send_error}")
# 命令出错时,根据命令的拦截设置决定是否继续处理消息
return True, str(e), False # 出错时继续处理消息
# 没有找到命令,继续处理消息
return False, None, True
except Exception as e:
logger.error(f"处理命令时出错: {e}")
return False, None, True # 出错时继续处理消息
async def _handle_adapter_response_from_dict(self, seg_data: dict | None): async def _handle_adapter_response_from_dict(self, seg_data: dict | None):
"""处理适配器命令响应(从字典数据)""" """处理适配器命令响应(从字典数据)"""
try: try:
@@ -412,16 +338,6 @@ class ChatBot:
logger.info(f"PlusCommand处理完成跳过后续消息处理: {plus_cmd_result}") logger.info(f"PlusCommand处理完成跳过后续消息处理: {plus_cmd_result}")
return return
# 如果不是PlusCommand尝试传统的BaseCommand处理
if not is_plus_command:
is_command, cmd_result, continue_process = await self._process_commands_with_new_system(message, chat)
# 如果是命令且不需要继续处理,则直接返回
if is_command and not continue_process:
await MessageStorage.store_message(message, chat)
logger.info(f"命令处理完成,跳过后续消息处理: {cmd_result}")
return
result = await event_manager.trigger_event(EventType.ON_MESSAGE, permission_group="SYSTEM", message=message) result = await event_manager.trigger_event(EventType.ON_MESSAGE, permission_group="SYSTEM", message=message)
if result and not result.all_continue_process(): if result and not result.all_continue_process():
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于消息到达时取消了消息处理") raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于消息到达时取消了消息处理")

View File

@@ -29,14 +29,16 @@ class PromptComponentManager:
def __init__(self): def __init__(self):
"""初始化管理器实例。""" """初始化管理器实例。"""
# _dynamic_rules 仅用于存储通过 API 动态添加的、非静态组件的规则。 # _dynamic_rules 存储通过 API 在运行时动态添加/修改的规则。
# 这是实现提示词动态性的核心数据结构。
# 结构: { # 结构: {
# "target_prompt_name": { # "target_prompt_name": { // 目标 Prompt 的名称
# "prompt_component_name": (InjectionRule, content_provider, source) # "prompt_component_name": (InjectionRule, content_provider, source) // 注入组件的规则详情
# } # }
# } # }
self._dynamic_rules: dict[str, dict[str, tuple[InjectionRule, Callable[..., Awaitable[str]], str]]] = {} self._dynamic_rules: dict[str, dict[str, tuple[InjectionRule, Callable[..., Awaitable[str]], str]]] = {}
self._lock = asyncio.Lock() # 锁现在保护 _dynamic_rules # 使用 asyncio.Lock 来确保对 _dynamic_rules 的所有写操作都是线程安全的。
self._lock = asyncio.Lock()
# --- 运行时规则管理 API --- # --- 运行时规则管理 API ---
@@ -64,9 +66,13 @@ class PromptComponentManager:
Returns: Returns:
bool: 如果成功添加或更新,则返回 True。 bool: 如果成功添加或更新,则返回 True。
""" """
# 加锁以保证多协程环境下的数据一致性
async with self._lock: async with self._lock:
# 遍历所有待添加的规则
for rule in rules: for rule in rules:
# 使用 setdefault 确保目标 prompt 的规则字典存在
target_rules = self._dynamic_rules.setdefault(rule.target_prompt, {}) target_rules = self._dynamic_rules.setdefault(rule.target_prompt, {})
# 添加或覆盖指定组件的规则、内容提供者和来源
target_rules[prompt_name] = (rule, content_provider, source) target_rules[prompt_name] = (rule, content_provider, source)
logger.info(f"成功添加/更新注入规则: '{prompt_name}' -> '{rule.target_prompt}' (来源: {source})") logger.info(f"成功添加/更新注入规则: '{prompt_name}' -> '{rule.target_prompt}' (来源: {source})")
return True return True
@@ -88,15 +94,16 @@ class PromptComponentManager:
如果未找到该组件的任何现有规则(无法复用),则返回 False。 如果未找到该组件的任何现有规则(无法复用),则返回 False。
""" """
async with self._lock: async with self._lock:
# 步骤 1: 查找现有的 content_provider 和 source # 步骤 1: 遍历所有动态规则,查找指定组件已存在的 provider 和 source
found_provider: Callable[..., Awaitable[str]] | None = None found_provider: Callable[..., Awaitable[str]] | None = None
found_source: str | None = None found_source: str | None = None
for target_rules in self._dynamic_rules.values(): for target_rules in self._dynamic_rules.values():
if prompt_name in target_rules: if prompt_name in target_rules:
# 如果找到,记录其 provider 和 source 并跳出循环
_, found_provider, found_source = target_rules[prompt_name] _, found_provider, found_source = target_rules[prompt_name]
break break
# 步骤 2: 如果找到 provider则操作失败 # 步骤 2: 如果遍历完仍未找到 provider说明该组件无任何规则,无法复用
if not found_provider: if not found_provider:
logger.warning( logger.warning(
f"尝试为组件 '{prompt_name}' 添加规则失败: " f"尝试为组件 '{prompt_name}' 添加规则失败: "
@@ -105,7 +112,7 @@ class PromptComponentManager:
return False return False
# 步骤 3: 使用找到的 provider 和 source 添加新规则 # 步骤 3: 使用找到的 provider 和 source 添加新规则
source_to_use = found_source or "runtime" # 提供一个默认值以防万一 source_to_use = found_source or "runtime" # 如果 source 为 None提供默认值
target_rules = self._dynamic_rules.setdefault(rule.target_prompt, {}) target_rules = self._dynamic_rules.setdefault(rule.target_prompt, {})
target_rules[prompt_name] = (rule, found_provider, source_to_use) target_rules[prompt_name] = (rule, found_provider, source_to_use)
logger.info( logger.info(
@@ -126,13 +133,16 @@ class PromptComponentManager:
bool: 如果成功移除,则返回 True如果规则不存在则返回 False。 bool: 如果成功移除,则返回 True如果规则不存在则返回 False。
""" """
async with self._lock: async with self._lock:
# 检查目标和组件规则是否存在
if target_prompt in self._dynamic_rules and prompt_name in self._dynamic_rules[target_prompt]: if target_prompt in self._dynamic_rules and prompt_name in self._dynamic_rules[target_prompt]:
# 存在则删除
del self._dynamic_rules[target_prompt][prompt_name] del self._dynamic_rules[target_prompt][prompt_name]
# 如果目标下已无任何规则,则清理掉这个 # 如果删除后,该目标下已无任何规则,则清理掉这个目标键,保持数据结构整洁
if not self._dynamic_rules[target_prompt]: if not self._dynamic_rules[target_prompt]:
del self._dynamic_rules[target_prompt] del self._dynamic_rules[target_prompt]
logger.info(f"成功移除注入规则: '{prompt_name}' from '{target_prompt}'") logger.info(f"成功移除注入规则: '{prompt_name}' from '{target_prompt}'")
return True return True
# 如果规则不存在,记录警告并返回 False
logger.warning(f"尝试移除注入规则失败: 未找到 '{prompt_name}' on '{target_prompt}'") logger.warning(f"尝试移除注入规则失败: 未找到 '{prompt_name}' on '{target_prompt}'")
return False return False
@@ -153,7 +163,9 @@ class PromptComponentManager:
async with self._lock: async with self._lock:
# 创建一个目标列表的副本进行迭代,因为我们可能会在循环中修改字典 # 创建一个目标列表的副本进行迭代,因为我们可能会在循环中修改字典
for target_prompt in list(self._dynamic_rules.keys()): for target_prompt in list(self._dynamic_rules.keys()):
# 检查当前目标下是否存在该组件的规则
if prompt_name in self._dynamic_rules[target_prompt]: if prompt_name in self._dynamic_rules[target_prompt]:
# 存在则删除
del self._dynamic_rules[target_prompt][prompt_name] del self._dynamic_rules[target_prompt][prompt_name]
removed = True removed = True
logger.info(f"成功移除注入规则: '{prompt_name}' from '{target_prompt}'") logger.info(f"成功移除注入规则: '{prompt_name}' from '{target_prompt}'")
@@ -176,17 +188,23 @@ class PromptComponentManager:
async def content_provider(params: PromptParameters, target_prompt_name: str) -> str: async def content_provider(params: PromptParameters, target_prompt_name: str) -> str:
"""实际执行内容生成的异步函数。""" """实际执行内容生成的异步函数。"""
try: try:
# 从注册表获取组件信息,用于后续获取插件配置
p_info = component_registry.get_component_info(component_name, ComponentType.PROMPT) p_info = component_registry.get_component_info(component_name, ComponentType.PROMPT)
plugin_config = {} plugin_config = {}
if isinstance(p_info, PromptInfo): if isinstance(p_info, PromptInfo):
# 获取该组件所属插件的配置
plugin_config = component_registry.get_plugin_config(p_info.plugin_name) plugin_config = component_registry.get_plugin_config(p_info.plugin_name)
# 实例化组件,并传入所需参数
instance = component_class( instance = component_class(
params=params, plugin_config=plugin_config, target_prompt_name=target_prompt_name params=params, plugin_config=plugin_config, target_prompt_name=target_prompt_name
) )
# 执行组件的 execute 方法以生成内容
result = await instance.execute() result = await instance.execute()
# 确保返回的是字符串
return str(result) if result is not None else "" return str(result) if result is not None else ""
except Exception as e: except Exception as e:
# 捕获并记录执行过程中的任何异常,返回空字符串以避免注入失败
logger.error(f"执行规则提供者 '{component_name}' 时出错: {e}", exc_info=True) logger.error(f"执行规则提供者 '{component_name}' 时出错: {e}", exc_info=True)
return "" return ""
@@ -202,16 +220,20 @@ class PromptComponentManager:
if not isinstance(info, PromptInfo): if not isinstance(info, PromptInfo):
continue continue
# 实时检查组件是否启用 # 实时检查组件是否启用,跳过禁用的组件
if not component_registry.is_component_available(name, ComponentType.PROMPT): if not component_registry.is_component_available(name, ComponentType.PROMPT):
continue continue
# 获取组件的类定义
component_class = component_registry.get_component_class(name, ComponentType.PROMPT) component_class = component_registry.get_component_class(name, ComponentType.PROMPT)
if not (component_class and issubclass(component_class, BasePrompt)): if not (component_class and issubclass(component_class, BasePrompt)):
continue continue
# 为该组件创建一个内容提供者
provider = self._create_content_provider(name, component_class) provider = self._create_content_provider(name, component_class)
# 遍历组件定义的所有注入规则
for rule in info.injection_rules: for rule in info.injection_rules:
# 如果规则的目标与当前目标匹配,则添加到列表中
if rule.target_prompt == target_prompt_name: if rule.target_prompt == target_prompt_name:
all_rules.append((rule, provider, "static")) all_rules.append((rule, provider, "static"))
@@ -219,11 +241,13 @@ class PromptComponentManager:
async with self._lock: async with self._lock:
runtime_rules = self._dynamic_rules.get(target_prompt_name, {}) runtime_rules = self._dynamic_rules.get(target_prompt_name, {})
for name, (rule, provider, source) in runtime_rules.items(): for name, (rule, provider, source) in runtime_rules.items():
# 确保运行时组件不会与禁用的静态组件冲突 # 检查该运行时规则是否关联到一个已注册的静态组件
static_info = component_registry.get_component_info(name, ComponentType.PROMPT) static_info = component_registry.get_component_info(name, ComponentType.PROMPT)
# 如果关联的静态组件存在且被禁用,则跳过此运行时规则
if static_info and not component_registry.is_component_available(name, ComponentType.PROMPT): if static_info and not component_registry.is_component_available(name, ComponentType.PROMPT):
logger.debug(f"跳过运行时规则 '{name}',因为它关联的静态组件当前已禁用。") logger.debug(f"跳过运行时规则 '{name}',因为它关联的静态组件当前已禁用。")
continue continue
# 将有效的运行时规则添加到列表
all_rules.append((rule, provider, source)) all_rules.append((rule, provider, source))
return all_rules return all_rules
@@ -252,17 +276,19 @@ class PromptComponentManager:
Returns: Returns:
str: 应用了所有注入规则后,最终生成的提示词模板字符串。 str: 应用了所有注入规则后,最终生成的提示词模板字符串。
""" """
# 构建适用于当前目标的所有规则
rules_for_target = await self._build_rules_for_target(target_prompt_name) rules_for_target = await self._build_rules_for_target(target_prompt_name)
if not rules_for_target: if not rules_for_target:
# 如果没有规则,直接返回原始模板
return original_template return original_template
# --- 占位符保护机制 --- # --- 占位符保护机制 ---
# 1. 保护: 找到所有 {placeholder} 并用临时标记替换
placeholders = re.findall(r"({[^{}]+})", original_template) placeholders = re.findall(r"({[^{}]+})", original_template)
placeholder_map: dict[str, str] = { placeholder_map: dict[str, str] = {
f"__PROMPT_PLACEHOLDER_{i}__": p for i, p in enumerate(placeholders) f"__PROMPT_PLACEHOLDER_{i}__": p for i, p in enumerate(placeholders)
} }
# 1. 保护: 将占位符替换为临时标记
protected_template = original_template protected_template = original_template
for marker, placeholder in placeholder_map.items(): for marker, placeholder in placeholder_map.items():
protected_template = protected_template.replace(placeholder, marker) protected_template = protected_template.replace(placeholder, marker)
@@ -271,6 +297,7 @@ class PromptComponentManager:
for rule, _, source in rules_for_target: for rule, _, source in rules_for_target:
if rule.injection_type in (InjectionType.REMOVE, InjectionType.REPLACE) and rule.target_content: if rule.injection_type in (InjectionType.REMOVE, InjectionType.REPLACE) and rule.target_content:
try: try:
# 检查规则的 target_content (正则) 是否可能匹配到任何一个占位符
for p in placeholders: for p in placeholders:
if re.search(rule.target_content, p): if re.search(rule.target_content, p):
logger.warning( logger.warning(
@@ -278,10 +305,10 @@ class PromptComponentManager:
f"规则 `target_content` ('{rule.target_content}') " f"规则 `target_content` ('{rule.target_content}') "
f"可能会影响核心占位符 '{p}'。为保证系统稳定,该占位符已被保护,不会被此规则修改。" f"可能会影响核心占位符 '{p}'。为保证系统稳定,该占位符已被保护,不会被此规则修改。"
) )
# 只对每个规则警告一次 # 每个规则警告一次
break break
except re.error: except re.error:
# 正则表达式本身有误,后执行时会再次捕获,这里可忽略 # 如果正则表达式本身有误,后执行时会捕获,此处可忽略
pass pass
# 3. 安全执行: 按优先级排序并应用规则 # 3. 安全执行: 按优先级排序并应用规则
@@ -290,13 +317,16 @@ class PromptComponentManager:
modified_template = protected_template modified_template = protected_template
for rule, provider, source in rules_for_target: for rule, provider, source in rules_for_target:
content = "" content = ""
# REMOVE 类型不需要生成内容
if rule.injection_type != InjectionType.REMOVE: if rule.injection_type != InjectionType.REMOVE:
try: try:
# 调用内容提供者生成要注入的文本
content = await provider(params, target_prompt_name) content = await provider(params, target_prompt_name)
except Exception as e: except Exception as e:
logger.error(f"执行规则 '{rule}' (来源: {source}) 的内容提供者时失败: {e}", exc_info=True) logger.error(f"执行规则 '{rule}' (来源: {source}) 的内容提供者时失败: {e}", exc_info=True)
continue continue # 执行失败则跳过此规则
# 应用注入规则
try: try:
if rule.injection_type == InjectionType.PREPEND: if rule.injection_type == InjectionType.PREPEND:
if content: if content:
@@ -309,6 +339,7 @@ class PromptComponentManager:
modified_template = re.sub(rule.target_content, str(content), modified_template) modified_template = re.sub(rule.target_content, str(content), modified_template)
elif rule.injection_type == InjectionType.INSERT_AFTER: elif rule.injection_type == InjectionType.INSERT_AFTER:
if content and rule.target_content: if content and rule.target_content:
# 使用 \\g<0> 在匹配项后插入内容
replacement = f"\\g<0>\n{content}" replacement = f"\\g<0>\n{content}"
modified_template = re.sub(rule.target_content, replacement, modified_template) modified_template = re.sub(rule.target_content, replacement, modified_template)
elif rule.injection_type == InjectionType.REMOVE: elif rule.injection_type == InjectionType.REMOVE:
@@ -319,7 +350,7 @@ class PromptComponentManager:
except Exception as e: except Exception as e:
logger.error(f"应用注入规则 '{rule}' (来源: {source}) 失败: {e}", exc_info=True) logger.error(f"应用注入规则 '{rule}' (来源: {source}) 失败: {e}", exc_info=True)
# 4. 占位符恢复 # 4. 占位符恢复: 将临时标记替换回原始的占位符
final_template = modified_template final_template = modified_template
for marker, placeholder in placeholder_map.items(): for marker, placeholder in placeholder_map.items():
final_template = final_template.replace(marker, placeholder) final_template = final_template.replace(marker, placeholder)
@@ -343,8 +374,9 @@ class PromptComponentManager:
str: 模拟生成的最终提示词模板字符串。如果找不到模板,则返回错误信息。 str: 模拟生成的最终提示词模板字符串。如果找不到模板,则返回错误信息。
""" """
try: try:
# 从全局提示词管理器获取最原始的模板内容 # 动态导入以避免循环依赖
from src.chat.utils.prompt import global_prompt_manager from src.chat.utils.prompt import global_prompt_manager
# 从全局管理器获取原始的、未经修改的提示词对象
original_prompt = global_prompt_manager._prompts.get(target_prompt_name) original_prompt = global_prompt_manager._prompts.get(target_prompt_name)
if not original_prompt: if not original_prompt:
logger.warning(f"无法预览 '{target_prompt_name}',因为找不到这个核心 Prompt。") logger.warning(f"无法预览 '{target_prompt_name}',因为找不到这个核心 Prompt。")
@@ -354,14 +386,16 @@ class PromptComponentManager:
logger.warning(f"无法预览 '{target_prompt_name}',因为找不到这个核心 Prompt。") logger.warning(f"无法预览 '{target_prompt_name}',因为找不到这个核心 Prompt。")
return f"Error: Prompt '{target_prompt_name}' not found." return f"Error: Prompt '{target_prompt_name}' not found."
# 直接调用核心注入逻辑来模拟结果 # 直接调用核心注入逻辑来模拟并返回结果
return await self.apply_injections(target_prompt_name, original_template, params) return await self.apply_injections(target_prompt_name, original_template, params)
# --- 状态观测与查询 API --- # --- 状态观测与查询 API ---
def get_core_prompts(self) -> list[str]: def get_core_prompts(self) -> list[str]:
"""获取所有已注册的核心提示词模板名称列表(即所有可注入的目标)。""" """获取所有已注册的核心提示词模板名称列表(即所有可注入的目标)。"""
# 动态导入以避免循环依赖
from src.chat.utils.prompt import global_prompt_manager from src.chat.utils.prompt import global_prompt_manager
# 返回所有核心 prompt 的名称列表
return list(global_prompt_manager._prompts.keys()) return list(global_prompt_manager._prompts.keys())
def get_core_prompt_contents(self, prompt_name: str | None = None) -> list[list[str]]: def get_core_prompt_contents(self, prompt_name: str | None = None) -> list[list[str]]:
@@ -381,9 +415,11 @@ class PromptComponentManager:
from src.chat.utils.prompt import global_prompt_manager from src.chat.utils.prompt import global_prompt_manager
if prompt_name: if prompt_name:
# 如果指定了名称,则查找并返回单个模板
prompt = global_prompt_manager._prompts.get(prompt_name) prompt = global_prompt_manager._prompts.get(prompt_name)
return [[prompt_name, prompt.template]] if prompt else [] return [[prompt_name, prompt.template]] if prompt else []
# 如果未指定名称,则返回所有模板的列表
return [[name, prompt.template] for name, prompt in global_prompt_manager._prompts.items()] return [[name, prompt.template] for name, prompt in global_prompt_manager._prompts.items()]
async def get_registered_prompt_component_info(self) -> list[PromptInfo]: async def get_registered_prompt_component_info(self) -> list[PromptInfo]:
@@ -391,21 +427,23 @@ class PromptComponentManager:
获取所有已注册和动态添加的Prompt组件信息并反映当前的注入规则状态。 获取所有已注册和动态添加的Prompt组件信息并反映当前的注入规则状态。
此方法现在直接从 component_registry 获取静态组件信息,并合并纯运行时的组件信息。 此方法现在直接从 component_registry 获取静态组件信息,并合并纯运行时的组件信息。
""" """
# 该方法现在直接从 component_registry 获取信息,因为它总是有最新的数据 # 从注册表获取所有已注册的静态 Prompt 组件信息
all_components = component_registry.get_components_by_type(ComponentType.PROMPT) all_components = component_registry.get_components_by_type(ComponentType.PROMPT)
info_list = [info for info in all_components.values() if isinstance(info, PromptInfo)] info_list = [info for info in all_components.values() if isinstance(info, PromptInfo)]
# 检查是否有纯动态组件需要添加 # 检查并合并仅在运行时通过 API 添加的“纯动态组件
async with self._lock: async with self._lock:
runtime_component_names = set() runtime_component_names = set()
# 收集所有动态规则中涉及的组件名称
for rules in self._dynamic_rules.values(): for rules in self._dynamic_rules.values():
runtime_component_names.update(rules.keys()) runtime_component_names.update(rules.keys())
static_component_names = {info.name for info in info_list} static_component_names = {info.name for info in info_list}
# 找出那些只存在于动态规则中,但未在静态组件中注册的名称
pure_dynamic_names = runtime_component_names - static_component_names pure_dynamic_names = runtime_component_names - static_component_names
for name in pure_dynamic_names: for name in pure_dynamic_names:
# 为纯动态组件创建临时的 PromptInfo # 为这些“纯动态组件创建一个临时的信息对象
dynamic_info = PromptInfo( dynamic_info = PromptInfo(
name=name, name=name,
component_type=ComponentType.PROMPT, component_type=ComponentType.PROMPT,
@@ -413,7 +451,7 @@ class PromptComponentManager:
plugin_name="runtime", plugin_name="runtime",
is_built_in=False, is_built_in=False,
) )
# 从 _dynamic_rules 中收集其所有规则 # 从动态规则中收集并关联其所有注入规则
for target, rules_in_target in self._dynamic_rules.items(): for target, rules_in_target in self._dynamic_rules.items():
if name in rules_in_target: if name in rules_in_target:
rule, _, _ = rules_in_target[name] rule, _, _ = rules_in_target[name]
@@ -433,10 +471,11 @@ class PromptComponentManager:
""" """
info_map = {} info_map = {}
all_core_prompts = self.get_core_prompts() all_core_prompts = self.get_core_prompts()
# 确定要处理的目标:如果指定了有效的目标,则只处理它;否则处理所有核心 prompt
targets_to_process = [target_prompt] if target_prompt and target_prompt in all_core_prompts else all_core_prompts targets_to_process = [target_prompt] if target_prompt and target_prompt in all_core_prompts else all_core_prompts
for target in targets_to_process: for target in targets_to_process:
# 动态构建规则列表 # 动态构建该目标的所有有效规则
rules_for_target = await self._build_rules_for_target(target) rules_for_target = await self._build_rules_for_target(target)
if not rules_for_target: if not rules_for_target:
info_map[target] = [] info_map[target] = []
@@ -444,9 +483,10 @@ class PromptComponentManager:
info_list = [] info_list = []
for rule, _, source in rules_for_target: for rule, _, source in rules_for_target:
# 从规则本身获取组件名 # 从规则对象中获取其所属组件的名称
prompt_name = rule.owner_component prompt_name = rule.owner_component
if detailed: if detailed:
# 如果需要详细信息,则添加更多字段
info_list.append( info_list.append(
{ {
"name": prompt_name, "name": prompt_name,
@@ -457,8 +497,10 @@ class PromptComponentManager:
} }
) )
else: else:
# 否则只添加基本信息
info_list.append({"name": prompt_name, "priority": rule.priority, "source": source}) info_list.append({"name": prompt_name, "priority": rule.priority, "source": source})
# 按优先级对结果进行排序
info_list.sort(key=lambda x: x["priority"]) info_list.sort(key=lambda x: x["priority"])
info_map[target] = info_list info_map[target] = info_list
return info_map return info_map
@@ -491,31 +533,33 @@ class PromptComponentManager:
for name, info in static_components.items(): for name, info in static_components.items():
if not isinstance(info, PromptInfo): if not isinstance(info, PromptInfo):
continue continue
# 应用 component_name 筛选 # 如果指定了 component_name 且不匹配,则跳过此组件
if component_name and name != component_name: if component_name and name != component_name:
continue continue
for rule in info.injection_rules: for rule in info.injection_rules:
# 应用 target_prompt 筛选 # 如果指定了 target_prompt 且不匹配,则跳过此规则
if target_prompt and rule.target_prompt != target_prompt: if target_prompt and rule.target_prompt != target_prompt:
continue continue
target_dict = all_rules.setdefault(rule.target_prompt, {}) target_dict = all_rules.setdefault(rule.target_prompt, {})
target_dict[name] = rule target_dict[name] = rule
# 2. 收集并合并所有运行时规则 # 2. 收集并合并所有运行时规则
async with self._lock: async with self._lock:
for target, rules_in_target in self._dynamic_rules.items(): for target, rules_in_target in self._dynamic_rules.items():
# 应用 target_prompt 筛选 # 如果指定了 target_prompt 且不匹配,则跳过此目标下的所有规则
if target_prompt and target != target_prompt: if target_prompt and target != target_prompt:
continue continue
for name, (rule, _, _) in rules_in_target.items(): for name, (rule, _, _) in rules_in_target.items():
# 应用 component_name 筛选 # 如果指定了 component_name 且不匹配,则跳过此规则
if component_name and name != component_name: if component_name and name != component_name:
continue continue
target_dict = all_rules.setdefault(target, {}) target_dict = all_rules.setdefault(target, {})
# 运行时规则会覆盖同名的静态规则
target_dict[name] = rule target_dict[name] = rule
# 返回深拷贝以防止外部修改影响内部状态
return copy.deepcopy(all_rules) return copy.deepcopy(all_rules)

File diff suppressed because it is too large Load Diff