fix(botops): v1.3 - 适配服务端中断机制,Ctrl+C立即可靠停止

问题:收到interrupt_ack时仅显示系统通知,没有立即收尾前端状态:
- 思考区域一直停留在"💭 思考中..."状态,不会更新
- AI回复状态未重置,后续残留chunk可能继续追加
- 服务端不发送reasoning_end,思考区域无法正常结束

修复:
- ChatView新增abort_reasoning()方法:中断时将思考区域标签更新为"🛑 思考已中断"(保留已展示内容)
- append_reasoning()增加_ai_finalized检查,中断后丢弃残留思考片段,防止重新创建思考区域
- app.py收到interrupt_ack时:先abort_reasoning()更新思考区域,再finish_ai()收尾AI状态,最后显示系统通知
- 中断后服务端发来的残留chunk/reasoning/complete均被安全忽略(append_ai已有_finalized检查,finish_ai幂等)
- 更新README v1.3日志
This commit is contained in:
qing committed 2026-08-01 14:12:27 +08:00
1 parent 9d737897df
commit c7ef7e715c
3 files changed
+24

No files matched your search

+7
View File
@@ -71,6 +71,13 @@ BotOps/
## 更新日志
### v1.3
- ✅ **适配服务端中断机制(Ctrl+C可靠停止)**:
- 收到 `interrupt_ack` 时立即收尾前端状态:调用 `abort_reasoning()` 将思考区域更新为"🛑 思考已中断"标签,调用 `finish_ai()` 结束AI回复状态
- 新增 `ChatView.abort_reasoning()` 方法:中断时更新思考区域标签(保留已展示的思考内容)
- `append_reasoning()` 增加 `_ai_finalized` 检查,中断后丢弃残留的思考片段防止重新创建思考区域
- 中断后服务端发来的残留 `chunk`/`reasoning`/`complete` 消息均被安全忽略,不会污染UI
### v1.2
- ✅ **彻底修复思考区域顺序问题**:移除预创建空 AI widget 的逻辑,改为第一次收到 `chunk` 时才创建 widget。按消息到达顺序自然挂载(`reasoning_start`→`reasoning`→`chunk`),从根本上保证思考区域始终在正式回答之前
- ✅ 简化 `complete` 处理逻辑:仅作为收尾重置状态信号,不再进行补建逻辑
+3
View File
@@ -226,6 +226,9 @@ class BotOpsApp(App):
f"❌ [{data.get('task_id', '?')}] {data.get('task_title', '?')}: "
f"{data.get('error', '未知错误')}")
elif t == "interrupt_ack":
# 服务端确认中断:立即收尾前端状态(更新思考区域为已中断、结束AI回复)
await self.chat.abort_reasoning()
await self.chat.finish_ai("")
await self.notify_system(f"🛑 {content}")
elif t == "error":
await self.notify_system(f"❌ {content}", level="error")
+14
View File
@@ -75,6 +75,9 @@ class ChatView(VerticalScroll):
"""追加思考内容增量。"""
if not delta:
return
if self._ai_finalized:
# 已收尾(中断/完成),丢弃残留的思考片段
return
if self._reasoning_widget is None:
await self.start_reasoning()
self._reasoning_text += delta
@@ -95,6 +98,17 @@ class ChatView(VerticalScroll):
self._reasoning_widget.update(f"💭 思考过程({len(self._reasoning_text)}字符)\n{display}\n{'─' * 30}")
# 思考区域保留显示,不删除
async def abort_reasoning(self):
"""中断思考区域(用户Ctrl+C),更新提示文字为已中断。"""
if self._reasoning_widget is not None:
if self._reasoning_text:
display = self._reasoning_text
if len(display) > 3000:
display = "...(前略)\n" + display[-3000:]
self._reasoning_widget.update(f"🛑 思考已中断({len(self._reasoning_text)}字符)\n{display}")
else:
self._reasoning_widget.update("🛑 思考已中断")
async def _ensure_ai_widget(self):
"""确保AI widget存在(第一次chunk到来时创建)。"""
if self._ai_widget is None: