fix(ci): restore lint and architecture gates

This commit is contained in:
jxxghp
2026-08-22 10:23:18 +08:00
parent 9273d68aa7
commit 508bf0bc38
8 changed files with 84 additions and 89 deletions
+3 -7
View File
@@ -5,15 +5,11 @@ from typing import Any, Optional, Tuple
from fastapi.concurrency import run_in_threadpool 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.chain import ChainBase
from app.runtime.log import logger from app.runtime.log import logger
from app.schemas.message import Message from app.schemas.message import Message, MessageResponse
from app.schemas.message import ( from app.schemas.notification import ChannelCapabilityManager, ChannelCapability
MessageResponse,
ChannelCapabilityManager,
ChannelCapability,
)
from app.schemas.types import NotificationChannel, MessageType from app.schemas.types import NotificationChannel, MessageType
+2 -2
View File
@@ -21,7 +21,7 @@ from langgraph.checkpoint.memory import InMemorySaver
from app.agent.callback import StreamingHandler from app.agent.callback import StreamingHandler
from app.agent.contracts import ReplyMode, build_display_message 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.llm.server_tools import ServerToolRegistry
from app.agent.memory import memory_manager from app.agent.memory import memory_manager
from app.agent.middleware.activity_log import ( 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.tool_selection import ToolSelectorMiddleware
from app.agent.middleware.usage import UsageMiddleware from app.agent.middleware.usage import UsageMiddleware
from app.agent.prompt import prompt_manager from app.agent.prompt import prompt_manager
from app.agent.policy import ( from app.agent.policy.contracts import (
AuthSource, AuthSource,
PrincipalType, PrincipalType,
ToolOrigin, ToolOrigin,
+18 -13
View File
@@ -65,6 +65,23 @@ def _check_auth(api_key: Optional[str]) -> Optional[JSONResponse]:
return None 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( async def _stream_anthropic_response(
manager, manager,
session_id: str, session_id: str,
@@ -241,19 +258,7 @@ async def messages(
stream_mode=False, stream_mode=False,
) )
except Exception as exc: except Exception as exc:
if _is_manager_unavailable(exc): return _manager_execution_error(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")
finally: finally:
await manager.clear_session(session_id=session_id, user_id=session_id) await manager.clear_session(session_id=session_id, user_id=session_id)
+26 -40
View File
@@ -351,6 +351,30 @@ def _is_manager_queue_full(error: BaseException) -> bool:
return getattr(error, "code", None) == "agent_manager_queue_full" 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( async def _run_managed_agent(
*, *,
manager, manager,
@@ -550,26 +574,7 @@ async def chat_completions(
stream_mode=False, stream_mode=False,
) )
except Exception as exc: except Exception as exc:
if _is_manager_unavailable(exc): return _manager_execution_error(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",
)
finally: finally:
if not use_server_session: if not use_server_session:
await manager.clear_session(session_id=session_id, user_id=session_key) await manager.clear_session(session_id=session_id, user_id=session_key)
@@ -657,26 +662,7 @@ async def responses(
stream_mode=False, stream_mode=False,
) )
except Exception as exc: except Exception as exc:
if _is_manager_unavailable(exc): return _manager_execution_error(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",
)
finally: finally:
if not payload.user: if not payload.user:
await manager.clear_session(session_id=session_id, user_id=session_key) await manager.clear_session(session_id=session_id, user_id=session_key)
+26 -22
View File
@@ -1353,6 +1353,28 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
""" """
return cls._subscription_query().exists(mediainfo, meta) 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( def search(
self, self,
sid: Optional[int] = None, sid: Optional[int] = None,
@@ -1370,17 +1392,8 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
""" """
lock_acquired = False lock_acquired = False
try: try:
if lock_acquired := self._rlock.acquire( lock_acquired = self._acquire_run_lock("search", progress_callback)
blocking=True, timeout=self._LOCK_TIMOUT if not lock_acquired:
):
logger.debug(f"search lock acquired at {datetime.now()}")
else:
logger.error("订阅搜索锁等待超时,已中止本轮执行")
if progress_callback:
progress_callback(
value=100,
text="订阅搜索锁等待超时,已跳过本轮",
)
return return
subscribeoper = SubscribeOper() subscribeoper = SubscribeOper()
@@ -1820,17 +1833,8 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
lock_acquired = False lock_acquired = False
try: try:
if lock_acquired := self._rlock.acquire( lock_acquired = self._acquire_run_lock("match", progress_callback)
blocking=True, timeout=self._LOCK_TIMOUT if not lock_acquired:
):
logger.debug(f"match lock acquired at {datetime.now()}")
else:
logger.error("订阅匹配锁等待超时,已中止本轮执行")
if progress_callback:
progress_callback(
value=100,
text="订阅匹配锁等待超时,已跳过本轮",
)
return return
# 预识别所有未识别的种子 # 预识别所有未识别的种子
+6 -2
View File
@@ -13,8 +13,8 @@
"runtime_to_db": [], "runtime_to_db": [],
"workflow_to_db": [] "workflow_to_db": []
}, },
"edge_count": 6313, "edge_count": 6317,
"edge_sha256": "25292ce04e33206f713eb24cff2397931124fcb22f55ab0d78f94314581161d4", "edge_sha256": "11978842b2fd9bd6ccec86c01c5f1b9389d959bc53aab5e16e29e3c121b3dab9",
"edges": [ "edges": [
"app -> app.runtime", "app -> app.runtime",
"app -> app.runtime.compat", "app -> app.runtime.compat",
@@ -169,11 +169,13 @@
"app.adapters.web.security.access -> app.schemas.token", "app.adapters.web.security.access -> app.schemas.token",
"app.agent.callback -> app.agent", "app.agent.callback -> app.agent",
"app.agent.callback -> app.agent.policy", "app.agent.callback -> app.agent.policy",
"app.agent.callback -> app.agent.policy.sanitizer",
"app.agent.callback -> app.chain", "app.agent.callback -> app.chain",
"app.agent.callback -> app.runtime", "app.agent.callback -> app.runtime",
"app.agent.callback -> app.runtime.log", "app.agent.callback -> app.runtime.log",
"app.agent.callback -> app.schemas", "app.agent.callback -> app.schemas",
"app.agent.callback -> app.schemas.message", "app.agent.callback -> app.schemas.message",
"app.agent.callback -> app.schemas.notification",
"app.agent.callback -> app.schemas.types", "app.agent.callback -> app.schemas.types",
"app.agent.capabilities.adapter -> app.agent", "app.agent.capabilities.adapter -> app.agent",
"app.agent.capabilities.adapter -> app.agent.capabilities", "app.agent.capabilities.adapter -> app.agent.capabilities",
@@ -303,6 +305,7 @@
"app.agent.orchestrator -> app.agent.callback", "app.agent.orchestrator -> app.agent.callback",
"app.agent.orchestrator -> app.agent.contracts", "app.agent.orchestrator -> app.agent.contracts",
"app.agent.orchestrator -> app.agent.llm", "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.llm.server_tools",
"app.agent.orchestrator -> app.agent.mcp", "app.agent.orchestrator -> app.agent.mcp",
"app.agent.orchestrator -> app.agent.memory", "app.agent.orchestrator -> app.agent.memory",
@@ -319,6 +322,7 @@
"app.agent.orchestrator -> app.agent.middleware.tool_selection", "app.agent.orchestrator -> app.agent.middleware.tool_selection",
"app.agent.orchestrator -> app.agent.middleware.usage", "app.agent.orchestrator -> app.agent.middleware.usage",
"app.agent.orchestrator -> app.agent.policy", "app.agent.orchestrator -> app.agent.policy",
"app.agent.orchestrator -> app.agent.policy.contracts",
"app.agent.orchestrator -> app.agent.prompt", "app.agent.orchestrator -> app.agent.prompt",
"app.agent.orchestrator -> app.agent.runtime", "app.agent.orchestrator -> app.agent.runtime",
"app.agent.orchestrator -> app.agent.runtime_loader", "app.agent.orchestrator -> app.agent.runtime_loader",
+1 -1
View File
@@ -7,7 +7,7 @@ import langchain.agents as langchain_agents
if not hasattr(langchain_agents, "create_agent"): if not hasattr(langchain_agents, "create_agent"):
langchain_agents.create_agent = lambda *args, **kwargs: None 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.callback import StreamingHandler
from app.agent.middleware.subagents import is_subagent_stream_metadata from app.agent.middleware.subagents import is_subagent_stream_metadata
from app.agent.tools.base import MoviePilotTool from app.agent.tools.base import MoviePilotTool
+2 -2
View File
@@ -11,9 +11,9 @@ import pytest
from app.domain.context import MediaInfo, Context, TorrentInfo from app.domain.context import MediaInfo, Context, TorrentInfo
from app.domain.metainfo import MetaInfo 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.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 NotificationChannel
from app.schemas.types import MediaType from app.schemas.types import MediaType