插件系统info修复,见changes.md

This commit is contained in:
UnCLAS-Prommer
2025-07-10 16:46:37 +08:00
parent d5cd0e8538
commit ab61b1bb22
15 changed files with 99 additions and 68 deletions

18
changes.md Normal file
View File

@@ -0,0 +1,18 @@
# 插件API与规范修改
1. 现在`plugin_system``__init__.py`文件中包含了所有插件API的导入用户可以直接使用`from plugin_system import *`来导入所有API。
2. register_plugin函数现在转移到了`plugin_system.apis.plugin_register_api`模块中,用户可以通过`from plugin_system.apis.plugin_register_api import register_plugin`来导入。
3. 现在强制要求的property如下
- `plugin_name`: 插件名称,必须是唯一的。(与文件夹相同)
- `enable_plugin`: 是否启用插件,默认为`True`
- `dependencies`: 插件依赖的其他插件列表,默认为空。**现在并不检查(也许)**
- `python_dependencies`: 插件依赖的Python包列表默认为空。**现在并不检查**
- `config_file_name`: 插件配置文件名,默认为`config.toml`
- `config_schema`: 插件配置文件的schema用于自动生成配置文件。
# 插件系统修改
1. 现在所有的匹配模式不再是关键字了,而是枚举类。**(可能有遗漏)**
2. 修复了一下显示插件信息不显示的问题。同时精简了一下显示内容
3. 修复了插件系统混用了`plugin_name``display_name`的问题。现在所有的插件信息都使用`display_name`来显示,而内部标识仍然使用`plugin_name`。**(可能有遗漏)**

View File

@@ -103,6 +103,8 @@ class HelloWorldPlugin(BasePlugin):
# 插件基本信息 # 插件基本信息
plugin_name = "hello_world_plugin" # 内部标识符 plugin_name = "hello_world_plugin" # 内部标识符
enable_plugin = True enable_plugin = True
dependencies = [] # 插件依赖列表
python_dependencies = [] # Python包依赖列表
config_file_name = "config.toml" # 配置文件名 config_file_name = "config.toml" # 配置文件名
# 配置节描述 # 配置节描述

View File

@@ -443,6 +443,8 @@ class TakePicturePlugin(BasePlugin):
plugin_name = "take_picture_plugin" # 内部标识符 plugin_name = "take_picture_plugin" # 内部标识符
enable_plugin = True enable_plugin = True
dependencies = [] # 插件依赖列表
python_dependencies = [] # Python包依赖列表
config_file_name = "config.toml" config_file_name = "config.toml"
# 配置节描述 # 配置节描述

View File

@@ -39,6 +39,7 @@ class ChatMessageContext:
return self.message return self.message
def check_types(self, types: list) -> bool: def check_types(self, types: list) -> bool:
# sourcery skip: invert-any-all, use-any, use-next
"""检查消息类型""" """检查消息类型"""
if not self.message.message_info.format_info.accept_format: if not self.message.message_info.format_info.accept_format:
return False return False

View File

@@ -561,7 +561,9 @@ class NormalChat:
available_actions = None available_actions = None
if self.enable_planner: if self.enable_planner:
try: try:
await self.action_modifier.modify_actions(mode=ChatMode.NORMAL, message_content=message.processed_plain_text) await self.action_modifier.modify_actions(
mode=ChatMode.NORMAL, message_content=message.processed_plain_text
)
available_actions = self.action_manager.get_using_actions_for_mode(ChatMode.NORMAL) available_actions = self.action_manager.get_using_actions_for_mode(ChatMode.NORMAL)
except Exception as e: except Exception as e:
logger.warning(f"[{self.stream_name}] 获取available_actions失败: {e}") logger.warning(f"[{self.stream_name}] 获取available_actions失败: {e}")

View File

@@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Type, Any from typing import Dict, List, Optional, Type
from src.plugin_system.base.base_action import BaseAction from src.plugin_system.base.base_action import BaseAction
from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.chat_stream import ChatStream
from src.common.logger import get_logger from src.common.logger import get_logger
@@ -7,6 +7,7 @@ from src.plugin_system.base.component_types import ComponentType, ActionActivati
logger = get_logger("action_manager") logger = get_logger("action_manager")
class ActionManager: class ActionManager:
""" """
动作管理器,用于管理各种类型的动作 动作管理器,用于管理各种类型的动作
@@ -73,7 +74,7 @@ class ActionManager:
"activation_keywords": action_info.activation_keywords, "activation_keywords": action_info.activation_keywords,
"keyword_case_sensitive": action_info.keyword_case_sensitive, "keyword_case_sensitive": action_info.keyword_case_sensitive,
# 模式和并行设置 # 模式和并行设置
"mode_enable": action_info.mode_enable.value, "mode_enable": action_info.mode_enable,
"parallel_action": action_info.parallel_action, "parallel_action": action_info.parallel_action,
# 插件信息 # 插件信息
"_plugin_name": getattr(action_info, "plugin_name", ""), "_plugin_name": getattr(action_info, "plugin_name", ""),
@@ -187,7 +188,7 @@ class ActionManager:
enabled_actions = {} enabled_actions = {}
for action_name, action_info in self._using_actions.items(): for action_name, action_info in self._using_actions.items():
action_mode = action_info.mode_enable action_mode = action_info["mode_enable"]
# 检查动作是否在当前模式下启用 # 检查动作是否在当前模式下启用
if action_mode in [ChatMode.ALL, mode]: if action_mode in [ChatMode.ALL, mode]:

View File

@@ -2,16 +2,16 @@ import random
import asyncio import asyncio
import hashlib import hashlib
import time import time
from typing import List, Optional, Any, Dict from typing import List, Any, Dict
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.config import global_config from src.config.config import global_config
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.chat.focus_chat.focus_loop_info import FocusLoopInfo from src.chat.focus_chat.focus_loop_info import FocusLoopInfo
from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.chat_stream import get_chat_manager, ChatMessageContext
from src.chat.planner_actions.action_manager import ActionManager from src.chat.planner_actions.action_manager import ActionManager
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages
from src.plugin_system.base.component_types import ChatMode from src.plugin_system.base.component_types import ChatMode, ActionInfo
logger = get_logger("action_manager") logger = get_logger("action_manager")
@@ -48,7 +48,7 @@ class ActionModifier:
loop_info=None, loop_info=None,
mode: ChatMode = ChatMode.FOCUS, mode: ChatMode = ChatMode.FOCUS,
message_content: str = "", message_content: str = "",
): ): # sourcery skip: use-named-expression
""" """
动作修改流程,整合传统观察处理和新的激活类型判定 动作修改流程,整合传统观察处理和新的激活类型判定
@@ -129,11 +129,10 @@ class ActionModifier:
f"{self.log_prefix}{mode}模式动作修改流程结束,最终可用动作: {list(self.action_manager.get_using_actions_for_mode(mode).keys())}||移除记录: {removals_summary}" f"{self.log_prefix}{mode}模式动作修改流程结束,最终可用动作: {list(self.action_manager.get_using_actions_for_mode(mode).keys())}||移除记录: {removals_summary}"
) )
def _check_action_associated_types(self, all_actions, chat_context): def _check_action_associated_types(self, all_actions: Dict[str, ActionInfo], chat_context: ChatMessageContext):
type_mismatched_actions = [] type_mismatched_actions = []
for action_name, data in all_actions.items(): for action_name, data in all_actions.items():
if data.get("associated_types"): if data["associated_types"] and not chat_context.check_types(data["associated_types"]):
if not chat_context.check_types(data["associated_types"]):
associated_types_str = ", ".join(data["associated_types"]) associated_types_str = ", ".join(data["associated_types"])
reason = f"适配器不支持(需要: {associated_types_str}" reason = f"适配器不支持(需要: {associated_types_str}"
type_mismatched_actions.append((action_name, reason)) type_mismatched_actions.append((action_name, reason))
@@ -205,35 +204,6 @@ class ActionModifier:
return deactivated_actions return deactivated_actions
async def process_actions_for_planner(
self, observed_messages_str: str = "", chat_context: Optional[str] = None, extra_context: Optional[str] = None
) -> Dict[str, Any]:
"""
[已废弃] 此方法现在已被整合到 modify_actions() 中
为了保持向后兼容性而保留,但建议直接使用 ActionManager.get_using_actions()
规划器应该直接从 ActionManager 获取最终的可用动作集,而不是调用此方法
新的架构:
1. 主循环调用 modify_actions() 处理完整的动作管理流程
2. 规划器直接使用 ActionManager.get_using_actions() 获取最终动作集
"""
logger.warning(
f"{self.log_prefix}process_actions_for_planner() 已废弃,建议规划器直接使用 ActionManager.get_using_actions()"
)
# 为了向后兼容,仍然返回当前使用的动作集
current_using_actions = self.action_manager.get_using_actions()
all_registered_actions = self.action_manager.get_registered_actions()
# 构建完整的动作信息
result = {}
for action_name in current_using_actions.keys():
if action_name in all_registered_actions:
result[action_name] = all_registered_actions[action_name]
return result
def _generate_context_hash(self, chat_content: str) -> str: def _generate_context_hash(self, chat_content: str) -> str:
"""生成上下文的哈希值用于缓存""" """生成上下文的哈希值用于缓存"""
context_content = f"{chat_content}" context_content = f"{chat_content}"

View File

@@ -1,6 +1,7 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Tuple, Optional from typing import Tuple, Optional
from src.common.logger import get_logger from src.common.logger import get_logger
from src.chat.message_receive.chat_stream import ChatStream
from src.plugin_system.base.component_types import ActionActivationType, ChatMode, ActionInfo, ComponentType from src.plugin_system.base.component_types import ActionActivationType, ChatMode, ActionInfo, ComponentType
from src.plugin_system.apis import send_api, database_api, message_api from src.plugin_system.apis import send_api, database_api, message_api
import time import time
@@ -31,7 +32,7 @@ class BaseAction(ABC):
reasoning: str, reasoning: str,
cycle_timers: dict, cycle_timers: dict,
thinking_id: str, thinking_id: str,
chat_stream=None, chat_stream: ChatStream = None,
log_prefix: str = "", log_prefix: str = "",
shutting_down: bool = False, shutting_down: bool = False,
plugin_config: dict = None, plugin_config: dict = None,

View File

@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Dict, List, Type, Optional, Any, Union from typing import Dict, List, Type, Any, Union
import os import os
import inspect import inspect
import toml import toml
@@ -29,18 +29,41 @@ class BasePlugin(ABC):
""" """
# 插件基本信息(子类必须定义) # 插件基本信息(子类必须定义)
plugin_name: str = "" # 插件内部标识符(如 "hello_world_plugin" @property
enable_plugin: bool = False # 是否启用插件 @abstractmethod
dependencies: List[str] = [] # 依赖的其他插件 def plugin_name(self) -> str:
python_dependencies: List[PythonDependency] = [] # Python包依赖 return "" # 插件内部标识符(如 "hello_world_plugin"
config_file_name: Optional[str] = None # 配置文件名
@property
@abstractmethod
def enable_plugin(self) -> bool:
return True # 是否启用插件
@property
@abstractmethod
def dependencies(self) -> List[str]:
return [] # 依赖的其他插件
@property
@abstractmethod
def python_dependencies(self) -> List[PythonDependency]:
return [] # Python包依赖
@property
@abstractmethod
def config_file_name(self) -> str:
return "" # 配置文件名
# manifest文件相关 # manifest文件相关
manifest_file_name: str = "_manifest.json" # manifest文件名 manifest_file_name: str = "_manifest.json" # manifest文件名
manifest_data: Dict[str, Any] = {} # manifest数据 manifest_data: Dict[str, Any] = {} # manifest数据
# 配置定义 # 配置定义
config_schema: Dict[str, Union[Dict[str, ConfigField], str]] = {} @property
@abstractmethod
def config_schema(self) -> Dict[str, Union[Dict[str, ConfigField], str]]:
return {}
config_section_descriptions: Dict[str, str] = {} config_section_descriptions: Dict[str, str] = {}
def __init__(self, plugin_dir: str = None): def __init__(self, plugin_dir: str = None):
@@ -70,7 +93,8 @@ class BasePlugin(ABC):
# 创建插件信息对象 # 创建插件信息对象
self.plugin_info = PluginInfo( self.plugin_info = PluginInfo(
name=self.display_name, # 使用显示名称 name=self.plugin_name,
display_name=self.display_name,
description=self.plugin_description, description=self.plugin_description,
version=self.plugin_version, version=self.plugin_version,
author=self.plugin_author, author=self.plugin_author,

View File

@@ -126,6 +126,7 @@ class CommandInfo(ComponentInfo):
class PluginInfo: class PluginInfo:
"""插件信息""" """插件信息"""
display_name: str # 插件显示名称
name: str # 插件名称 name: str # 插件名称
description: str # 插件描述 description: str # 插件描述
version: str = "1.0.0" # 插件版本 version: str = "1.0.0" # 插件版本

View File

@@ -85,16 +85,17 @@ class PluginManager:
total_failed_registration = 0 total_failed_registration = 0
for plugin_name in self.plugin_classes.keys(): for plugin_name in self.plugin_classes.keys():
if self.load_registered_plugin_classes(plugin_name): load_status, count = self.load_registered_plugin_classes(plugin_name)
if load_status:
total_registered += 1 total_registered += 1
else: else:
total_failed_registration += 1 total_failed_registration += count
self._show_stats(total_registered, total_failed_registration) self._show_stats(total_registered, total_failed_registration)
return total_registered, total_failed_registration return total_registered, total_failed_registration
def load_registered_plugin_classes(self, plugin_name: str) -> bool: def load_registered_plugin_classes(self, plugin_name: str) -> Tuple[bool, int]:
# sourcery skip: extract-duplicate-method, extract-method # sourcery skip: extract-duplicate-method, extract-method
""" """
加载已经注册的插件类 加载已经注册的插件类
@@ -102,7 +103,7 @@ class PluginManager:
plugin_class: Type[BasePlugin] = self.plugin_classes.get(plugin_name) plugin_class: Type[BasePlugin] = self.plugin_classes.get(plugin_name)
if not plugin_class: if not plugin_class:
logger.error(f"插件 {plugin_name} 的插件类未注册或不存在") logger.error(f"插件 {plugin_name} 的插件类未注册或不存在")
return False return False, 1
try: try:
# 使用记录的插件目录路径 # 使用记录的插件目录路径
plugin_dir = self.plugin_paths.get(plugin_name) plugin_dir = self.plugin_paths.get(plugin_name)
@@ -116,7 +117,7 @@ class PluginManager:
# 检查插件是否启用 # 检查插件是否启用
if not plugin_instance.enable_plugin: if not plugin_instance.enable_plugin:
logger.info(f"插件 {plugin_name} 已禁用,跳过加载") logger.info(f"插件 {plugin_name} 已禁用,跳过加载")
return False return False, 0
# 检查版本兼容性 # 检查版本兼容性
is_compatible, compatibility_error = self._check_plugin_version_compatibility( is_compatible, compatibility_error = self._check_plugin_version_compatibility(
@@ -125,22 +126,22 @@ class PluginManager:
if not is_compatible: if not is_compatible:
self.failed_plugins[plugin_name] = compatibility_error self.failed_plugins[plugin_name] = compatibility_error
logger.error(f"❌ 插件加载失败: {plugin_name} - {compatibility_error}") logger.error(f"❌ 插件加载失败: {plugin_name} - {compatibility_error}")
return False return False, 1
if plugin_instance.register_plugin(): if plugin_instance.register_plugin():
self.loaded_plugins[plugin_name] = plugin_instance self.loaded_plugins[plugin_name] = plugin_instance
self._show_plugin_components(plugin_name) self._show_plugin_components(plugin_name)
return True return True, 1
else: else:
self.failed_plugins[plugin_name] = "插件注册失败" self.failed_plugins[plugin_name] = "插件注册失败"
logger.error(f"❌ 插件注册失败: {plugin_name}") logger.error(f"❌ 插件注册失败: {plugin_name}")
return False return False, 1
except FileNotFoundError as e: except FileNotFoundError as e:
# manifest文件缺失 # manifest文件缺失
error_msg = f"缺少manifest文件: {str(e)}" error_msg = f"缺少manifest文件: {str(e)}"
self.failed_plugins[plugin_name] = error_msg self.failed_plugins[plugin_name] = error_msg
logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}") logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}")
return False return False, 1
except ValueError as e: except ValueError as e:
# manifest文件格式错误或验证失败 # manifest文件格式错误或验证失败
@@ -148,7 +149,7 @@ class PluginManager:
error_msg = f"manifest验证失败: {str(e)}" error_msg = f"manifest验证失败: {str(e)}"
self.failed_plugins[plugin_name] = error_msg self.failed_plugins[plugin_name] = error_msg
logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}") logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}")
return False return False, 1
except Exception as e: except Exception as e:
# 其他错误 # 其他错误
@@ -156,7 +157,7 @@ class PluginManager:
self.failed_plugins[plugin_name] = error_msg self.failed_plugins[plugin_name] = error_msg
logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}") logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}")
logger.debug("详细错误信息: ", exc_info=True) logger.debug("详细错误信息: ", exc_info=True)
return False return False, 1
def unload_registered_plugin_module(self, plugin_name: str) -> None: def unload_registered_plugin_module(self, plugin_name: str) -> None:
""" """
@@ -489,14 +490,16 @@ class PluginManager:
info_parts = [part for part in [version_info, author_info, license_info] if part] info_parts = [part for part in [version_info, author_info, license_info] if part]
extra_info = f" ({', '.join(info_parts)})" if info_parts else "" extra_info = f" ({', '.join(info_parts)})" if info_parts else ""
logger.info(f" 📦 {plugin_name}{extra_info}") logger.info(f" 📦 {plugin_info.display_name}{extra_info}")
# Manifest信息 # Manifest信息
if plugin_info.manifest_data: if plugin_info.manifest_data:
"""
if plugin_info.keywords: if plugin_info.keywords:
logger.info(f" 🏷️ 关键词: {', '.join(plugin_info.keywords)}") logger.info(f" 🏷️ 关键词: {', '.join(plugin_info.keywords)}")
if plugin_info.categories: if plugin_info.categories:
logger.info(f" 📁 分类: {', '.join(plugin_info.categories)}") logger.info(f" 📁 分类: {', '.join(plugin_info.categories)}")
"""
if plugin_info.homepage_url: if plugin_info.homepage_url:
logger.info(f" 🌐 主页: {plugin_info.homepage_url}") logger.info(f" 🌐 主页: {plugin_info.homepage_url}")

View File

@@ -136,6 +136,8 @@ class CoreActionsPlugin(BasePlugin):
# 插件基本信息 # 插件基本信息
plugin_name = "core_actions" # 内部标识符 plugin_name = "core_actions" # 内部标识符
enable_plugin = True enable_plugin = True
dependencies = [] # 插件依赖列表
python_dependencies = [] # Python包依赖列表
config_file_name = "config.toml" config_file_name = "config.toml"
# 配置节描述 # 配置节描述

View File

@@ -109,6 +109,8 @@ class TTSPlugin(BasePlugin):
# 插件基本信息 # 插件基本信息
plugin_name = "tts_plugin" # 内部标识符 plugin_name = "tts_plugin" # 内部标识符
enable_plugin = True enable_plugin = True
dependencies = [] # 插件依赖列表
python_dependencies = [] # Python包依赖列表
config_file_name = "config.toml" config_file_name = "config.toml"
# 配置节描述 # 配置节描述

View File

@@ -110,6 +110,8 @@ class VTBPlugin(BasePlugin):
# 插件基本信息 # 插件基本信息
plugin_name = "vtb_plugin" # 内部标识符 plugin_name = "vtb_plugin" # 内部标识符
enable_plugin = True enable_plugin = True
dependencies = [] # 插件依赖列表
python_dependencies = [] # Python包依赖列表
config_file_name = "config.toml" config_file_name = "config.toml"
# 配置节描述 # 配置节描述