mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-28 11:37:23 +08:00
301 lines
14 KiB
Python
301 lines
14 KiB
Python
from typing import Callable, List, Optional, Type
|
|
|
|
from app.agent.tools.impl.add_download_tasks import AddDownloadTasksTool
|
|
from app.agent.tools.impl.add_subscribe import AddSubscribeTool
|
|
from app.agent.tools.impl.update_subscribe import UpdateSubscribeTool
|
|
from app.agent.tools.impl.search_subscribe import SearchSubscribeTool
|
|
from app.agent.tools.impl.get_recommendations import GetRecommendationsTool
|
|
from app.agent.tools.impl.query_downloaders import QueryDownloadersTool
|
|
from app.agent.tools.impl.query_download_tasks import QueryDownloadTasksTool
|
|
from app.agent.tools.impl.query_library_exists import QueryLibraryExistsTool
|
|
from app.agent.tools.impl.query_library_latest import QueryLibraryLatestTool
|
|
from app.agent.tools.impl.query_sites import QuerySitesTool
|
|
from app.agent.tools.impl.update_site import UpdateSiteTool
|
|
from app.agent.tools.impl.query_site_userdata import QuerySiteUserdataTool
|
|
from app.agent.tools.impl.test_site import TestSiteTool
|
|
from app.agent.tools.impl.query_subscribes import QuerySubscribesTool
|
|
from app.agent.tools.impl.query_subscribe_shares import QuerySubscribeSharesTool
|
|
from app.agent.tools.impl.query_rule_groups import QueryRuleGroupsTool
|
|
from app.agent.tools.impl.query_builtin_filter_rules import QueryBuiltinFilterRulesTool
|
|
from app.agent.tools.impl.query_custom_filter_rules import QueryCustomFilterRulesTool
|
|
from app.agent.tools.impl.add_custom_filter_rule import AddCustomFilterRuleTool
|
|
from app.agent.tools.impl.update_custom_filter_rule import UpdateCustomFilterRuleTool
|
|
from app.agent.tools.impl.delete_custom_filter_rule import DeleteCustomFilterRuleTool
|
|
from app.agent.tools.impl.add_rule_group import AddRuleGroupTool
|
|
from app.agent.tools.impl.update_rule_group import UpdateRuleGroupTool
|
|
from app.agent.tools.impl.delete_rule_group import DeleteRuleGroupTool
|
|
from app.agent.tools.impl.query_popular_subscribes import QueryPopularSubscribesTool
|
|
from app.agent.tools.impl.query_subscribe_history import QuerySubscribeHistoryTool
|
|
from app.agent.tools.impl.delete_subscribe import DeleteSubscribeTool
|
|
from app.agent.tools.impl.search_media import SearchMediaTool
|
|
from app.agent.tools.impl.search_person import SearchPersonTool
|
|
from app.agent.tools.impl.search_person_credits import SearchPersonCreditsTool
|
|
from app.agent.tools.impl.recognize_media import RecognizeMediaTool
|
|
from app.agent.tools.impl.scrape_metadata import ScrapeMetadataTool
|
|
from app.agent.tools.impl.query_episode_schedule import QueryEpisodeScheduleTool
|
|
from app.agent.tools.impl.query_media_detail import QueryMediaDetailTool
|
|
from app.agent.tools.impl.search_torrents import SearchTorrentsTool
|
|
from app.agent.tools.impl.get_search_results import GetSearchResultsTool
|
|
from app.agent.tools.impl.search_web import SearchWebTool
|
|
from app.agent.tools.impl.recognize_captcha import RecognizeCaptchaTool
|
|
from app.agent.tools.impl.send_message import SendMessageTool
|
|
from app.agent.tools.impl.ask_user_choice import AskUserChoiceTool
|
|
from app.agent.tools.impl.send_local_file import SendLocalFileTool
|
|
from app.agent.tools.impl.send_voice_message import SendVoiceMessageTool
|
|
from app.agent.tools.impl.query_schedulers import QuerySchedulersTool
|
|
from app.agent.tools.impl.run_scheduler import RunSchedulerTool
|
|
from app.agent.tools.impl.query_workflows import QueryWorkflowsTool
|
|
from app.agent.tools.impl.run_workflow import RunWorkflowTool
|
|
from app.agent.tools.impl.query_personas import QueryPersonasTool
|
|
from app.agent.tools.impl.switch_persona import SwitchPersonaTool
|
|
from app.agent.tools.impl.update_persona_definition import UpdatePersonaDefinitionTool
|
|
from app.agent.tools.impl.update_site_cookie import UpdateSiteCookieTool
|
|
from app.agent.tools.impl.delete_download_tasks import DeleteDownloadTasksTool
|
|
from app.agent.tools.impl.delete_download_history import DeleteDownloadHistoryTool
|
|
from app.agent.tools.impl.delete_transfer_history import DeleteTransferHistoryTool
|
|
from app.agent.tools.impl.update_download_tasks import UpdateDownloadTasksTool
|
|
from app.agent.tools.impl.query_directory_settings import QueryDirectorySettingsTool
|
|
from app.agent.tools.impl.list_directory import ListDirectoryTool
|
|
from app.agent.tools.impl.query_transfer_history import QueryTransferHistoryTool
|
|
from app.agent.tools.impl.transfer_file import TransferFileTool
|
|
from app.agent.tools.impl.execute_command import ExecuteCommandTool
|
|
from app.agent.tools.impl.edit_file import EditFileTool
|
|
from app.agent.tools.impl.write_file import WriteFileTool
|
|
from app.agent.tools.impl.read_file import ReadFileTool
|
|
from app.agent.tools.impl.browse_webpage import BrowseWebpageTool
|
|
from app.agent.tools.impl.query_installed_plugins import QueryInstalledPluginsTool
|
|
from app.agent.tools.impl.query_market_plugins import QueryMarketPluginsTool
|
|
from app.agent.tools.impl.query_plugin_capabilities import QueryPluginCapabilitiesTool
|
|
from app.agent.tools.impl.query_plugin_config import QueryPluginConfigTool
|
|
from app.agent.tools.impl.update_plugin_config import UpdatePluginConfigTool
|
|
from app.agent.tools.impl.reload_plugin import ReloadPluginTool
|
|
from app.agent.tools.impl.query_plugin_data import QueryPluginDataTool
|
|
from app.agent.tools.impl.install_plugin import InstallPluginTool
|
|
from app.agent.tools.impl.uninstall_plugin import UninstallPluginTool
|
|
from app.agent.tools.impl.run_slash_command import RunSlashCommandTool
|
|
from app.agent.tools.impl.list_slash_commands import ListSlashCommandsTool
|
|
from app.agent.tools.impl.query_custom_identifiers import QueryCustomIdentifiersTool
|
|
from app.agent.tools.impl.query_doctor_report import QueryDoctorReportTool
|
|
from app.agent.tools.impl.update_custom_identifiers import UpdateCustomIdentifiersTool
|
|
from app.agent.tools.impl.query_system_settings import QuerySystemSettingsTool
|
|
from app.agent.tools.impl.update_system_settings import UpdateSystemSettingsTool
|
|
from app.agent.llm.capability import AgentCapabilityManager
|
|
from app.core.plugin import PluginManager
|
|
from app.log import logger
|
|
from app.schemas.message import ChannelCapabilityManager
|
|
from app.schemas.types import MessageChannel
|
|
from .base import MoviePilotTool
|
|
|
|
|
|
class MoviePilotToolFactory:
|
|
"""
|
|
MoviePilot工具工厂
|
|
"""
|
|
|
|
BUILTIN_TOOL_CLASSES: tuple[Type[MoviePilotTool], ...] = (
|
|
SearchMediaTool,
|
|
SearchPersonTool,
|
|
SearchPersonCreditsTool,
|
|
RecognizeMediaTool,
|
|
ScrapeMetadataTool,
|
|
QueryEpisodeScheduleTool,
|
|
QueryMediaDetailTool,
|
|
AddSubscribeTool,
|
|
UpdateSubscribeTool,
|
|
SearchSubscribeTool,
|
|
SearchTorrentsTool,
|
|
GetSearchResultsTool,
|
|
SearchWebTool,
|
|
RecognizeCaptchaTool,
|
|
AddDownloadTasksTool,
|
|
QuerySubscribesTool,
|
|
QuerySubscribeSharesTool,
|
|
QueryPopularSubscribesTool,
|
|
QueryBuiltinFilterRulesTool,
|
|
QueryCustomFilterRulesTool,
|
|
QueryRuleGroupsTool,
|
|
AddCustomFilterRuleTool,
|
|
UpdateCustomFilterRuleTool,
|
|
DeleteCustomFilterRuleTool,
|
|
AddRuleGroupTool,
|
|
UpdateRuleGroupTool,
|
|
DeleteRuleGroupTool,
|
|
QuerySubscribeHistoryTool,
|
|
DeleteSubscribeTool,
|
|
QueryDownloadTasksTool,
|
|
DeleteDownloadTasksTool,
|
|
DeleteDownloadHistoryTool,
|
|
DeleteTransferHistoryTool,
|
|
UpdateDownloadTasksTool,
|
|
QueryDownloadersTool,
|
|
QuerySitesTool,
|
|
UpdateSiteTool,
|
|
QuerySiteUserdataTool,
|
|
TestSiteTool,
|
|
UpdateSiteCookieTool,
|
|
GetRecommendationsTool,
|
|
QueryLibraryExistsTool,
|
|
QueryLibraryLatestTool,
|
|
QueryDirectorySettingsTool,
|
|
ListDirectoryTool,
|
|
QueryTransferHistoryTool,
|
|
TransferFileTool,
|
|
SendMessageTool,
|
|
QuerySchedulersTool,
|
|
RunSchedulerTool,
|
|
QueryWorkflowsTool,
|
|
RunWorkflowTool,
|
|
QueryPersonasTool,
|
|
SwitchPersonaTool,
|
|
UpdatePersonaDefinitionTool,
|
|
ExecuteCommandTool,
|
|
EditFileTool,
|
|
WriteFileTool,
|
|
ReadFileTool,
|
|
BrowseWebpageTool,
|
|
QueryInstalledPluginsTool,
|
|
QueryMarketPluginsTool,
|
|
QueryPluginCapabilitiesTool,
|
|
QueryPluginConfigTool,
|
|
UpdatePluginConfigTool,
|
|
ReloadPluginTool,
|
|
QueryPluginDataTool,
|
|
InstallPluginTool,
|
|
UninstallPluginTool,
|
|
RunSlashCommandTool,
|
|
ListSlashCommandsTool,
|
|
QueryDoctorReportTool,
|
|
QueryCustomIdentifiersTool,
|
|
UpdateCustomIdentifiersTool,
|
|
QuerySystemSettingsTool,
|
|
UpdateSystemSettingsTool,
|
|
)
|
|
|
|
# 这些通用工具需要始终保留,避免大工具集裁剪后让 Agent 丢失基础的
|
|
# 文件系统、命令执行、历史检索或交互确认能力。AskUserChoiceTool 仅在支持按钮
|
|
# 的渠道中才会实际注入,因此后续会再按已加载工具做一次求交集。
|
|
TOOL_SELECTOR_ALWAYS_INCLUDE_NAMES = (
|
|
"list_directory",
|
|
"write_file",
|
|
"read_file",
|
|
"edit_file",
|
|
"execute_command",
|
|
"ask_user_choice",
|
|
)
|
|
|
|
@staticmethod
|
|
def _should_enable_choice_tool(channel: Optional[str] = None) -> bool:
|
|
if not channel:
|
|
return False
|
|
try:
|
|
message_channel = MessageChannel(channel)
|
|
except ValueError:
|
|
return False
|
|
return ChannelCapabilityManager.supports_buttons(
|
|
message_channel
|
|
) and ChannelCapabilityManager.supports_callbacks(message_channel)
|
|
|
|
@classmethod
|
|
def get_tool_selector_always_include_names(
|
|
cls, tools: List[MoviePilotTool]
|
|
) -> List[str]:
|
|
"""
|
|
返回当前实际已加载且需要绕过工具筛选的工具名。
|
|
|
|
`LLMToolSelectorMiddleware` 会校验 `always_include` 中的工具名是否
|
|
存在于当前请求里,因此这里必须根据运行时工具列表做交集过滤。
|
|
"""
|
|
available_tool_names = {
|
|
tool.name for tool in tools if getattr(tool, "name", None)
|
|
}
|
|
return [
|
|
tool_name
|
|
for tool_name in cls.TOOL_SELECTOR_ALWAYS_INCLUDE_NAMES
|
|
if tool_name in available_tool_names
|
|
]
|
|
|
|
@classmethod
|
|
def _get_builtin_tool_classes(
|
|
cls, channel: Optional[str] = None
|
|
) -> list[Type[MoviePilotTool]]:
|
|
"""
|
|
返回当前渠道可用的内置工具类清单。
|
|
"""
|
|
tool_definitions = list(cls.BUILTIN_TOOL_CLASSES)
|
|
if cls._should_enable_choice_tool(channel):
|
|
tool_definitions.append(AskUserChoiceTool)
|
|
tool_definitions.append(SendLocalFileTool)
|
|
if AgentCapabilityManager.supports_audio_output():
|
|
tool_definitions.append(SendVoiceMessageTool)
|
|
return tool_definitions
|
|
|
|
@classmethod
|
|
def create_tools(
|
|
cls,
|
|
session_id: str,
|
|
user_id: str,
|
|
channel: str = None,
|
|
source: str = None,
|
|
username: str = None,
|
|
stream_handler: Callable = None,
|
|
agent_context: dict = None,
|
|
allow_message_tools: bool = True,
|
|
) -> List[MoviePilotTool]:
|
|
"""
|
|
创建MoviePilot工具列表
|
|
"""
|
|
tools = []
|
|
tool_definitions = cls._get_builtin_tool_classes(channel)
|
|
# 创建内置工具
|
|
for ToolClass in tool_definitions:
|
|
tool = ToolClass(session_id=session_id, user_id=user_id)
|
|
if not allow_message_tools and getattr(tool, "sends_message", False):
|
|
continue
|
|
tool.set_message_attr(channel=channel, source=source, username=username)
|
|
tool.set_stream_handler(stream_handler=stream_handler)
|
|
tool.set_agent_context(agent_context=agent_context)
|
|
tools.append(tool)
|
|
|
|
# 加载插件提供的工具
|
|
plugin_tools_count = 0
|
|
plugin_tools_info = PluginManager().get_plugin_agent_tools()
|
|
for plugin_info in plugin_tools_info:
|
|
plugin_id = plugin_info.get("plugin_id")
|
|
plugin_name = plugin_info.get("plugin_name")
|
|
tool_classes = plugin_info.get("tools", [])
|
|
for ToolClass in tool_classes:
|
|
try:
|
|
# 验证工具类是否继承自 MoviePilotTool
|
|
if not issubclass(ToolClass, MoviePilotTool):
|
|
logger.warning(
|
|
f"插件 {plugin_name}({plugin_id}) 提供的工具类 {ToolClass.__name__} 未继承自 MoviePilotTool,已跳过"
|
|
)
|
|
continue
|
|
# 创建工具实例
|
|
tool = ToolClass(session_id=session_id, user_id=user_id)
|
|
if not allow_message_tools and getattr(tool, "sends_message", False):
|
|
continue
|
|
tool.set_message_attr(
|
|
channel=channel, source=source, username=username
|
|
)
|
|
tool.set_stream_handler(stream_handler=stream_handler)
|
|
tool.set_agent_context(agent_context=agent_context)
|
|
tools.append(tool)
|
|
plugin_tools_count += 1
|
|
logger.debug(
|
|
f"成功加载插件 {plugin_name}({plugin_id}) 的工具: {ToolClass.__name__}"
|
|
)
|
|
except Exception as e:
|
|
logger.error(
|
|
f"加载插件 {plugin_name}({plugin_id}) 的工具 {ToolClass.__name__} 失败: {str(e)}"
|
|
)
|
|
|
|
builtin_tools_count = len(tool_definitions)
|
|
if plugin_tools_count > 0:
|
|
logger.debug(
|
|
f"成功创建 {len(tools)} 个MoviePilot工具(内置工具: {builtin_tools_count} 个,插件工具: {plugin_tools_count} 个)"
|
|
)
|
|
else:
|
|
logger.debug(f"成功创建 {len(tools)} 个MoviePilot工具")
|
|
return tools
|