refactor(plugin): 优化插件配置加载与同步机制
- 移除全局插件配置开关 `plugins.centralized_config`。 - 简化 `PluginBase` 的配置加载逻辑,不再使用模板文件,而是直接在中央配置目录生成默认配置。 - 在 `PluginManager` 中增加配置同步逻辑,在加载插件时,实现插件目录与中央配置目录之间的双向同步,确保配置一致性。 - 更新 `bot_config_template.toml`,移除已废弃的 `[plugins]` 配置项并提升版本号。
This commit is contained in:
@@ -40,7 +40,6 @@ from src.config.official_configs import (
|
|||||||
DependencyManagementConfig,
|
DependencyManagementConfig,
|
||||||
WebSearchConfig,
|
WebSearchConfig,
|
||||||
AntiPromptInjectionConfig,
|
AntiPromptInjectionConfig,
|
||||||
PluginsConfig,
|
|
||||||
SleepSystemConfig,
|
SleepSystemConfig,
|
||||||
MonthlyPlanSystemConfig,
|
MonthlyPlanSystemConfig,
|
||||||
CrossContextConfig,
|
CrossContextConfig,
|
||||||
@@ -389,7 +388,6 @@ class Config(ValidatedConfigBase):
|
|||||||
video_analysis: VideoAnalysisConfig = Field(default_factory=lambda: VideoAnalysisConfig(), description="视频分析配置")
|
video_analysis: VideoAnalysisConfig = Field(default_factory=lambda: VideoAnalysisConfig(), description="视频分析配置")
|
||||||
dependency_management: DependencyManagementConfig = Field(default_factory=lambda: DependencyManagementConfig(), description="依赖管理配置")
|
dependency_management: DependencyManagementConfig = Field(default_factory=lambda: DependencyManagementConfig(), description="依赖管理配置")
|
||||||
web_search: WebSearchConfig = Field(default_factory=lambda: WebSearchConfig(), description="网络搜索配置")
|
web_search: WebSearchConfig = Field(default_factory=lambda: WebSearchConfig(), description="网络搜索配置")
|
||||||
plugins: PluginsConfig = Field(default_factory=lambda: PluginsConfig(), description="插件配置")
|
|
||||||
sleep_system: SleepSystemConfig = Field(default_factory=lambda: SleepSystemConfig(), description="睡眠系统配置")
|
sleep_system: SleepSystemConfig = Field(default_factory=lambda: SleepSystemConfig(), description="睡眠系统配置")
|
||||||
monthly_plan_system: MonthlyPlanSystemConfig = Field(default_factory=lambda: MonthlyPlanSystemConfig(), description="月层计划系统配置")
|
monthly_plan_system: MonthlyPlanSystemConfig = Field(default_factory=lambda: MonthlyPlanSystemConfig(), description="月层计划系统配置")
|
||||||
cross_context: CrossContextConfig = Field(default_factory=lambda: CrossContextConfig(), description="跨群聊上下文共享配置")
|
cross_context: CrossContextConfig = Field(default_factory=lambda: CrossContextConfig(), description="跨群聊上下文共享配置")
|
||||||
|
|||||||
@@ -600,13 +600,6 @@ class AntiPromptInjectionConfig(ValidatedConfigBase):
|
|||||||
shield_suffix: str = Field(default=" 🛡️", description="保护后缀")
|
shield_suffix: str = Field(default=" 🛡️", description="保护后缀")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PluginsConfig(ValidatedConfigBase):
|
|
||||||
"""插件配置"""
|
|
||||||
|
|
||||||
centralized_config: bool = Field(default=True, description="是否启用插件配置集中化管理")
|
|
||||||
|
|
||||||
|
|
||||||
class SleepSystemConfig(ValidatedConfigBase):
|
class SleepSystemConfig(ValidatedConfigBase):
|
||||||
"""睡眠系统配置类"""
|
"""睡眠系统配置类"""
|
||||||
|
|
||||||
|
|||||||
@@ -458,54 +458,35 @@ class PluginBase(ABC):
|
|||||||
加载插件配置文件,实现集中化管理和自动迁移。
|
加载插件配置文件,实现集中化管理和自动迁移。
|
||||||
|
|
||||||
处理逻辑:
|
处理逻辑:
|
||||||
1. 确定插件模板配置文件路径(位于插件目录内)。
|
1. 确定用户配置文件路径(位于 `config/plugins/` 目录下)。
|
||||||
2. 如果模板不存在,则在插件目录内生成一份默认配置。
|
2. 如果用户配置文件不存在,则根据 config_schema 直接在中央目录生成一份。
|
||||||
3. 确定用户配置文件路径(位于 `config/plugins/` 目录下)。
|
3. 加载用户配置文件,并进行版本检查和自动迁移(如果需要)。
|
||||||
4. 如果用户配置文件不存在,则从插件目录复制模板文件过去。
|
4. 最终加载的配置是用户配置文件。
|
||||||
5. 加载用户配置文件,并进行版本检查和自动迁移(如果需要)。
|
|
||||||
6. 最终加载的配置是用户配置文件。
|
|
||||||
"""
|
"""
|
||||||
if not self.config_file_name:
|
if not self.config_file_name:
|
||||||
logger.debug(f"{self.log_prefix} 未指定配置文件,跳过加载")
|
logger.debug(f"{self.log_prefix} 未指定配置文件,跳过加载")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 1. 确定插件模板配置文件路径
|
# 1. 确定并确保用户配置文件路径存在
|
||||||
template_config_path = os.path.join(self.plugin_dir, self.config_file_name)
|
user_config_path = os.path.join(CONFIG_DIR, "plugins", self.plugin_name, self.config_file_name)
|
||||||
|
os.makedirs(os.path.dirname(user_config_path), exist_ok=True)
|
||||||
|
|
||||||
# 2. 如果模板不存在,则在插件目录内生成
|
# 2. 如果用户配置文件不存在,直接在中央目录生成
|
||||||
if not os.path.exists(template_config_path):
|
|
||||||
logger.info(f"{self.log_prefix} 插件目录缺少配置文件 {template_config_path},将生成默认配置。")
|
|
||||||
self._generate_and_save_default_config(template_config_path)
|
|
||||||
|
|
||||||
# 3. 确定用户配置文件路径
|
|
||||||
plugin_config_dir = os.path.join(CONFIG_DIR, "plugins", self.plugin_name)
|
|
||||||
user_config_path = os.path.join(plugin_config_dir, self.config_file_name)
|
|
||||||
|
|
||||||
# 确保用户插件配置目录存在
|
|
||||||
os.makedirs(plugin_config_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# 4. 如果用户配置文件不存在,从模板复制
|
|
||||||
if not os.path.exists(user_config_path):
|
if not os.path.exists(user_config_path):
|
||||||
try:
|
logger.info(f"{self.log_prefix} 用户配置文件 {user_config_path} 不存在,将生成默认配置。")
|
||||||
shutil.copy2(template_config_path, user_config_path)
|
self._generate_and_save_default_config(user_config_path)
|
||||||
logger.info(f"{self.log_prefix} 已从模板创建用户配置文件: {user_config_path}")
|
|
||||||
except IOError as e:
|
|
||||||
logger.error(f"{self.log_prefix} 复制配置文件失败: {e}", exc_info=True)
|
|
||||||
# 如果复制失败,后续将无法加载,直接返回
|
|
||||||
return
|
|
||||||
|
|
||||||
# 检查最终的用户配置文件是否存在
|
# 检查最终的用户配置文件是否存在
|
||||||
if not os.path.exists(user_config_path):
|
if not os.path.exists(user_config_path):
|
||||||
logger.warning(f"{self.log_prefix} 用户配置文件 {user_config_path} 不存在且无法创建。")
|
logger.warning(f"{self.log_prefix} 用户配置文件 {user_config_path} 不存在且无法创建。")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 5. 加载、检查和迁移用户配置文件
|
# 3. 加载、检查和迁移用户配置文件
|
||||||
_, file_ext = os.path.splitext(self.config_file_name)
|
_, file_ext = os.path.splitext(self.config_file_name)
|
||||||
if file_ext.lower() != ".toml":
|
if file_ext.lower() != ".toml":
|
||||||
logger.warning(f"{self.log_prefix} 不支持的配置文件格式: {file_ext},仅支持 .toml")
|
logger.warning(f"{self.log_prefix} 不支持的配置文件格式: {file_ext},仅支持 .toml")
|
||||||
self.config = {}
|
self.config = {}
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(user_config_path, "r", encoding="utf-8") as f:
|
with open(user_config_path, "r", encoding="utf-8") as f:
|
||||||
existing_config = toml.load(f) or {}
|
existing_config = toml.load(f) or {}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
import hashlib
|
||||||
import traceback
|
import traceback
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
@@ -37,6 +39,48 @@ class PluginManager:
|
|||||||
self._ensure_plugin_directories()
|
self._ensure_plugin_directories()
|
||||||
logger.info("插件管理器初始化完成")
|
logger.info("插件管理器初始化完成")
|
||||||
|
|
||||||
|
def _synchronize_plugin_config(self, plugin_name: str, plugin_dir: str):
|
||||||
|
"""
|
||||||
|
同步单个插件的配置。
|
||||||
|
"""
|
||||||
|
central_config_dir = os.path.join("config", "plugins", plugin_name)
|
||||||
|
plugin_config_dir = os.path.join(plugin_dir, "config")
|
||||||
|
|
||||||
|
# 确保中央配置目录存在
|
||||||
|
os.makedirs(central_config_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 1. 从插件目录同步到中央目录(如果中央配置不存在)
|
||||||
|
if os.path.exists(plugin_config_dir) and os.path.isdir(plugin_config_dir):
|
||||||
|
for filename in os.listdir(plugin_config_dir):
|
||||||
|
central_config_file = os.path.join(central_config_dir, filename)
|
||||||
|
plugin_config_file = os.path.join(plugin_config_dir, filename)
|
||||||
|
|
||||||
|
if not os.path.exists(central_config_file) and os.path.isfile(plugin_config_file):
|
||||||
|
shutil.copy2(plugin_config_file, central_config_file)
|
||||||
|
logger.info(f"从 {plugin_name} 复制默认配置到中央目录: {filename}")
|
||||||
|
|
||||||
|
# 2. 从中央目录同步到插件目录(覆盖)
|
||||||
|
if os.path.isdir(central_config_dir):
|
||||||
|
for filename in os.listdir(central_config_dir):
|
||||||
|
central_config_file = os.path.join(central_config_dir, filename)
|
||||||
|
plugin_config_file = os.path.join(plugin_config_dir, filename)
|
||||||
|
|
||||||
|
if not os.path.isfile(central_config_file):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 确保插件的 config 目录存在
|
||||||
|
os.makedirs(plugin_config_dir, exist_ok=True)
|
||||||
|
|
||||||
|
should_copy = True
|
||||||
|
if os.path.exists(plugin_config_file):
|
||||||
|
with open(central_config_file, 'rb') as f1, open(plugin_config_file, 'rb') as f2:
|
||||||
|
if hashlib.md5(f1.read()).hexdigest() == hashlib.md5(f2.read()).hexdigest():
|
||||||
|
should_copy = False
|
||||||
|
|
||||||
|
if should_copy:
|
||||||
|
shutil.copy2(central_config_file, plugin_config_file)
|
||||||
|
logger.info(f"同步中央配置到 {plugin_name}: {filename}")
|
||||||
|
|
||||||
# === 插件目录管理 ===
|
# === 插件目录管理 ===
|
||||||
|
|
||||||
def add_plugin_directory(self, directory: str) -> bool:
|
def add_plugin_directory(self, directory: str) -> bool:
|
||||||
@@ -104,6 +148,9 @@ class PluginManager:
|
|||||||
if not plugin_dir:
|
if not plugin_dir:
|
||||||
return False, 1
|
return False, 1
|
||||||
|
|
||||||
|
# 同步插件配置
|
||||||
|
self._synchronize_plugin_config(plugin_name, plugin_dir)
|
||||||
|
|
||||||
plugin_instance = plugin_class(plugin_dir=plugin_dir) # 实例化插件(可能因为缺少manifest而失败)
|
plugin_instance = plugin_class(plugin_dir=plugin_dir) # 实例化插件(可能因为缺少manifest而失败)
|
||||||
if not plugin_instance:
|
if not plugin_instance:
|
||||||
logger.error(f"插件 {plugin_name} 实例化失败")
|
logger.error(f"插件 {plugin_name} 实例化失败")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[inner]
|
[inner]
|
||||||
version = "6.5.8"
|
version = "6.6.0"
|
||||||
|
|
||||||
#----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读----
|
#----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读----
|
||||||
#如果你想要修改配置文件,请递增version的值
|
#如果你想要修改配置文件,请递增version的值
|
||||||
@@ -411,9 +411,6 @@ exa_api_keys = ["None"]# EXA API密钥列表,支持轮询机制
|
|||||||
enabled_engines = ["ddg"] # 启用的搜索引擎列表,可选: "exa", "tavily", "ddg","bing"
|
enabled_engines = ["ddg"] # 启用的搜索引擎列表,可选: "exa", "tavily", "ddg","bing"
|
||||||
search_strategy = "single" # 搜索策略: "single"(使用第一个可用引擎), "parallel"(并行使用所有启用的引擎), "fallback"(按顺序尝试,失败则尝试下一个)
|
search_strategy = "single" # 搜索策略: "single"(使用第一个可用引擎), "parallel"(并行使用所有启用的引擎), "fallback"(按顺序尝试,失败则尝试下一个)
|
||||||
|
|
||||||
[plugins] # 插件配置
|
|
||||||
centralized_config = true # 是否启用插件配置集中化管理
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
# 月度计划系统设置 (Monthly Plan System Settings)
|
# 月度计划系统设置 (Monthly Plan System Settings)
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user