refactor: unify agent data port access

This commit is contained in:
jxxghp
2026-08-24 04:56:11 +08:00
parent c8ad54a190
commit ae38e61880
37 changed files with 217 additions and 87 deletions
+4 -4
View File
@@ -9,7 +9,7 @@ from langchain_core.messages import BaseMessage, messages_from_dict, messages_to
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.application.agentdata import AgentChatPort as AgentChatOper
from app.application.agentdata import get_agent_chat_port
from app.application.messaging.chat import (
get_configured_agent_chat_persistence,
get_configured_agent_chat_service,
@@ -86,9 +86,9 @@ class MemoryManager:
return memory.messages
try:
chat = AgentChatOper().get(session_id=session_id, user_id=user_id)
chat = get_agent_chat_port().get(session_id=session_id, user_id=user_id)
if not chat:
chat = AgentChatOper().get(session_id=session_id)
chat = get_agent_chat_port().get(session_id=session_id)
except Exception as e:
logger.debug(f"读取持久化Agent会话失败: {e}")
return []
@@ -161,7 +161,7 @@ class MemoryManager:
# 更新内存缓存
self.save_memory(memory)
try:
AgentChatOper().save_agent_messages(
get_agent_chat_port().save_agent_messages(
session_id=session_id,
user_id=user_id,
messages=messages_to_dict(messages),
+4 -4
View File
@@ -79,8 +79,8 @@ from app.application.plugin.runtime import get_plugin_manager
def _get_plugin_tools_revision() -> int:
"""读取插件工具目录修订号,避免 Agent 编排依赖具体管理器类型。"""
return get_plugin_manager().get_plugin_agent_tools_revision()
from app.application.agentdata import AgentTaskPort as AgentTaskOper
from app.application.agentdata import UserPort as UserOper
from app.application.agentdata import get_agent_task_port
from app.application.agentdata import get_agent_user_port
from app.application.messaging.chat import (
get_configured_agent_chat_service,
get_configured_agent_chat_persistence,
@@ -977,7 +977,7 @@ class MoviePilotAgent:
if not self.username:
return False
try:
user = await UserOper().async_get_by_name(self.username)
user = await get_agent_user_port().async_get_by_name(self.username)
except Exception as e:
logger.error(f"检查 Agent 用户管理员身份失败: {e}")
return False
@@ -3514,7 +3514,7 @@ class AgentManager:
"""
if not settings.AI_AGENT_ENABLE:
return False, "AI Agent 未启用"
oper = AgentTaskOper()
oper = get_agent_task_port()
task = oper.get(task_id)
if not task or not task.enabled:
return False, "Agent 定时任务不存在或已停用"
+4 -4
View File
@@ -5,7 +5,7 @@ import re
from typing import Any, Dict, Iterable, Optional
from app.runtime.events import eventmanager
from app.application.agentdata import SubscribePort as SubscribeOper
from app.application.agentdata import get_agent_subscribe_port
from app.application.configuration import get_configured_system_config
from app.application.rules import RuleHelper
from app.application.rules import RuleParser
@@ -284,7 +284,7 @@ async def collect_rule_group_usages(
continue
ensure_usage(name)["used_in_global_best_version"] = True
subscribes = await SubscribeOper().async_list()
subscribes = await get_agent_subscribe_port().async_list()
for subscribe in subscribes:
filter_groups = subscribe.filter_groups or []
for name in filter_groups:
@@ -481,7 +481,7 @@ async def rename_rule_group_references(old_name: str, new_name: str) -> dict:
await save_system_config(config_key, updated)
changed["global_settings"][config_key.value] = updated
subscribe_oper = SubscribeOper()
subscribe_oper = get_agent_subscribe_port()
subscribes = await subscribe_oper.async_list()
for subscribe in subscribes:
original = subscribe.filter_groups or []
@@ -519,7 +519,7 @@ async def remove_rule_group_references(group_name: str) -> dict:
await save_system_config(config_key, updated)
changed["global_settings"][config_key.value] = updated
subscribe_oper = SubscribeOper()
subscribe_oper = get_agent_subscribe_port()
subscribes = await subscribe_oper.async_list()
for subscribe in subscribes:
original = subscribe.filter_groups or []
+2 -2
View File
@@ -17,7 +17,7 @@ from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.domain.context import Context
from app.domain.metainfo import MetaInfo
from app.application.agentdata import SitePort as SiteOper
from app.application.agentdata import get_agent_site_port
from app.application.directory import DirectoryHelper, validate_download_save_path
from app.runtime.log import logger
from app.schemas.file import FileURI
@@ -277,7 +277,7 @@ class AddDownloadTasksTool(MoviePilotTool):
failed_messages.append(f"{torrent_input} 缺少站点名称")
continue
siteinfo = await SiteOper().async_get_by_name(site_name)
siteinfo = await get_agent_site_port().async_get_by_name(site_name)
if not siteinfo:
failed_messages.append(f"{torrent_input} 未找到站点信息 {site_name}")
continue
+2 -2
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.subscribe import SubscribeChain
from app.application.agentdata import UserPort as UserOper
from app.application.agentdata import get_agent_user_port
from app.runtime.log import logger
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaSource, MediaType, NotificationChannel
from app.domain.media import normalize_music_type
@@ -154,7 +154,7 @@ class AddSubscribeTool(MoviePilotTool):
mapped_username = await self.run_blocking(
"db",
UserOper().get_name,
get_agent_user_port().get_name,
**{key: self._user_id for key in binding_keys},
)
return mapped_username or resolved_username
+5 -5
View File
@@ -10,8 +10,8 @@ from app.agent.tools.tags import ToolTag
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.application.agentdata import AgentChatPort as AgentChatOper
from app.application.agentdata import AgentTaskPort as AgentTaskOper
from app.application.agentdata import get_agent_chat_port
from app.application.agentdata import get_agent_task_port
from app.runtime.scheduling import TimerUtils
@@ -115,11 +115,11 @@ class CreateAgentTaskTool(MoviePilotTool):
timezone_name=settings.TZ,
require_future=True,
)
chat = AgentChatOper().get(
chat = get_agent_chat_port().get(
session_id=self._session_id,
user_id=self._user_id,
)
task = AgentTaskOper().add(
task = get_agent_task_port().add(
name=payload.name.strip(),
content=payload.content.strip(),
trigger_type=payload.trigger_type,
@@ -133,7 +133,7 @@ class CreateAgentTaskTool(MoviePilotTool):
original_chat_id=chat.original_chat_id if chat else None,
)
next_run_at = update_agent_task_job(task.id)
return AgentTaskOper.to_dict(
return get_agent_task_port().to_dict(
task,
next_run_at=next_run_at,
timezone=settings.TZ,
+2 -2
View File
@@ -4,7 +4,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import AgentTaskPort as AgentTaskOper
from app.application.agentdata import get_agent_task_port
class DeleteAgentTaskInput(BaseModel):
@@ -33,7 +33,7 @@ class DeleteAgentTaskTool(MoviePilotTool):
"""删除当前用户的任务并移除运行时调度。"""
from app.application.scheduling import remove_agent_task_job
deleted = AgentTaskOper().delete(
deleted = get_agent_task_port().delete(
task_id=task_id,
user_id=str(self._user_id),
)
@@ -6,7 +6,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import DownloadHistoryPort as DownloadHistoryOper
from app.application.agentdata import get_agent_download_history_port
from app.runtime.log import logger
@@ -37,7 +37,7 @@ class DeleteDownloadHistoryTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: history_id={history_id}")
try:
await DownloadHistoryOper().async_delete_history(history_id)
await get_agent_download_history_port().async_delete_history(history_id)
return f"下载历史记录 ID: {history_id} 已成功删除"
except Exception as e:
logger.error(f"删除下载历史记录失败: {e}", exc_info=True)
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.storage import StorageChain
from app.application.agentdata import TransferHistoryPort as TransferHistoryOper
from app.application.agentdata import get_agent_transfer_history_port
from app.runtime.log import logger
from app.schemas.workflow import FileItem
@@ -53,7 +53,7 @@ class DeleteTransferHistoryTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: history_id={history_id}")
try:
transferhis = TransferHistoryOper()
transferhis = get_agent_transfer_history_port()
history = await transferhis.async_get(history_id)
if not history:
return f"错误:整理历史记录不存在,ID={history_id}"
+2 -2
View File
@@ -8,7 +8,7 @@ from app.agent.tools.tags import ToolTag
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.application.agentdata import AgentTaskPort as AgentTaskOper
from app.application.agentdata import get_agent_task_port
class QueryAgentTasksInput(BaseModel):
@@ -52,7 +52,7 @@ class QueryAgentTasksTool(MoviePilotTool):
"""读取当前用户的任务及运行时下一次触发时间。"""
from app.application.scheduling import get_agent_task_next_run
oper = AgentTaskOper()
oper = get_agent_task_port()
if task_id:
task = oper.get(task_id=task_id, user_id=str(self._user_id))
tasks = [task] if task else []
+2 -2
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.download import DownloadChain
from app.application.agentdata import DownloadHistoryPort as DownloadHistoryOper
from app.application.agentdata import get_agent_download_history_port
from app.runtime.log import logger
from app.schemas.transfer import DownloaderTorrent
from app.schemas.types import MUSIC_ENTITY_RECORDING, TorrentQueryStatus, media_type_to_agent
@@ -143,7 +143,7 @@ class QueryDownloadTasksTool(MoviePilotTool):
hashes = [torrent.hash for torrent in torrents if getattr(torrent, "hash", None)]
if not hashes:
return {}
return DownloadHistoryOper().get_by_hashes(hashes)
return get_agent_download_history_port().get_by_hashes(hashes)
@classmethod
def _query_downloads_sync(
+2 -2
View File
@@ -12,7 +12,7 @@ from app.agent.tools.impl._plugin_tool_utils import (
build_preview_payload,
get_plugin_snapshot,
)
from app.application.agentdata import PluginDataPort as PluginDataOper
from app.application.agentdata import get_agent_plugin_data_port
from app.runtime.log import logger
@@ -73,7 +73,7 @@ class QueryPluginDataTool(MoviePilotTool):
ensure_ascii=False,
)
plugin_data_oper = PluginDataOper()
plugin_data_oper = get_agent_plugin_data_port()
if key:
value = await plugin_data_oper.async_get_data(plugin_id, key)
if value is None:
+2 -2
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import SitePort as SiteOper
from app.application.agentdata import get_agent_site_port
from app.runtime.log import logger
SITE_USERDATA_DETAIL_PREVIEW_LIMIT = 10
@@ -62,7 +62,7 @@ class QuerySiteUserdataTool(MoviePilotTool):
)
try:
site_oper = SiteOper()
site_oper = get_agent_site_port()
site = await site_oper.async_get(site_id)
if not site:
return json.dumps(
+2 -2
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import SitePort as SiteOper
from app.application.agentdata import get_agent_site_port
from app.runtime.log import logger
@@ -58,7 +58,7 @@ class QuerySitesTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: status={status}, name={name}")
try:
is_admin = await self.is_admin_user()
site_oper = SiteOper()
site_oper = get_agent_site_port()
# 获取所有站点(按优先级排序)
sites = await site_oper.async_list()
filtered_sites = []
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import SubscribeHistoryPort as SubscribeHistoryOper
from app.application.agentdata import get_agent_subscribe_history_port
from app.runtime.log import logger
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaType, media_type_to_agent
from app.domain.media import normalize_music_type
@@ -106,7 +106,7 @@ class QuerySubscribeHistoryTool(MoviePilotTool):
if MediaType.MUSIC.value not in requested_types:
return "错误:music_type 仅能与 media_type='music''all' 一起使用"
subscribe_history_oper = SubscribeHistoryOper()
subscribe_history_oper = get_agent_subscribe_history_port()
if name:
# 有名称过滤时,获取足够多的记录在内存中过滤,不分页
fetch_count = 500
+2 -2
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import SubscribePort as SubscribeOper
from app.application.agentdata import get_agent_subscribe_port
from app.runtime.log import logger
from app.schemas.subscribe import Subscribe as SubscribeSchema
from app.schemas.types import (
@@ -157,7 +157,7 @@ class QuerySubscribesTool(MoviePilotTool):
if media_type not in ("all", "music"):
return "错误:music_type 仅能与 media_type='music''all' 一起使用"
subscribe_oper = SubscribeOper()
subscribe_oper = get_agent_subscribe_port()
subscribes = await subscribe_oper.async_list()
filtered_subscribes = []
for sub in subscribes:
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import TransferHistoryPort as TransferHistoryOper
from app.application.agentdata import get_agent_transfer_history_port
from app.runtime.log import logger
from app.schemas.types import media_type_to_agent
from app.foundation.text import cut as jieba_cut
@@ -75,7 +75,7 @@ class QueryTransferHistoryTool(MoviePilotTool):
# 每页固定 30 条,与工具说明保持一致,避免整理路径等字段撑大上下文。
count = 30
transferhis = TransferHistoryOper()
transferhis = get_agent_transfer_history_port()
# 处理标题搜索
if title:
# 使用统一分词封装处理标题,便于替换底层实现。
+2 -2
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import WorkflowPort as WorkflowOper
from app.application.agentdata import get_agent_workflow_port
from app.runtime.log import logger
@@ -54,7 +54,7 @@ class QueryWorkflowsTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: state={state}, name={name}, trigger_type={trigger_type}")
try:
workflow_oper = WorkflowOper()
workflow_oper = get_agent_workflow_port()
workflows = await workflow_oper.async_list()
# 过滤工作流
+2 -2
View File
@@ -6,7 +6,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.application.agentdata import AgentTaskPort as AgentTaskOper
from app.application.agentdata import get_agent_task_port
class RunAgentTaskInput(BaseModel):
@@ -42,7 +42,7 @@ class RunAgentTaskTool(MoviePilotTool):
def _get_task_state(self, task_id: int) -> tuple[str, Optional[str]]:
"""校验任务归属和状态,返回可执行性及任务名称。"""
task = AgentTaskOper().get(
task = get_agent_task_port().get(
task_id=task_id,
user_id=str(self._user_id),
)
+2 -2
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.subscribe import SubscribeChain
from app.application.agentdata import SubscribePort as SubscribeOper
from app.application.agentdata import get_agent_subscribe_port
from app.runtime.log import logger
from app.schemas.types import media_type_to_agent
@@ -57,7 +57,7 @@ class SearchSubscribeTool(MoviePilotTool):
try:
# 先验证订阅是否存在
subscribe_oper = SubscribeOper()
subscribe_oper = get_agent_subscribe_port()
subscribe = await subscribe_oper.async_get(subscribe_id)
if not subscribe:
+2 -2
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.site import SiteChain
from app.application.agentdata import SitePort as SiteOper
from app.application.agentdata import get_agent_site_port
from app.runtime.log import logger
@@ -33,7 +33,7 @@ class TestSiteTool(MoviePilotTool):
@staticmethod
def _test_site_sync(site_identifier: int) -> tuple[Optional[str], Optional[str], bool, str]:
"""在同步线程里执行站点联通测试,避免网络请求卡住事件循环。"""
site = SiteOper().get(site_identifier)
site = get_agent_site_port().get(site_identifier)
if not site:
return None, None, False, f"未找到站点:{site_identifier},请使用 query_sites 工具查询可用的站点"
+2 -2
View File
@@ -10,7 +10,7 @@ from app.agent.tools.tags import ToolTag
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.application.agentdata import AgentTaskPort as AgentTaskOper
from app.application.agentdata import get_agent_task_port
from app.runtime.scheduling import TimerUtils
@@ -104,7 +104,7 @@ class UpdateAgentTaskTool(MoviePilotTool):
"""更新当前用户的任务并刷新运行时调度。"""
from app.application.scheduling import update_agent_task_job
oper = AgentTaskOper()
oper = get_agent_task_port()
task = oper.get(task_id=payload.task_id, user_id=str(self._user_id))
if not task:
return None
+2 -2
View File
@@ -8,7 +8,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.runtime.events import eventmanager
from app.application.agentdata import SitePort as SiteOper
from app.application.agentdata import get_agent_site_port
from app.runtime.log import logger
from app.schemas.types import EventType
from app.foundation import url as url_tools
@@ -124,7 +124,7 @@ class UpdateSiteTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: site_id={site_id}")
try:
site_oper = SiteOper()
site_oper = get_agent_site_port()
site = await site_oper.async_get(site_id)
if not site:
return json.dumps(
+2 -2
View File
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.site import SiteChain
from app.application.agentdata import SitePort as SiteOper
from app.application.agentdata import get_agent_site_port
from app.runtime.log import logger
@@ -59,7 +59,7 @@ class UpdateSiteCookieTool(MoviePilotTool):
"""
在同步线程里执行站点登录和 Cookie 更新,避免网络登录阻塞协程。
"""
site = SiteOper().get(site_identifier)
site = get_agent_site_port().get(site_identifier)
if not site:
return None, False, f"未找到站点:{site_identifier},请使用 query_sites 工具查询可用的站点"
+50
View File
@@ -125,3 +125,53 @@ def get_agent_data_ports() -> AgentDataPorts:
if _ports is None:
raise RuntimeError("Agent 数据端口尚未配置")
return _ports
def get_agent_chat_port() -> Any:
"""创建 Agent 会话数据端口实例。"""
return get_agent_data_ports().agent_chat()
def get_agent_task_port() -> Any:
"""创建 Agent 定时任务数据端口实例。"""
return get_agent_data_ports().agent_task()
def get_agent_user_port() -> Any:
"""创建 Agent 用户数据端口实例。"""
return get_agent_data_ports().user()
def get_agent_site_port() -> Any:
"""创建 Agent 站点数据端口实例。"""
return get_agent_data_ports().site()
def get_agent_subscribe_port() -> Any:
"""创建 Agent 订阅数据端口实例。"""
return get_agent_data_ports().subscribe()
def get_agent_subscribe_history_port() -> Any:
"""创建 Agent 订阅历史数据端口实例。"""
return get_agent_data_ports().subscribe_history()
def get_agent_transfer_history_port() -> Any:
"""创建 Agent 整理历史数据端口实例。"""
return get_agent_data_ports().transfer_history()
def get_agent_download_history_port() -> Any:
"""创建 Agent 下载历史数据端口实例。"""
return get_agent_data_ports().download_history()
def get_agent_workflow_port() -> Any:
"""创建 Agent 工作流数据端口实例。"""
return get_agent_data_ports().workflow()
def get_agent_plugin_data_port() -> Any:
"""创建 Agent 插件数据端口实例。"""
return get_agent_data_ports().plugin_data()