diff --git a/app/adapters/external/market.py b/app/adapters/external/market.py index ecf98d20e..23cb67251 100644 --- a/app/adapters/external/market.py +++ b/app/adapters/external/market.py @@ -2467,14 +2467,15 @@ class PluginHelper(metaclass=WeakSingleton): """异步恢复主程序运行依赖,避免修复命令绕过可取消进程边界。""" repair_target = snapshot_file repair_desc = "主程序依赖快照" - if repair_target and not repair_target.exists(): + if repair_target and not await _await_thread_operation(repair_target.exists): repair_target = None if repair_target is None: repair_target = settings.ROOT_PATH / "pyproject.toml" repair_desc = "主程序 uv.lock" - if not repair_target.exists(): + if not await _await_thread_operation(repair_target.exists): return False, f"恢复依赖文件不存在:{repair_target}" - if snapshot_file is None and not (settings.ROOT_PATH / "uv.lock").exists(): + lock_file = settings.ROOT_PATH / "uv.lock" + if snapshot_file is None and not await _await_thread_operation(lock_file.exists): return False, f"恢复依赖文件不存在:{settings.ROOT_PATH / 'uv.lock'}" request = cls.__build_package_install_request( @@ -2568,7 +2569,7 @@ class PluginHelper(metaclass=WeakSingleton): candidate_dirs = [] for dependency_file in resolved_dependency_files: wheels_dir = dependency_file.parent / "wheels" - if wheels_dir.is_dir(): + if await _await_thread_operation(wheels_dir.is_dir): candidate_dirs.append(wheels_dir) if find_links_dirs: candidate_dirs.extend(find_links_dirs) @@ -2577,9 +2578,11 @@ class PluginHelper(metaclass=WeakSingleton): seen_dirs = set() for candidate_dir in candidate_dirs: candidate_path = Path(candidate_dir) - if not candidate_path.is_dir(): + if not await _await_thread_operation(candidate_path.is_dir): continue - candidate_key = str(candidate_path.resolve()) + candidate_key = str( + await _await_thread_operation(candidate_path.resolve) + ) if candidate_key in seen_dirs: continue seen_dirs.add(candidate_key) @@ -2707,7 +2710,10 @@ class PluginHelper(metaclass=WeakSingleton): if acquired: cls._package_install_lock.release() if constraints_file: - constraints_file.unlink(missing_ok=True) + await _await_thread_operation( + constraints_file.unlink, + missing_ok=True, + ) async def __async_backup_plugin(self, pid: str) -> str: """ diff --git a/app/api/endpoints/plugin.py b/app/api/endpoints/plugin.py index 951d23455..091de1f50 100644 --- a/app/api/endpoints/plugin.py +++ b/app/api/endpoints/plugin.py @@ -57,7 +57,7 @@ from app.api.dependencies.plugin import ( from app.adapters.external.server import MoviePilotServerHelper from app.adapters.external.market import PluginHelper from app.adapters.system.plugin.package import PluginPackageManager -from app.application.database import DatabaseWorkerOverloadedError +from app.schemas.exception import DatabaseWorkerOverloadedError from app.runtime.log import logger from app.schemas.types import SystemConfigKey diff --git a/app/application/database.py b/app/application/database.py index 78e6dafa5..d69414d27 100644 --- a/app/application/database.py +++ b/app/application/database.py @@ -18,14 +18,6 @@ DatabaseProbe = Callable[[], Optional[str]] T = TypeVar("T") -class DatabaseWorkerClosedError(RuntimeError): - """数据库执行器尚未启动或已经停止。""" - - -class DatabaseWorkerOverloadedError(RuntimeError): - """数据库执行器的运行与排队容量已经用尽。""" - - class AsyncDatabaseExecutor(Protocol): """让异步业务调用同步短事务而不阻塞事件循环。""" diff --git a/app/application/messaging/chat.py b/app/application/messaging/chat.py index bdb831348..5a8b3cb22 100644 --- a/app/application/messaging/chat.py +++ b/app/application/messaging/chat.py @@ -10,10 +10,12 @@ from weakref import WeakValueDictionary from app.application.database import ( AsyncDatabaseExecutor, +) +from app.schemas.agent import AgentChatSessionDetail, AgentChatSessionSummary +from app.schemas.exception import ( DatabaseWorkerClosedError, DatabaseWorkerOverloadedError, ) -from app.schemas.agent import AgentChatSessionDetail, AgentChatSessionSummary from app.runtime.observability import record_metric diff --git a/app/application/plugin/install.py b/app/application/plugin/install.py index c33f40bd3..f858d2b9e 100644 --- a/app/application/plugin/install.py +++ b/app/application/plugin/install.py @@ -7,7 +7,7 @@ from collections.abc import Awaitable, Callable from dataclasses import dataclass, field from typing import Any, Optional -from app.application.database import DatabaseWorkerOverloadedError +from app.schemas.exception import DatabaseWorkerOverloadedError from app.application.plugin.lifecycle import plugin_lifecycle from app.runtime.log import logger diff --git a/app/db/worker.py b/app/db/worker.py index 1690df663..122366f42 100644 --- a/app/db/worker.py +++ b/app/db/worker.py @@ -10,7 +10,7 @@ from contextvars import copy_context from dataclasses import dataclass from typing import Callable, TypeVar -from app.application.database import ( +from app.schemas.exception import ( DatabaseWorkerClosedError, DatabaseWorkerOverloadedError, ) diff --git a/app/factory.py b/app/factory.py index 7db6a5e82..12a06b8bd 100644 --- a/app/factory.py +++ b/app/factory.py @@ -14,7 +14,7 @@ from app.adapters.observability.otel import build_observation_port from app.adapters.web.plugin.routes import FastAPIDynamicRouteRegistry from app.adapters.web.health import install_health_routes from app.application.plugin.routes import configure_plugin_routes -from app.application.database import ( +from app.schemas.exception import ( DatabaseWorkerClosedError, DatabaseWorkerOverloadedError, ) diff --git a/app/schemas/exception.py b/app/schemas/exception.py index f51b5759e..066109517 100644 --- a/app/schemas/exception.py +++ b/app/schemas/exception.py @@ -47,6 +47,14 @@ class StorageQueryError(Exception): pass +class DatabaseWorkerClosedError(RuntimeError): + """数据库执行器尚未启动或已经停止。""" + + +class DatabaseWorkerOverloadedError(RuntimeError): + """数据库执行器的运行与排队容量已经用尽。""" + + class TMDbException(Exception): """ 用于表示TheMovieDB数据源请求失败的跨层异常契约。 diff --git a/app/schemas/exports.py b/app/schemas/exports.py index 25e8d04f1..946467c21 100644 --- a/app/schemas/exports.py +++ b/app/schemas/exports.py @@ -81,6 +81,8 @@ SCHEMA_EXPORTS = { 'DashboardMemoryInfo': ('app.schemas.dashboard', 'DashboardMemoryInfo'), 'DashboardSystemInfo': ('app.schemas.dashboard', 'DashboardSystemInfo'), 'DataT': ('app.schemas.response', 'DataT'), + 'DatabaseWorkerClosedError': ('app.schemas.exception', 'DatabaseWorkerClosedError'), + 'DatabaseWorkerOverloadedError': ('app.schemas.exception', 'DatabaseWorkerOverloadedError'), 'Dict': ('app.schemas.mcp', 'Dict'), 'DiscoverMediaSource': ('app.schemas.event', 'DiscoverMediaSource'), 'DiscoverSourceEventData': ('app.schemas.event', 'DiscoverSourceEventData'), diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index abf4f749c..9ea776895 100644 --- a/tests/fixtures/architecture/dependency-baseline.json +++ b/tests/fixtures/architecture/dependency-baseline.json @@ -13,8 +13,8 @@ "runtime_to_db": [], "workflow_to_db": [] }, - "edge_count": 6440, - "edge_sha256": "93995005b1d5d1da95e9ec73a39c1d92e8e8e3e3ce3751ce1035e3f31103350d", + "edge_count": 6451, + "edge_sha256": "9075d8717e384580cd6a41bc36685438770db4a8d8f18c57e6c494f32937113a", "edges": [ "app -> app.runtime", "app -> app.runtime.compat", @@ -237,6 +237,8 @@ "app.agent.mcp -> app.schemas.types", "app.agent.memory -> app.application", "app.agent.memory -> app.application.agentdata", + "app.agent.memory -> app.application.messaging", + "app.agent.memory -> app.application.messaging.chat", "app.agent.memory -> app.runtime", "app.agent.memory -> app.runtime.log", "app.agent.memory -> app.runtime.settings", @@ -345,6 +347,8 @@ "app.agent.orchestrator -> app.agent.tools.impl.query_system_settings", "app.agent.orchestrator -> app.application", "app.agent.orchestrator -> app.application.agentdata", + "app.agent.orchestrator -> app.application.messaging", + "app.agent.orchestrator -> app.application.messaging.chat", "app.agent.orchestrator -> app.application.plugin", "app.agent.orchestrator -> app.application.plugin.runtime", "app.agent.orchestrator -> app.chain", @@ -2052,7 +2056,6 @@ "app.api.endpoints.plugin -> app.application", "app.api.endpoints.plugin -> app.application.commands", "app.api.endpoints.plugin -> app.application.configuration", - "app.api.endpoints.plugin -> app.application.database", "app.api.endpoints.plugin -> app.application.plugin", "app.api.endpoints.plugin -> app.application.plugin.config", "app.api.endpoints.plugin -> app.application.plugin.folders", @@ -2068,6 +2071,7 @@ "app.api.endpoints.plugin -> app.runtime.log", "app.api.endpoints.plugin -> app.schemas", "app.api.endpoints.plugin -> app.schemas.common", + "app.api.endpoints.plugin -> app.schemas.exception", "app.api.endpoints.plugin -> app.schemas.plugin", "app.api.endpoints.plugin -> app.schemas.response", "app.api.endpoints.plugin -> app.schemas.token", @@ -2553,8 +2557,13 @@ "app.application.mediaserver -> app.schemas.types", "app.application.messaging.agent -> app.schemas", "app.application.messaging.agent -> app.schemas.types", + "app.application.messaging.chat -> app.application", + "app.application.messaging.chat -> app.application.database", + "app.application.messaging.chat -> app.runtime", + "app.application.messaging.chat -> app.runtime.observability", "app.application.messaging.chat -> app.schemas", "app.application.messaging.chat -> app.schemas.agent", + "app.application.messaging.chat -> app.schemas.exception", "app.application.messaging.interaction -> app.schemas", "app.application.messaging.interaction -> app.schemas.message", "app.application.messaging.interaction -> app.schemas.notification", @@ -2641,11 +2650,12 @@ "app.application.plugin.folders -> app.schemas", "app.application.plugin.folders -> app.schemas.types", "app.application.plugin.install -> app.application", - "app.application.plugin.install -> app.application.database", "app.application.plugin.install -> app.application.plugin", "app.application.plugin.install -> app.application.plugin.lifecycle", "app.application.plugin.install -> app.runtime", "app.application.plugin.install -> app.runtime.log", + "app.application.plugin.install -> app.schemas", + "app.application.plugin.install -> app.schemas.exception", "app.application.recognition -> app.application", "app.application.recognition -> app.application.configuration", "app.application.recognition -> app.schemas", @@ -3675,10 +3685,10 @@ "app.db.session -> app.runtime.config", "app.db.session -> app.runtime.log", "app.db.session -> app.runtime.observability", - "app.db.worker -> app.application", - "app.db.worker -> app.application.database", "app.db.worker -> app.runtime", "app.db.worker -> app.runtime.observability", + "app.db.worker -> app.schemas", + "app.db.worker -> app.schemas.exception", "app.doctor.checks -> app.adapters", "app.doctor.checks -> app.adapters.system", "app.doctor.checks -> app.adapters.system.backup", @@ -3802,7 +3812,6 @@ "app.factory -> app.api", "app.factory -> app.api.response", "app.factory -> app.application", - "app.factory -> app.application.database", "app.factory -> app.application.plugin", "app.factory -> app.application.plugin.routes", "app.factory -> app.application.security", @@ -3816,6 +3825,7 @@ "app.factory -> app.runtime.observability", "app.factory -> app.runtime.settings", "app.factory -> app.schemas", + "app.factory -> app.schemas.exception", "app.factory -> app.schemas.mcp", "app.factory -> app.schemas.openai", "app.factory -> app.schemas.response", @@ -6094,6 +6104,7 @@ "app.startup.modules_initializer -> app.application.history", "app.startup.modules_initializer -> app.application.image", "app.startup.modules_initializer -> app.application.messaging", + "app.startup.modules_initializer -> app.application.messaging.agent", "app.startup.modules_initializer -> app.application.messaging.chat", "app.startup.modules_initializer -> app.application.messaging.message", "app.startup.modules_initializer -> app.application.module", diff --git a/tests/test_agent_chat_persistence.py b/tests/test_agent_chat_persistence.py index 513a21172..bee1bd917 100644 --- a/tests/test_agent_chat_persistence.py +++ b/tests/test_agent_chat_persistence.py @@ -11,7 +11,7 @@ from uuid import uuid4 import pytest from sqlalchemy import delete, select -from app.application.database import ( +from app.schemas.exception import ( DatabaseWorkerClosedError, DatabaseWorkerOverloadedError, ) diff --git a/tests/test_api_response.py b/tests/test_api_response.py index 78a3ee335..715aadf9e 100644 --- a/tests/test_api_response.py +++ b/tests/test_api_response.py @@ -23,7 +23,7 @@ from app.factory import ( localized_unhandled_exception_handler, localized_validation_exception_handler, ) -from app.application.database import ( +from app.schemas.exception import ( DatabaseWorkerClosedError, DatabaseWorkerOverloadedError, ) diff --git a/tests/test_database_worker.py b/tests/test_database_worker.py index 156862610..4e755980c 100644 --- a/tests/test_database_worker.py +++ b/tests/test_database_worker.py @@ -6,8 +6,8 @@ from unittest.mock import patch import pytest -from app.db.worker import ( - DatabaseWorker, +from app.db.worker import DatabaseWorker +from app.schemas.exception import ( DatabaseWorkerClosedError, DatabaseWorkerOverloadedError, ) diff --git a/tests/test_plugin_install_command.py b/tests/test_plugin_install_command.py index 7830ffdcb..d1736662a 100644 --- a/tests/test_plugin_install_command.py +++ b/tests/test_plugin_install_command.py @@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, Mock import pytest -from app.application.database import DatabaseWorkerOverloadedError +from app.schemas.exception import DatabaseWorkerOverloadedError from app.application.plugin.install import PluginInstallCommand