mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-28 11:37:23 +08:00
feat: implement file upload and callback handling for Web Agent
This commit is contained in:
@@ -25,11 +25,15 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
telegram_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.Telegram.value
|
||||
)
|
||||
web_agent_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.WebAgent.value
|
||||
)
|
||||
wechat_prompt = prompt_manager.get_agent_prompt(
|
||||
channel=MessageChannel.Wechat.value
|
||||
)
|
||||
|
||||
self.assertIn("ask_user_choice", telegram_prompt)
|
||||
self.assertIn("ask_user_choice", web_agent_prompt)
|
||||
self.assertIn("terminal interaction tool", telegram_prompt)
|
||||
self.assertIn("do not write a final text reply after it", telegram_prompt)
|
||||
self.assertNotIn("ask_user_choice", wechat_prompt)
|
||||
@@ -46,6 +50,13 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
source="telegram-test",
|
||||
username="tester",
|
||||
)
|
||||
web_agent_tools = MoviePilotToolFactory.create_tools(
|
||||
session_id="session-web",
|
||||
user_id="10001",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="tester",
|
||||
)
|
||||
wechat_tools = MoviePilotToolFactory.create_tools(
|
||||
session_id="session-2",
|
||||
user_id="10001",
|
||||
@@ -55,6 +66,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assertIn("ask_user_choice", [tool.name for tool in telegram_tools])
|
||||
self.assertIn("ask_user_choice", [tool.name for tool in web_agent_tools])
|
||||
self.assertNotIn("ask_user_choice", [tool.name for tool in wechat_tools])
|
||||
|
||||
def test_choice_tool_returns_direct_after_sending_interaction(self):
|
||||
@@ -74,7 +86,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
tool.set_agent_context(agent_context={})
|
||||
|
||||
with patch(
|
||||
"app.agent.tools.impl.ask_user_choice.ToolChain.async_post_message",
|
||||
"app.agent.tools.base.ToolChain.async_post_message",
|
||||
new=AsyncMock(),
|
||||
) as async_post_message:
|
||||
result = asyncio.run(
|
||||
@@ -115,7 +127,7 @@ class TestAgentInteraction(unittest.TestCase):
|
||||
)
|
||||
|
||||
with patch(
|
||||
"app.agent.tools.impl.ask_user_choice.ToolChain.async_post_message",
|
||||
"app.agent.tools.base.ToolChain.async_post_message",
|
||||
new=AsyncMock(),
|
||||
) as async_post_message:
|
||||
result = asyncio.run(
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from app import schemas
|
||||
from app.agent import ReplyMode
|
||||
from app.api.endpoints.agent import (
|
||||
_build_web_agent_session_id,
|
||||
_WebAgentMoviePilotAgent,
|
||||
_build_web_agent_notification_events,
|
||||
_build_web_agent_session_id,
|
||||
_resolve_web_agent_choice_payload,
|
||||
_split_web_agent_output,
|
||||
)
|
||||
from app.helper.interaction import AgentInteractionOption, agent_interaction_manager
|
||||
from app.schemas.message import ChannelCapability, ChannelCapabilityManager
|
||||
from app.schemas.types import MessageChannel, NotificationType
|
||||
|
||||
@@ -44,8 +50,34 @@ def test_build_web_agent_session_id_is_stable_per_user_and_seed():
|
||||
assert first.startswith("web-agent:")
|
||||
|
||||
|
||||
def test_web_agent_admin_context_uses_current_user_id():
|
||||
"""Web Agent 工具权限应按当前登录用户 ID 判断管理员身份。"""
|
||||
agent = _WebAgentMoviePilotAgent(
|
||||
session_id="web-agent:session",
|
||||
user_id="7",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="normal-user",
|
||||
replay_mode=ReplyMode.CAPTURE_ONLY,
|
||||
)
|
||||
|
||||
with patch("app.api.endpoints.agent.UserOper") as user_oper:
|
||||
user_oper.return_value.async_get_by_id = AsyncMock(
|
||||
return_value=SimpleNamespace(is_superuser=True)
|
||||
)
|
||||
|
||||
assert asyncio.run(agent._is_system_admin_context()) is True
|
||||
user_oper.return_value.async_get_by_id.assert_awaited_once_with(7)
|
||||
|
||||
|
||||
def test_web_agent_channel_supports_streaming_and_attachments():
|
||||
"""WebAgent 渠道应声明流式、多媒体和文件发送能力。"""
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.INLINE_BUTTONS
|
||||
)
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.CALLBACK_QUERIES
|
||||
)
|
||||
assert ChannelCapabilityManager.supports_capability(
|
||||
MessageChannel.WebAgent, ChannelCapability.MESSAGE_EDITING
|
||||
)
|
||||
@@ -109,3 +141,81 @@ def test_build_web_agent_notification_events_registers_local_file(tmp_path):
|
||||
assert attachment["mime_type"] == "text/plain"
|
||||
assert attachment["size"] == 5
|
||||
assert attachment["url"].startswith("message/agent/file/")
|
||||
|
||||
|
||||
def test_build_web_agent_notification_events_extracts_choice_card():
|
||||
"""Agent 按钮通知应转换为 Web 选择卡片事件而非普通文本。"""
|
||||
events = _build_web_agent_notification_events(
|
||||
schemas.Notification(
|
||||
channel=MessageChannel.WebAgent,
|
||||
mtype=NotificationType.Agent,
|
||||
title="需要你的选择",
|
||||
text="请选择要执行的操作",
|
||||
buttons=[
|
||||
[
|
||||
{
|
||||
"text": "继续下载",
|
||||
"callback_data": "agent_interaction:choice:req-1:1",
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"text": "查看详情",
|
||||
"callback_data": "agent_interaction:choice:req-1:2",
|
||||
}
|
||||
],
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
assert events == [
|
||||
{
|
||||
"type": "choice",
|
||||
"choice": {
|
||||
"id": "req-1",
|
||||
"title": "需要你的选择",
|
||||
"prompt": "请选择要执行的操作",
|
||||
"buttons": [
|
||||
{
|
||||
"label": "继续下载",
|
||||
"callback_data": "agent_interaction:choice:req-1:1",
|
||||
},
|
||||
{
|
||||
"label": "查看详情",
|
||||
"callback_data": "agent_interaction:choice:req-1:2",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_resolve_web_agent_choice_payload_returns_next_message():
|
||||
"""Web 按钮回调应解析为下一条用户消息并返回卡片反馈。"""
|
||||
agent_interaction_manager.clear()
|
||||
request = agent_interaction_manager.create_request(
|
||||
session_id="web-agent:session",
|
||||
user_id="1",
|
||||
channel=MessageChannel.WebAgent.value,
|
||||
source="web-agent",
|
||||
username="admin",
|
||||
title="需要你的选择",
|
||||
prompt="请选择",
|
||||
options=[
|
||||
AgentInteractionOption(label="电影", value="我选择电影"),
|
||||
AgentInteractionOption(label="电视剧", value="我选择电视剧"),
|
||||
],
|
||||
)
|
||||
|
||||
try:
|
||||
result = _resolve_web_agent_choice_payload(
|
||||
callback_data=f"agent_interaction:choice:{request.request_id}:2",
|
||||
user_id="1",
|
||||
)
|
||||
finally:
|
||||
agent_interaction_manager.clear()
|
||||
|
||||
assert result["message"] == "我选择电视剧"
|
||||
assert result["session_id"] == "web-agent:session"
|
||||
assert result["feedback"]["prompt"] == "请选择"
|
||||
assert result["feedback"]["selected_label"] == "电视剧"
|
||||
|
||||
Reference in New Issue
Block a user