mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-31 13:07:56 +08:00
- ChainBase 拆分为 RecognitionMixin/MessageProcessingMixin/NotificationMixin - TransferChain 拆分为 7 个功能 mixin(_mixins.py),SubscribeChain 音乐订阅域拆出 _music.py - 斜杠命令交互四件套收敛为 InteractionChainMixin 委托,会话管理器移至 application 层,chain 层不再 re-export - 模块基础类收敛到 app/modules/_base(notification/mediaserver 语义重命名) - 清理 app/chain/__init__.py 24 个未使用导入,修正 49 处测试 patch 目标到实际命名空间 - 兼容层 legacy 符号不再并入 __all__,根治 schemas 初始化反向拉起 application.transfer 的循环导入 - 修复 bangumi 集数为字符串时 set_bangumi_info 抛 TypeError - 新增重复代码等架构门禁测试;capability 清单校验排除下划线内部目录
117 lines
4.4 KiB
Python
117 lines
4.4 KiB
Python
# 把真实 Agent 服务注册进 application 门面(幂等),供测试 patch 门面背后的单例方法。
|
|
from app.agent.llm import AgentCapabilityManager, LLMHelper
|
|
from app.agent.orchestrator import agent_manager
|
|
from app.agent.prompt import prompt_manager
|
|
from app.agent.prompt.transfer_redo import build_manual_redo_prompt
|
|
from app.application.agent import register_agent_services
|
|
|
|
register_agent_services(
|
|
agent_manager=agent_manager,
|
|
prompt_manager=prompt_manager,
|
|
capability_manager=AgentCapabilityManager,
|
|
llm_helper=LLMHelper,
|
|
manual_redo_prompt_builder=build_manual_redo_prompt,
|
|
)
|
|
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
from app.agent import MoviePilotAgent
|
|
from app.agent.llm import AgentCapabilityManager, LLMHelper
|
|
from app.chain.message import MessageChain
|
|
from app.runtime.config import settings
|
|
from app.schemas.types import MessageChannel
|
|
|
|
|
|
def test_llm_supports_image_input_uses_model_catalog_text_only(monkeypatch):
|
|
"""内置目录明确为纯文本模型时,应自动关闭图片输入。"""
|
|
monkeypatch.setattr(settings, "LLM_SUPPORT_IMAGE_INPUT", True)
|
|
|
|
assert not LLMHelper.supports_image_input(
|
|
provider="minimax",
|
|
model="MiniMax-M2.7",
|
|
)
|
|
|
|
|
|
def test_llm_supports_image_input_keeps_known_vision_model(monkeypatch):
|
|
"""内置目录明确为视觉模型时,应允许图片输入。"""
|
|
monkeypatch.setattr(settings, "LLM_SUPPORT_IMAGE_INPUT", True)
|
|
|
|
assert LLMHelper.supports_image_input(
|
|
provider="zhipuai",
|
|
model="glm-5v-turbo",
|
|
)
|
|
|
|
|
|
def test_llm_supports_image_input_keeps_unknown_model_override(monkeypatch):
|
|
"""未知自定义模型保持用户开关语义,避免误伤私有视觉模型。"""
|
|
monkeypatch.setattr(settings, "LLM_SUPPORT_IMAGE_INPUT", True)
|
|
|
|
assert LLMHelper.supports_image_input(
|
|
provider="custom-provider",
|
|
model="custom-vlm-model",
|
|
)
|
|
|
|
|
|
def test_agent_capability_manager_delegates_image_support():
|
|
"""Agent 能力管理器应复用统一的模型图片能力判断。"""
|
|
with patch.object(LLMHelper, "supports_image_input", return_value=False) as supports:
|
|
assert not AgentCapabilityManager.supports_image_input()
|
|
|
|
supports.assert_called_once_with()
|
|
|
|
|
|
def test_handle_ai_message_routes_text_only_model_images_to_files(monkeypatch):
|
|
"""纯文本模型收到图片消息时,应降级为文件附件而非 image_url 内容块。"""
|
|
chain = MessageChain()
|
|
monkeypatch.setattr(settings, "AI_AGENT_ENABLE", True)
|
|
monkeypatch.setattr(settings, "LLM_SUPPORT_IMAGE_INPUT", True)
|
|
monkeypatch.setattr(settings, "LLM_PROVIDER", "minimax")
|
|
monkeypatch.setattr(settings, "LLM_MODEL", "MiniMax-M2.7")
|
|
|
|
with patch.object(
|
|
chain, "_get_or_create_session_id", return_value="session-1"
|
|
), patch.object(
|
|
chain, "_download_attachments_to_data_urls"
|
|
) as download_images, patch.object(
|
|
chain,
|
|
"_prepare_agent_files",
|
|
return_value=[
|
|
{
|
|
"name": "image_1.jpg",
|
|
"mime_type": "image/jpeg",
|
|
"local_path": "/tmp/image_1.jpg",
|
|
"status": "ready",
|
|
}
|
|
],
|
|
) as prepare_files, patch(
|
|
"app.application.agent._agent_manager.process_message", new_callable=AsyncMock
|
|
) as process_message, patch(
|
|
"app.chain.message.asyncio.run_coroutine_threadsafe",
|
|
side_effect=lambda coro, _loop: coro.close(),
|
|
):
|
|
chain._handle_ai_message(
|
|
text="/ai 帮我看看这张图",
|
|
channel=MessageChannel.Telegram,
|
|
source="telegram-test",
|
|
userid="10001",
|
|
username="tester",
|
|
images=["tg://file_id/image-1"],
|
|
)
|
|
|
|
download_images.assert_not_called()
|
|
prepare_files.assert_called_once()
|
|
assert prepare_files.call_args.kwargs["files"][0].ref == "tg://file_id/image-1"
|
|
assert process_message.call_args.kwargs["images"] is None
|
|
assert process_message.call_args.kwargs["files"][0]["local_path"] == "/tmp/image_1.jpg"
|
|
|
|
|
|
def test_unsupported_image_error_recognizes_vlm_text_only_message():
|
|
"""兼容端点返回 not a VLM 时,应识别为图片输入能力错误。"""
|
|
error = Exception(
|
|
"Error code: 400 - {'code': 20041, 'message': "
|
|
"'The model is not a VLM (Vision Language Model). "
|
|
"Please use text-only prompts.'}"
|
|
)
|
|
|
|
assert MoviePilotAgent._is_unsupported_image_input_error(error)
|