Files
MoviePilot/app/agent/tools/impl/update_plugin_config.py
2026-04-29 08:29:04 +08:00

154 lines
5.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""修改插件配置工具"""
import json
from typing import Any, Dict, List, Optional, Type
from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.impl._plugin_tool_utils import get_plugin_snapshot
from app.core.plugin import PluginManager
from app.log import logger
class UpdatePluginConfigInput(BaseModel):
"""修改插件配置工具的输入参数模型"""
explanation: str = Field(
...,
description="Clear explanation of why this tool is being used in the current context",
)
plugin_id: str = Field(
...,
description="The plugin ID to update. Use query_plugin_config first to inspect the current config.",
)
updates: Optional[Dict[str, Any]] = Field(
None,
description=(
"Config items to save. By default this tool merges these keys into the existing config "
"instead of replacing the whole config."
),
)
remove_keys: Optional[List[str]] = Field(
None,
description="Optional config keys to remove from the saved plugin config.",
)
replace: Optional[bool] = Field(
False,
description=(
"Whether to replace the entire saved config with 'updates'. "
"Default false, which performs a partial merge update."
),
)
class UpdatePluginConfigTool(MoviePilotTool):
name: str = "update_plugin_config"
description: str = (
"Update the saved configuration of an installed plugin. "
"By default this performs a partial merge update and does NOT reload the plugin automatically. "
"Call reload_plugin afterwards to apply the latest saved config to the running plugin."
)
require_admin: bool = True
args_schema: Type[BaseModel] = UpdatePluginConfigInput
def get_tool_message(self, **kwargs) -> Optional[str]:
"""生成友好的提示消息"""
plugin_id = kwargs.get("plugin_id", "")
replace = kwargs.get("replace", False)
action = "覆盖插件配置" if replace else "修改插件配置"
return f"{action}: {plugin_id}"
@staticmethod
async def _update_plugin_config(
plugin_id: str,
updates: Optional[Dict[str, Any]] = None,
remove_keys: Optional[List[str]] = None,
replace: bool = False,
) -> str:
"""
仅异步保存插件配置,不主动生效,让 Agent 可以先批量改完再显式重载插件。
"""
plugin_info = get_plugin_snapshot(plugin_id)
if not plugin_info:
return json.dumps(
{
"success": False,
"message": f"插件 {plugin_id} 不存在,请先使用 query_installed_plugins 查询有效插件 ID",
},
ensure_ascii=False,
)
remove_keys = remove_keys or []
if not replace and not updates and not remove_keys:
return json.dumps(
{"success": False, "message": "没有提供任何需要修改的配置项"},
ensure_ascii=False,
)
plugin_manager = PluginManager()
current_config = dict(plugin_manager.get_plugin_config(plugin_id) or {})
# merge 模式以当前保存值为基准replace 模式则从空配置开始重建。
next_config = {} if replace else dict(current_config)
if updates:
next_config.update(updates)
for key in remove_keys:
next_config.pop(key, None)
changed_keys = sorted(
key
for key in set(current_config.keys()) | set(next_config.keys())
if current_config.get(key) != next_config.get(key)
or (key in current_config) != (key in next_config)
)
if not await plugin_manager.async_save_plugin_config(plugin_id, next_config):
return json.dumps(
{
"success": False,
"message": f"保存插件 {plugin_id} 配置失败",
},
ensure_ascii=False,
)
return json.dumps(
{
"success": True,
**plugin_info,
"message": "插件配置已保存,请调用 reload_plugin 使最新配置生效",
"replace": replace,
"changed_keys": changed_keys,
"removed_keys": remove_keys,
"config_requires_reload": True,
"previous_config": current_config,
"saved_config": next_config,
},
ensure_ascii=False,
indent=2,
default=str,
)
async def run(
self,
plugin_id: str,
updates: Optional[Dict[str, Any]] = None,
remove_keys: Optional[List[str]] = None,
replace: bool = False,
**kwargs,
) -> str:
logger.info(
f"执行工具: {self.name}, 参数: plugin_id={plugin_id}, replace={replace}"
)
try:
return await self._update_plugin_config(
plugin_id, updates, remove_keys, replace
)
except Exception as e:
logger.error(f"修改插件配置失败: {e}", exc_info=True)
return json.dumps(
{"success": False, "message": f"修改插件配置时发生错误: {str(e)}"},
ensure_ascii=False,
)