From f3d06252bb1398b872372851ad96732e60084993 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 1 Aug 2026 21:01:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(aibox/botops):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=A4=E5=A4=84=E9=87=8D=E5=A4=8D=E8=BE=93=E5=87=BA=E6=A0=B9?= =?UTF-8?q?=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 侧栏重复:_handle_tool_calls 中 msg_type in ("line", "complete") 将流式工具末尾的 complete 信号也当作 line 转发,导致所有支持 流式输出的工具(get_current_time、run_command、read_file等20+个) 结果在侧栏显示两次。改为仅转发 "line" 类型。 2. 聊天区Markdown列表重复:Textual 的 Markdown.append() 在 chunk 边界切断 Markdown 语法(如列表项 "- "被拆成两个chunk)时,增量 解析可能导致内容重复渲染。改为累积完整文本后调用 update() 整体 重新解析,避免增量解析的边界问题。 --- BotOps/widgets/chat_view.py | 11 ++++++++--- aibox/aibox.py | 4 +++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/BotOps/widgets/chat_view.py b/BotOps/widgets/chat_view.py index 09c0bf8..4f0734e 100644 --- a/BotOps/widgets/chat_view.py +++ b/BotOps/widgets/chat_view.py @@ -117,7 +117,12 @@ class ChatView(VerticalScroll): self._anchor_bottom() async def append_ai(self, delta: str): - """追加增量片段(服务端 chunk 为增量语义,直接拼接)。""" + """追加增量片段(服务端 chunk 为增量语义,累积文本后 update 整个 Markdown)。 + + 使用 update() 而非 Markdown.append():Textual 的 Markdown.append() 在 + chunk 边界切断 Markdown 语法(如列表项、代码块)时可能导致重复解析渲染。 + 累积完整文本后 update() 确保每次都从完整文本重新解析,避免重复。 + """ if not delta: return if self._ai_finalized: @@ -125,9 +130,9 @@ class ChatView(VerticalScroll): return # 第一次收到chunk时创建widget(此时思考widget如果存在已经在前面了) await self._ensure_ai_widget() - self._ai_widget.append(delta) self._ai_text += delta - # anchor 状态下容器会自动吸底;保险起见刷新后再滚一次 + # 累积文本后整体update,而非增量append,避免Markdown增量解析导致的重复渲染 + self._ai_widget.update(self._ai_text) self.call_after_refresh(self.scroll_end, animate=False) async def finish_ai(self, full_text: str = ""): diff --git a/aibox/aibox.py b/aibox/aibox.py index 2334e97..fa3e612 100644 --- a/aibox/aibox.py +++ b/aibox/aibox.py @@ -7068,7 +7068,9 @@ class DeepSeekChat: while not output_queue.empty(): try: msg_type, content = output_queue.get_nowait() - if output_callback and msg_type in ("line", "complete"): + if output_callback and msg_type == "line": + # 只转发"line"类型;"complete"是流式结束信号,不作为行输出 + # (之前complete也被转发导致工具结果在侧栏显示两次) output_callback("line", str(content) if content is not None else "") has_stream_output = True except queue.Empty: