mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-08 17:08:35 +08:00
fix(agent): quote verbose telegram tool messages
This commit is contained in:
@@ -76,7 +76,7 @@ class StreamingHandler:
|
||||
self._allow_dispatch_without_context = False
|
||||
# 非啰嗦模式下的待输出工具统计,等下一段文本到来时再统一补一句摘要
|
||||
self._pending_tool_stats: dict[str, dict[str, Any]] = {}
|
||||
# 本轮已写入缓冲区的工具摘要行,供 Telegram 富文本渲染时做区分样式
|
||||
# 本轮已写入缓冲区的工具展示行,供 Telegram 富文本渲染时做区分样式
|
||||
self._tool_summaries: set[str] = set()
|
||||
|
||||
def set_dispatch_policy(self, allow_dispatch_without_context: bool = False) -> None:
|
||||
@@ -108,6 +108,24 @@ class StreamingHandler:
|
||||
self._buffer += emitted
|
||||
return emitted
|
||||
|
||||
def emit_tool_message(self, message: str) -> str:
|
||||
"""
|
||||
将啰嗦模式的逐条工具提示写入缓冲区,并登记为独立展示行。
|
||||
|
||||
Telegram Rich Markdown 依赖登记结果把工具提示渲染成引用块;
|
||||
其他渠道仍消费相同的纯文本缓冲内容。
|
||||
"""
|
||||
normalized_message = str(message or "").strip()
|
||||
if not normalized_message:
|
||||
return ""
|
||||
|
||||
tool_message = f"⚙️ => {normalized_message}"
|
||||
with self._lock:
|
||||
self._tool_summaries.update(
|
||||
line for line in tool_message.splitlines() if line
|
||||
)
|
||||
return self.emit(f"\n\n{tool_message}\n\n")
|
||||
|
||||
async def take(self) -> str:
|
||||
"""
|
||||
获取当前已积累的消息内容,获取后清空缓冲区。
|
||||
@@ -539,7 +557,7 @@ class StreamingHandler:
|
||||
|
||||
def _quote_tool_summary_lines(self, text: str) -> str:
|
||||
"""
|
||||
将缓冲区中的工具摘要整行转换为 Markdown 引用块。
|
||||
将缓冲区中的工具展示行转换为 Markdown 引用块。
|
||||
|
||||
富文本会把普通段落间的空行折叠成紧凑排版,引用块作为独立 block 类型
|
||||
渲染,保证工具执行信息在 Telegram 上始终与正文有可辨识的视觉分隔。
|
||||
|
||||
@@ -53,6 +53,10 @@ else:
|
||||
"""追加流式文本并返回实际追加内容。"""
|
||||
...
|
||||
|
||||
def emit_tool_message(self, message: str) -> str:
|
||||
"""追加逐条工具提示并登记其展示语义。"""
|
||||
...
|
||||
|
||||
async def take(self) -> str:
|
||||
"""取出并清空当前缓冲内容。"""
|
||||
...
|
||||
@@ -434,7 +438,7 @@ class MoviePilotTool(BaseTool, metaclass=ABCMeta):
|
||||
if self._stream_handler.is_auto_flushing:
|
||||
# 渠道支持编辑:工具消息追加到 buffer,由定时刷新推送
|
||||
if tool_message:
|
||||
self._stream_handler.emit(f"\n\n⚙️ => {tool_message}\n\n")
|
||||
self._stream_handler.emit_tool_message(tool_message)
|
||||
else:
|
||||
allow_dispatch_without_context = self._agent_context.get(
|
||||
"should_dispatch_reply", False
|
||||
|
||||
@@ -429,6 +429,34 @@ class TestAgentToolStreaming:
|
||||
assert text == "(查看了 1 个目录)\n\n"
|
||||
assert rich_message == "> (查看了 1 个目录)\n\n"
|
||||
|
||||
def test_verbose_tool_message_is_quoted_for_telegram(self):
|
||||
"""校验 Telegram 啰嗦模式逐条工具提示也使用引用块。"""
|
||||
|
||||
async def _run():
|
||||
tool = DummyTool(session_id="session-1", user_id="10001")
|
||||
handler = StreamingHandler()
|
||||
handler._channel = NotificationChannel.Telegram.value
|
||||
handler._source = "telegram"
|
||||
handler._streaming_enabled = True
|
||||
handler.emit("前置内容")
|
||||
flush_task = asyncio.create_task(asyncio.sleep(60))
|
||||
handler._flush_task = flush_task
|
||||
tool.set_stream_handler(handler)
|
||||
|
||||
try:
|
||||
with patch.object(settings, "AI_AGENT_VERBOSE", True):
|
||||
await tool._arun()
|
||||
return handler._buffer, handler._get_rich_message(handler._buffer)
|
||||
finally:
|
||||
flush_task.cancel()
|
||||
await asyncio.gather(flush_task, return_exceptions=True)
|
||||
handler._flush_task = None
|
||||
|
||||
text, rich_message = asyncio.run(_run())
|
||||
|
||||
assert text == "前置内容\n\n⚙️ => run test tool\n\n"
|
||||
assert rich_message == "前置内容\n\n> ⚙️ => run test tool\n\n"
|
||||
|
||||
def test_rich_message_keeps_body_text_unquoted_for_telegram(self):
|
||||
"""校验 Telegram 富文本只转换工具摘要行,正文保持原样。"""
|
||||
handler = StreamingHandler()
|
||||
|
||||
Reference in New Issue
Block a user