mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-05-25 07:26:48 +00:00
fix(agent): reply Feishu agent streams with cards
This commit is contained in:
@@ -179,6 +179,8 @@ class MoviePilotAgent:
|
||||
channel: str = None,
|
||||
source: str = None,
|
||||
username: str = None,
|
||||
original_message_id: Optional[str] = None,
|
||||
original_chat_id: Optional[str] = None,
|
||||
replay_mode: ReplyMode = ReplyMode.DISPATCH,
|
||||
persist_output_message: bool = True,
|
||||
allow_message_tools: bool = True,
|
||||
@@ -189,6 +191,8 @@ class MoviePilotAgent:
|
||||
self.channel = channel
|
||||
self.source = source
|
||||
self.username = username
|
||||
self.original_message_id = original_message_id
|
||||
self.original_chat_id = original_chat_id
|
||||
self.reply_mode = replay_mode
|
||||
self.persist_output_message = persist_output_message
|
||||
self.allow_message_tools = allow_message_tools
|
||||
@@ -619,6 +623,8 @@ class MoviePilotAgent:
|
||||
source=self.source,
|
||||
user_id=self.user_id,
|
||||
username=self.username,
|
||||
original_message_id=self.original_message_id,
|
||||
original_chat_id=self.original_chat_id,
|
||||
)
|
||||
|
||||
# 流式运行智能体,token 直接推送到 stream_handler
|
||||
@@ -794,6 +800,8 @@ class _MessageTask:
|
||||
channel: Optional[str] = None
|
||||
source: Optional[str] = None
|
||||
username: Optional[str] = None
|
||||
original_message_id: Optional[str] = None
|
||||
original_chat_id: Optional[str] = None
|
||||
reply_mode: ReplyMode = ReplyMode.DISPATCH
|
||||
|
||||
|
||||
@@ -878,6 +886,8 @@ class AgentManager:
|
||||
channel: str = None,
|
||||
source: str = None,
|
||||
username: str = None,
|
||||
original_message_id: Optional[str] = None,
|
||||
original_chat_id: Optional[str] = None,
|
||||
reply_mode: ReplyMode = ReplyMode.DISPATCH,
|
||||
) -> str:
|
||||
"""
|
||||
@@ -893,6 +903,8 @@ class AgentManager:
|
||||
channel=channel,
|
||||
source=source,
|
||||
username=username,
|
||||
original_message_id=original_message_id,
|
||||
original_chat_id=original_chat_id,
|
||||
reply_mode=reply_mode,
|
||||
)
|
||||
|
||||
@@ -980,6 +992,8 @@ class AgentManager:
|
||||
channel=task.channel,
|
||||
source=task.source,
|
||||
username=task.username,
|
||||
original_message_id=task.original_message_id,
|
||||
original_chat_id=task.original_chat_id,
|
||||
replay_mode=task.reply_mode,
|
||||
)
|
||||
self.active_agents[session_id] = agent
|
||||
@@ -992,6 +1006,8 @@ class AgentManager:
|
||||
agent.source = task.source
|
||||
if task.username:
|
||||
agent.username = task.username
|
||||
agent.original_message_id = task.original_message_id
|
||||
agent.original_chat_id = task.original_chat_id
|
||||
agent.reply_mode = task.reply_mode
|
||||
|
||||
return await agent.process(task.message, images=task.images, files=task.files)
|
||||
|
||||
@@ -61,6 +61,8 @@ class StreamingHandler:
|
||||
self._source: Optional[str] = None
|
||||
self._user_id: Optional[str] = None
|
||||
self._username: Optional[str] = None
|
||||
self._original_message_id: Optional[str] = None
|
||||
self._original_chat_id: Optional[str] = None
|
||||
self._title: str = ""
|
||||
self._allow_dispatch_without_context = False
|
||||
# 非啰嗦模式下的待输出工具统计,等下一段文本到来时再统一补一句摘要
|
||||
@@ -147,6 +149,8 @@ class StreamingHandler:
|
||||
source: Optional[str] = None,
|
||||
user_id: Optional[str] = None,
|
||||
username: Optional[str] = None,
|
||||
original_message_id: Optional[str] = None,
|
||||
original_chat_id: Optional[str] = None,
|
||||
title: str = "",
|
||||
):
|
||||
"""
|
||||
@@ -163,6 +167,8 @@ class StreamingHandler:
|
||||
self._source = source
|
||||
self._user_id = user_id
|
||||
self._username = username
|
||||
self._original_message_id = original_message_id
|
||||
self._original_chat_id = original_chat_id
|
||||
self._title = title
|
||||
|
||||
self._streaming_enabled = True
|
||||
@@ -472,6 +478,8 @@ class StreamingHandler:
|
||||
mtype=NotificationType.Agent,
|
||||
userid=self._user_id,
|
||||
username=self._username,
|
||||
original_message_id=self._original_message_id,
|
||||
original_chat_id=self._original_chat_id,
|
||||
title=self._title,
|
||||
text=current_text,
|
||||
),
|
||||
@@ -515,6 +523,8 @@ class StreamingHandler:
|
||||
mtype=NotificationType.Agent,
|
||||
userid=self._user_id,
|
||||
username=self._username,
|
||||
original_message_id=self._original_message_id,
|
||||
original_chat_id=self._original_chat_id,
|
||||
title=self._title,
|
||||
text=current_text,
|
||||
),
|
||||
|
||||
@@ -85,12 +85,16 @@ class _OpenAIStreamingHandler(StreamingHandler):
|
||||
source: Optional[str] = None,
|
||||
user_id: Optional[str] = None,
|
||||
username: Optional[str] = None,
|
||||
original_message_id: Optional[str] = None,
|
||||
original_chat_id: Optional[str] = None,
|
||||
title: str = "",
|
||||
):
|
||||
self._channel = channel
|
||||
self._source = source
|
||||
self._user_id = user_id
|
||||
self._username = username
|
||||
self._original_message_id = original_message_id
|
||||
self._original_chat_id = original_chat_id
|
||||
self._title = title
|
||||
self._streaming_enabled = True
|
||||
self._sent_text = ""
|
||||
|
||||
@@ -269,6 +269,8 @@ class MessageChain(ChainBase):
|
||||
source=source,
|
||||
userid=userid,
|
||||
username=username,
|
||||
original_message_id=original_message_id,
|
||||
original_chat_id=original_chat_id,
|
||||
images=images,
|
||||
files=files,
|
||||
)
|
||||
@@ -284,6 +286,8 @@ class MessageChain(ChainBase):
|
||||
source=source,
|
||||
userid=userid,
|
||||
username=username,
|
||||
original_message_id=original_message_id,
|
||||
original_chat_id=original_chat_id,
|
||||
images=images,
|
||||
files=files,
|
||||
)
|
||||
@@ -1055,6 +1059,8 @@ class MessageChain(ChainBase):
|
||||
source: str,
|
||||
userid: Union[str, int],
|
||||
username: str,
|
||||
original_message_id: Optional[Union[str, int]] = None,
|
||||
original_chat_id: Optional[str] = None,
|
||||
images: Optional[List[CommingMessage.MessageImage]] = None,
|
||||
files: Optional[List[CommingMessage.MessageAttachment]] = None,
|
||||
session_id: Optional[str] = None,
|
||||
@@ -1168,6 +1174,8 @@ class MessageChain(ChainBase):
|
||||
channel=channel.value if channel else None,
|
||||
source=source,
|
||||
username=username,
|
||||
original_message_id=str(original_message_id) if original_message_id else None,
|
||||
original_chat_id=original_chat_id,
|
||||
),
|
||||
global_vars.loop,
|
||||
)
|
||||
|
||||
@@ -652,21 +652,29 @@ class Feishu:
|
||||
userid: Optional[str] = None,
|
||||
chat_id: Optional[str] = None,
|
||||
receive_id_type: Optional[str] = None,
|
||||
original_message_id: Optional[str] = None,
|
||||
) -> Optional[dict]:
|
||||
card_id = self._create_streaming_card(title=title, text=text)
|
||||
if not card_id:
|
||||
return None
|
||||
receive_id, resolved_receive_id_type = self._resolve_target(
|
||||
userid=userid,
|
||||
chat_id=chat_id,
|
||||
receive_id_type=receive_id_type,
|
||||
)
|
||||
result = self._send_message(
|
||||
receive_id,
|
||||
resolved_receive_id_type,
|
||||
"interactive",
|
||||
{"type": "card", "data": {"card_id": card_id}},
|
||||
)
|
||||
if original_message_id:
|
||||
result = self._reply_message(
|
||||
message_id=original_message_id,
|
||||
msg_type="interactive",
|
||||
content={"type": "card", "data": {"card_id": card_id}},
|
||||
)
|
||||
else:
|
||||
receive_id, resolved_receive_id_type = self._resolve_target(
|
||||
userid=userid,
|
||||
chat_id=chat_id,
|
||||
receive_id_type=receive_id_type,
|
||||
)
|
||||
result = self._send_message(
|
||||
receive_id,
|
||||
resolved_receive_id_type,
|
||||
"interactive",
|
||||
{"type": "card", "data": {"card_id": card_id}},
|
||||
)
|
||||
if not result:
|
||||
return None
|
||||
result["metadata"] = {
|
||||
@@ -1111,7 +1119,6 @@ class Feishu:
|
||||
message.mtype == NotificationType.Agent
|
||||
and not message.buttons
|
||||
and not message.link
|
||||
and not original_message_id
|
||||
)
|
||||
if is_streaming_agent_text:
|
||||
try:
|
||||
@@ -1121,6 +1128,7 @@ class Feishu:
|
||||
userid=userid,
|
||||
chat_id=chat_id,
|
||||
receive_id_type=receive_id_type,
|
||||
original_message_id=original_message_id,
|
||||
)
|
||||
except Exception as err:
|
||||
logger.error(f"飞书流式卡片发送失败:{err}")
|
||||
|
||||
@@ -263,6 +263,33 @@ class TestAgentToolStreaming(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(handler.has_sent_message)
|
||||
|
||||
def test_flush_passes_original_message_context_to_send_direct_message(self):
|
||||
handler = StreamingHandler()
|
||||
handler._channel = MessageChannel.Feishu.value
|
||||
handler._source = "feishu-main"
|
||||
handler._user_id = "ou_user"
|
||||
handler._username = "tester"
|
||||
handler._original_message_id = "om_origin"
|
||||
handler._original_chat_id = "oc_origin"
|
||||
handler._streaming_enabled = True
|
||||
handler.emit("hello")
|
||||
|
||||
with patch(
|
||||
"app.agent.callback.run_in_threadpool", new_callable=AsyncMock
|
||||
) as run_in_threadpool_mock:
|
||||
run_in_threadpool_mock.return_value = MessageResponse(
|
||||
message_id="om_stream",
|
||||
chat_id="oc_origin",
|
||||
source="feishu-main",
|
||||
success=True,
|
||||
)
|
||||
|
||||
asyncio.run(handler._flush())
|
||||
|
||||
notification = run_in_threadpool_mock.await_args.args[1]
|
||||
self.assertEqual(notification.original_message_id, "om_origin")
|
||||
self.assertEqual(notification.original_chat_id, "oc_origin")
|
||||
|
||||
def test_verbose_background_tool_call_does_not_post_message(self):
|
||||
async def _run():
|
||||
tool = DummyTool(session_id="session-1", user_id="10001")
|
||||
|
||||
@@ -301,6 +301,30 @@ class TestFeishu(unittest.TestCase):
|
||||
self.assertEqual(message_request.request_body.msg_type, "interactive")
|
||||
self.assertEqual(json.loads(message_request.request_body.content)["data"]["card_id"], "card_stream")
|
||||
|
||||
def test_send_notification_replies_with_streaming_card_for_agent_text(self):
|
||||
client = self._build_client()
|
||||
client._api_client, message_api = self._build_message_api(
|
||||
reply_response=self._success_response(message_id="om_reply", chat_id="oc_stream"),
|
||||
card_create_response=self._card_create_success_response("card_stream"),
|
||||
)
|
||||
|
||||
result = client.send_notification(
|
||||
Notification(
|
||||
mtype=NotificationType.Agent,
|
||||
title="MoviePilot助手",
|
||||
text="第一帧内容",
|
||||
),
|
||||
userid="ou_user_stream",
|
||||
original_message_id="om_origin",
|
||||
)
|
||||
|
||||
self.assertTrue(result["success"])
|
||||
message_api.create.assert_not_called()
|
||||
reply_request = message_api.reply.call_args.args[0]
|
||||
self.assertEqual(reply_request.message_id, "om_origin")
|
||||
self.assertEqual(reply_request.request_body.msg_type, "interactive")
|
||||
self.assertEqual(json.loads(reply_request.request_body.content)["data"]["card_id"], "card_stream")
|
||||
|
||||
def test_edit_message_uses_cardkit_content_for_streaming_card(self):
|
||||
client = self._build_client()
|
||||
client._api_client, message_api = self._build_message_api(
|
||||
|
||||
Reference in New Issue
Block a user