fix(aibox/client): 放弃 rich.Live 改为 complete 时一次性静态渲染,修复内容一闪而过
This commit is contained in:
1 parent
2982bc30a4
commit
dabd892601
1 file changed
+29
-48
+29
-48
@@ -15,10 +15,9 @@ from typing import Optional, Dict, Any, List
|
||||
import websockets
|
||||
from colorama import init, Fore, Style as ColoramaStyle
|
||||
|
||||
# rich 为可选依赖:有则流式渲染 Markdown,无则回退纯文本流式输出
|
||||
# rich 为可选依赖:有则 AI 回复渲染为 Markdown,无则回退纯文本流式输出
|
||||
try:
|
||||
from rich.console import Console
|
||||
from rich.live import Live
|
||||
from rich.markdown import Markdown
|
||||
_RICH = True
|
||||
except ImportError:
|
||||
@@ -165,10 +164,9 @@ class SimpleCLI:
|
||||
self._manual_disconnect = False # 用户主动 /disconnect、/quit、/connect 后置 True,抑制自动重连
|
||||
self._reconnect_task: Optional[asyncio.Task] = None
|
||||
|
||||
# Markdown 流式渲染状态(rich 可用时启用)
|
||||
# Markdown 渲染(rich 可用时启用)
|
||||
self._console = Console() if _RICH else None
|
||||
self._md_live = None # rich.Live 实例(渲染进行中)
|
||||
self._md_buffer = "" # 当前累计的 AI 回复全文
|
||||
self._md_generating_shown = False # "生成中…"状态行是否已显示
|
||||
|
||||
# 流式输出状态
|
||||
self.streaming_line = False # 是否正在流式输出行
|
||||
@@ -194,7 +192,6 @@ class SimpleCLI:
|
||||
|
||||
def _print_line(self, line: str = "", end: str = "\n"):
|
||||
"""打印一行,并恢复提示符"""
|
||||
self._md_stream_stop() # 打印普通行前,先定格进行中的 Markdown 渲染
|
||||
with self.lock:
|
||||
current = self.current_input
|
||||
sys.stdout.write('\r' + ' ' * 120 + '\r')
|
||||
@@ -272,13 +269,9 @@ class SimpleCLI:
|
||||
if key in self.displayed_messages:
|
||||
return
|
||||
|
||||
# rich 可用:累计文本直接整体渲染为 Markdown(服务端 chunk 为累计全文)
|
||||
# rich 可用:流式期间只显示状态行并累计全文,complete 时一次性渲染
|
||||
if _RICH:
|
||||
with self.lock:
|
||||
self.waiting_for_response = True
|
||||
self.prompt_shown = False
|
||||
self.streaming_line = True
|
||||
self._md_stream_update(clean)
|
||||
self._md_show_generating()
|
||||
self.ai_previous = clean
|
||||
self.ai_full = clean
|
||||
return
|
||||
@@ -316,36 +309,31 @@ class SimpleCLI:
|
||||
self.ai_previous = clean
|
||||
self.ai_full = clean
|
||||
|
||||
# ── Markdown 流式渲染(rich)─────────────────────────────────────────
|
||||
def _md_stream_start(self):
|
||||
"""开启 Live 渲染区域(若 rich 不可用或已开启则忽略)。"""
|
||||
if not _RICH or self._md_live is not None:
|
||||
return
|
||||
self._md_buffer = ""
|
||||
# transient=False:结束后保留最终渲染结果;vertical_overflow=visible 不裁剪长文
|
||||
self._md_live = Live(Markdown(""), console=self._console,
|
||||
refresh_per_second=8, vertical_overflow="visible")
|
||||
self._md_live.start()
|
||||
|
||||
def _md_stream_update(self, full_text: str):
|
||||
"""用累计全文刷新渲染(服务端 chunk 即累计文本)。"""
|
||||
# ── Markdown 渲染(rich)─────────────────────────────────────────────
|
||||
def _md_show_generating(self):
|
||||
"""流式期间显示一次性状态行(不实时渲染,避免 Live 闪烁/被卷走)。"""
|
||||
if not _RICH:
|
||||
return
|
||||
self._md_stream_start()
|
||||
self._md_buffer = full_text
|
||||
self._md_live.update(Markdown(full_text))
|
||||
with self.lock:
|
||||
if not self._md_generating_shown:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.write(f"{Colors.AI}🤖 AI助手: 生成中…{Colors.RESET}")
|
||||
sys.stdout.flush()
|
||||
self._md_generating_shown = True
|
||||
self.streaming_line = True
|
||||
self.prompt_shown = False
|
||||
|
||||
def _md_stream_stop(self):
|
||||
"""结束渲染:最终刷新一次后停住(内容定格保留在终端)。"""
|
||||
if self._md_live is not None:
|
||||
try:
|
||||
if self._md_buffer:
|
||||
self._md_live.update(Markdown(self._md_buffer))
|
||||
self._md_live.stop()
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
self._md_live = None
|
||||
def _md_render_final(self, text: str):
|
||||
"""complete 时一次性静态渲染最终 Markdown(先擦掉"生成中…"行)。"""
|
||||
if not _RICH:
|
||||
return
|
||||
with self.lock:
|
||||
if self._md_generating_shown:
|
||||
sys.stdout.write('\r' + ' ' * 40 + '\r')
|
||||
self._md_generating_shown = False
|
||||
self.streaming_line = False
|
||||
if text:
|
||||
self._console.print(Markdown(text))
|
||||
|
||||
def _remember_displayed(self, key: str):
|
||||
"""记录已显示消息(LRU:超 DISPLAYED_MAX 淘汰最旧,防长对话内存膨胀)。"""
|
||||
@@ -365,7 +353,6 @@ class SimpleCLI:
|
||||
elif msg_type == 'task_complete':
|
||||
self._handle_task_complete(extra)
|
||||
elif msg_type == 'dialogue_ended':
|
||||
self._md_stream_stop()
|
||||
with self.lock:
|
||||
if self.streaming_line:
|
||||
sys.stdout.write('\n')
|
||||
@@ -398,7 +385,6 @@ class SimpleCLI:
|
||||
"""处理流式块"""
|
||||
# 检查是否是工具调用相关的消息
|
||||
if content.startswith('🔧 调用工具'):
|
||||
self._md_stream_stop() # 工具消息与 Markdown 渲染区域交替,先定格
|
||||
with self.lock:
|
||||
self.in_tool_phase = True
|
||||
self.prompt_shown = False
|
||||
@@ -412,7 +398,6 @@ class SimpleCLI:
|
||||
|
||||
# 检查是否是工具结果
|
||||
if content.startswith(('✅ 对话记忆已保存', '💾 保存记忆', '❌', '⚠️')):
|
||||
self._md_stream_stop()
|
||||
with self.lock:
|
||||
# 打印工具结果
|
||||
sys.stdout.write(f"{Colors.TASK}{content}{Colors.RESET}\n")
|
||||
@@ -437,20 +422,18 @@ class SimpleCLI:
|
||||
|
||||
def _handle_complete(self, content: str):
|
||||
"""处理完成事件 - 只负责结束流式输出,不重复显示内容"""
|
||||
# rich 渲染路径:最终全文已在 Live 区域定格,只需收尾并恢复提示符
|
||||
# rich 渲染路径:擦掉"生成中…",一次性渲染最终 Markdown,恢复提示符
|
||||
if _RICH:
|
||||
clean = self._clean_content(content) if content else ""
|
||||
self._md_render_final(clean)
|
||||
if clean:
|
||||
self._md_stream_update(clean)
|
||||
self._remember_displayed(clean[:200])
|
||||
self._md_stream_stop()
|
||||
with self.lock:
|
||||
self.in_tool_phase = False
|
||||
self.waiting_for_response = False
|
||||
self.streaming_line = False
|
||||
self.prompt_shown = True
|
||||
current = self.current_input
|
||||
sys.stdout.write('\r' + ' ' * 120 + '\r')
|
||||
sys.stdout.write(self._get_prompt() + current)
|
||||
sys.stdout.flush()
|
||||
self.ai_previous = ""
|
||||
@@ -659,7 +642,6 @@ class SimpleCLI:
|
||||
else:
|
||||
self.display("system", "未连接")
|
||||
elif cmd == '/clear':
|
||||
self._md_stream_stop()
|
||||
os.system('clear' if os.name == 'posix' else 'cls')
|
||||
self.displayed_messages.clear()
|
||||
self.current_input = ""
|
||||
@@ -731,7 +713,6 @@ class SimpleCLI:
|
||||
self.display("error", "未连接到服务器")
|
||||
return
|
||||
|
||||
self._md_stream_stop() # 发送新消息前定格上一段渲染
|
||||
# 重置流式状态
|
||||
with self.lock:
|
||||
if self.streaming_line:
|
||||
|
||||
Reference in new issue
Block a user