mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
fix(ci): restore lint and architecture gates
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user