From 508bf0bc38590c6386c6becbc98308b4917eef2b Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 22 Aug 2026 10:23:18 +0800 Subject: [PATCH] fix(ci): restore lint and architecture gates --- app/agent/callback/__init__.py | 10 +-- app/agent/orchestrator.py | 4 +- app/api/endpoints/anthropic.py | 31 +++++---- app/api/endpoints/openai.py | 66 ++++++++----------- app/chain/subscribe.py | 48 +++++++------- .../architecture/dependency-baseline.json | 8 ++- tests/test_agent_tool_streaming.py | 2 +- tests/test_telegram.py | 4 +- 8 files changed, 84 insertions(+), 89 deletions(-) diff --git a/app/agent/callback/__init__.py b/app/agent/callback/__init__.py index d72b398f5..f423004fe 100644 --- a/app/agent/callback/__init__.py +++ b/app/agent/callback/__init__.py @@ -5,15 +5,11 @@ from typing import Any, Optional, Tuple from fastapi.concurrency import run_in_threadpool -from app.agent.policy import sanitize_for_host +from app.agent.policy.sanitizer import sanitize_for_host from app.chain import ChainBase from app.runtime.log import logger -from app.schemas.message import Message -from app.schemas.message import ( - MessageResponse, - ChannelCapabilityManager, - ChannelCapability, -) +from app.schemas.message import Message, MessageResponse +from app.schemas.notification import ChannelCapabilityManager, ChannelCapability from app.schemas.types import NotificationChannel, MessageType diff --git a/app/agent/orchestrator.py b/app/agent/orchestrator.py index a52d19ee2..30bb2c41f 100644 --- a/app/agent/orchestrator.py +++ b/app/agent/orchestrator.py @@ -21,7 +21,7 @@ from langgraph.checkpoint.memory import InMemorySaver from app.agent.callback import StreamingHandler from app.agent.contracts import ReplyMode, build_display_message -from app.agent.llm import LLMHelper +from app.agent.llm.helper import LLMHelper from app.agent.llm.server_tools import ServerToolRegistry from app.agent.memory import memory_manager from app.agent.middleware.activity_log import ( @@ -51,7 +51,7 @@ from app.agent.middleware.subagents import ( from app.agent.middleware.tool_selection import ToolSelectorMiddleware from app.agent.middleware.usage import UsageMiddleware from app.agent.prompt import prompt_manager -from app.agent.policy import ( +from app.agent.policy.contracts import ( AuthSource, PrincipalType, ToolOrigin, diff --git a/app/api/endpoints/anthropic.py b/app/api/endpoints/anthropic.py index 43bd1e06e..3897bcc82 100644 --- a/app/api/endpoints/anthropic.py +++ b/app/api/endpoints/anthropic.py @@ -65,6 +65,23 @@ def _check_auth(api_key: Optional[str]) -> Optional[JSONResponse]: return None +def _manager_execution_error(error: BaseException) -> JSONResponse: + """把 AgentManager 稳定错误映射为 Anthropic 兼容错误响应。""" + if _is_manager_unavailable(error): + return _anthropic_error_response( + "MoviePilot AI agent is unavailable.", + 503, + error_type="api_error", + ) + if _is_manager_queue_full(error): + return _anthropic_error_response( + str(error), + 429, + error_type="rate_limit_error", + ) + return _anthropic_error_response(str(error), 500, error_type="api_error") + + async def _stream_anthropic_response( manager, session_id: str, @@ -241,19 +258,7 @@ async def messages( stream_mode=False, ) except Exception as exc: - if _is_manager_unavailable(exc): - return _anthropic_error_response( - "MoviePilot AI agent is unavailable.", - 503, - error_type="api_error", - ) - if _is_manager_queue_full(exc): - return _anthropic_error_response( - str(exc), - 429, - error_type="rate_limit_error", - ) - return _anthropic_error_response(str(exc), 500, error_type="api_error") + return _manager_execution_error(exc) finally: await manager.clear_session(session_id=session_id, user_id=session_id) diff --git a/app/api/endpoints/openai.py b/app/api/endpoints/openai.py index 5368badd9..cc30ab2e9 100644 --- a/app/api/endpoints/openai.py +++ b/app/api/endpoints/openai.py @@ -351,6 +351,30 @@ def _is_manager_queue_full(error: BaseException) -> bool: return getattr(error, "code", None) == "agent_manager_queue_full" +def _manager_execution_error(error: BaseException) -> JSONResponse: + """把 AgentManager 稳定错误映射为 OpenAI 兼容错误响应。""" + if _is_manager_unavailable(error): + return _error_response( + "MoviePilot AI agent is unavailable.", + 503, + error_type="server_error", + code="ai_agent_unavailable", + ) + if _is_manager_queue_full(error): + return _error_response( + str(error), + 429, + error_type="rate_limit_error", + code="ai_agent_queue_full", + ) + return _error_response( + str(error), + 500, + error_type="server_error", + code="agent_execution_failed", + ) + + async def _run_managed_agent( *, manager, @@ -550,26 +574,7 @@ async def chat_completions( stream_mode=False, ) except Exception as exc: - if _is_manager_unavailable(exc): - return _error_response( - "MoviePilot AI agent is unavailable.", - 503, - error_type="server_error", - code="ai_agent_unavailable", - ) - if _is_manager_queue_full(exc): - return _error_response( - str(exc), - 429, - error_type="rate_limit_error", - code="ai_agent_queue_full", - ) - return _error_response( - str(exc), - 500, - error_type="server_error", - code="agent_execution_failed", - ) + return _manager_execution_error(exc) finally: if not use_server_session: await manager.clear_session(session_id=session_id, user_id=session_key) @@ -657,26 +662,7 @@ async def responses( stream_mode=False, ) except Exception as exc: - if _is_manager_unavailable(exc): - return _error_response( - "MoviePilot AI agent is unavailable.", - 503, - error_type="server_error", - code="ai_agent_unavailable", - ) - if _is_manager_queue_full(exc): - return _error_response( - str(exc), - 429, - error_type="rate_limit_error", - code="ai_agent_queue_full", - ) - return _error_response( - str(exc), - 500, - error_type="server_error", - code="agent_execution_failed", - ) + return _manager_execution_error(exc) finally: if not payload.user: await manager.clear_session(session_id=session_id, user_id=session_key) diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index da41f1bf9..906be8219 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -1353,6 +1353,28 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase): """ return cls._subscription_query().exists(mediainfo, meta) + def _acquire_run_lock( + self, + operation: str, + progress_callback: Optional[Callable[..., None]], + ) -> bool: + """获取订阅任务锁,超时时统一记录并结束本轮进度。""" + if self._rlock.acquire(blocking=True, timeout=self._LOCK_TIMOUT): + logger.debug(f"{operation} lock acquired at {datetime.now()}") + return True + operation_label = {"search": "搜索", "match": "匹配"}[operation] + progress_text = { + "search": "订阅搜索锁等待超时,已跳过本轮", + "match": "订阅匹配锁等待超时,已跳过本轮", + }[operation] + logger.error(f"订阅{operation_label}锁等待超时,已中止本轮执行") + if progress_callback: + progress_callback( + value=100, + text=progress_text, + ) + return False + def search( self, sid: Optional[int] = None, @@ -1370,17 +1392,8 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase): """ lock_acquired = False try: - if lock_acquired := self._rlock.acquire( - blocking=True, timeout=self._LOCK_TIMOUT - ): - logger.debug(f"search lock acquired at {datetime.now()}") - else: - logger.error("订阅搜索锁等待超时,已中止本轮执行") - if progress_callback: - progress_callback( - value=100, - text="订阅搜索锁等待超时,已跳过本轮", - ) + lock_acquired = self._acquire_run_lock("search", progress_callback) + if not lock_acquired: return subscribeoper = SubscribeOper() @@ -1820,17 +1833,8 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase): lock_acquired = False try: - if lock_acquired := self._rlock.acquire( - blocking=True, timeout=self._LOCK_TIMOUT - ): - logger.debug(f"match lock acquired at {datetime.now()}") - else: - logger.error("订阅匹配锁等待超时,已中止本轮执行") - if progress_callback: - progress_callback( - value=100, - text="订阅匹配锁等待超时,已跳过本轮", - ) + lock_acquired = self._acquire_run_lock("match", progress_callback) + if not lock_acquired: return # 预识别所有未识别的种子 diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index 77f0c5ead..486645837 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": 6313, - "edge_sha256": "25292ce04e33206f713eb24cff2397931124fcb22f55ab0d78f94314581161d4", + "edge_count": 6317, + "edge_sha256": "11978842b2fd9bd6ccec86c01c5f1b9389d959bc53aab5e16e29e3c121b3dab9", "edges": [ "app -> app.runtime", "app -> app.runtime.compat", @@ -169,11 +169,13 @@ "app.adapters.web.security.access -> app.schemas.token", "app.agent.callback -> app.agent", "app.agent.callback -> app.agent.policy", + "app.agent.callback -> app.agent.policy.sanitizer", "app.agent.callback -> app.chain", "app.agent.callback -> app.runtime", "app.agent.callback -> app.runtime.log", "app.agent.callback -> app.schemas", "app.agent.callback -> app.schemas.message", + "app.agent.callback -> app.schemas.notification", "app.agent.callback -> app.schemas.types", "app.agent.capabilities.adapter -> app.agent", "app.agent.capabilities.adapter -> app.agent.capabilities", @@ -303,6 +305,7 @@ "app.agent.orchestrator -> app.agent.callback", "app.agent.orchestrator -> app.agent.contracts", "app.agent.orchestrator -> app.agent.llm", + "app.agent.orchestrator -> app.agent.llm.helper", "app.agent.orchestrator -> app.agent.llm.server_tools", "app.agent.orchestrator -> app.agent.mcp", "app.agent.orchestrator -> app.agent.memory", @@ -319,6 +322,7 @@ "app.agent.orchestrator -> app.agent.middleware.tool_selection", "app.agent.orchestrator -> app.agent.middleware.usage", "app.agent.orchestrator -> app.agent.policy", + "app.agent.orchestrator -> app.agent.policy.contracts", "app.agent.orchestrator -> app.agent.prompt", "app.agent.orchestrator -> app.agent.runtime", "app.agent.orchestrator -> app.agent.runtime_loader", diff --git a/tests/test_agent_tool_streaming.py b/tests/test_agent_tool_streaming.py index e462109f1..74ab7d136 100644 --- a/tests/test_agent_tool_streaming.py +++ b/tests/test_agent_tool_streaming.py @@ -7,7 +7,7 @@ import langchain.agents as langchain_agents if not hasattr(langchain_agents, "create_agent"): langchain_agents.create_agent = lambda *args, **kwargs: None -from app.agent import _ThinkTagStripper +from app.agent.orchestrator import _ThinkTagStripper from app.agent.callback import StreamingHandler from app.agent.middleware.subagents import is_subagent_stream_metadata from app.agent.tools.base import MoviePilotTool diff --git a/tests/test_telegram.py b/tests/test_telegram.py index 318617688..274db937a 100644 --- a/tests/test_telegram.py +++ b/tests/test_telegram.py @@ -11,9 +11,9 @@ import pytest from app.domain.context import MediaInfo, Context, TorrentInfo from app.domain.metainfo import MetaInfo -from app.modules.telegram import TelegramModule +from app.modules.telegram.module import TelegramModule from app.modules.telegram.telegram import Telegram -from app.schemas import Message +from app.schemas.message import Message from app.schemas.types import NotificationChannel from app.schemas.types import MediaType