11 Commits
Author SHA1 Message Date
qing af5cd02663 fix(aibox/botops): 思考区域按时间顺序分布,不再全部堆在顶部
问题根因:
1. ChatView.end_reasoning() 不清空 _reasoning_widget 引用,导致
   多轮工具调用时 start_reasoning() 直接 return,所有轮次的思考
   内容都被追加到第一个思考区域(在聊天区最顶部)。
2. AI widget 在工具调用期间不重置,导致后续轮次的思考区域被
   挂载在AI widget中间,顺序混乱。

修复:
1. end_reasoning()/abort_reasoning() 更新标签后清除引用(widget
   保留在DOM显示),下一轮 reasoning_start 创建新的思考区域。
2. 新增 ai_break 协议信号:服务端在工具调用执行前发送,前端
   break_ai() 清除AI widget引用(保留已显示内容),后续chunk
   创建新的AI widget,确保思考区域按时间顺序插入在两段AI文本之间。
3. ai_break 前flush chunk_buffer,防止工具调用前的最后文本片段丢失。
2026-08-01 21:10:42 +08:00
qing f3d06252bb fix(aibox/botops): 修复两处重复输出根因
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() 整体
   重新解析,避免增量解析的边界问题。
2026-08-01 21:01:31 +08:00
qing c7ef7e715c 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日志
2026-08-01 14:12:27 +08:00
qing bf2b5d31f7 fix(botops): v1.2 - 彻底修复思考区域与正式回答顺序问题
问题根因:之前预创建空 AI widget,思考 widget 挂载在其后,即使动态重排也可能因 Textual 组件树更新时序导致顺序不稳定。

修复方案:
- 移除 prefab 空 AI widget 预创建逻辑
- start_ai() 仅标记状态,不创建任何 widget
- _ensure_ai_widget() 延迟到第一次 chunk 到来时才创建 AI widget
- 按消息到达顺序自然挂载:reasoning_start → reasoning → chunk → end_reasoning/finish_ai
- 思考 widget 必然先于 AI widget 挂载,顺序天然正确

同时:
- 简化 app.py 发送失败处理,不再需要移除空 widget
- 更新 README v1.2 说明
2026-08-01 05:00:56 +08:00
qing 039d6acf37 fix(botops): fix reasoning panel appearing after AI response and filter conversation start notification
- Add _ai_prefab flag to track pre-created empty AI widget
- start_reasoning() now reorders widgets: remove empty AI widget, mount reasoning first, then remount AI widget
- This ensures reasoning always appears BEFORE the actual answer
- Filter out 'Conversation started'/'对话开始' info notifications (implicit event, no need to show)
- Increase reasoning display limit from 2000 to 3000 chars
2026-08-01 04:55:12 +08:00
qing b79044d631 feat(aibox): add DeepSeek reasoning mode support with tool calling on deepseek-v4-flash
- Add api.reasoning_enabled config (default false) to enable thinking mode
- Uses deepseek-v4-flash native thinking capability (thinking param), supports tool calls while reasoning
- Real-time reasoning content streaming via reasoning_start/reasoning/reasoning_end WS messages
- Frontend shows reasoning in gray italic area, auto-folds long reasoning (>2000 chars)
- Dynamic reasoning detection: shows reasoning even if model returns it without explicit config
- Increase default API timeout to 120s for longer reasoning responses
- Remove duplicate _add_assistant_message_with_reasoning method
- Update docs: config example, message types table, v2.5 changelog
2026-08-01 04:33:55 +08:00
qing 1b9e6f6b3a fix(botops): resolve duplicate AI response rendering in ChatView
Root cause: aibox.py think_and_respond recursively calls itself after tool
execution, and each recursive call's complete callback carries only the
final-round full_response. The old finish_ai logic treated complete as a
full-text alignment signal — when full_text != self._ai_text (which always
happened after recursive tool calls because _ai_text accumulated all chunks
while complete only held the last round), it triggered remove+remount,
causing visible duplicate/flash of AI responses. A secondary issue was
dialogue_ended arriving before complete, which made a second finish_ai call
remount the message a second time.

Changes:
- Remove the full-text replacement branch in finish_ai; chunk streaming is
  now the authoritative content source, complete is a finalization signal only
- Add _ai_finalized flag: start_ai resets it, finish_ai sets it; repeated
  finish_ai calls (dialogue_ended + complete) return early to prevent remount
- append_ai drops late chunks after finalization to avoid dirty appends
- Empty pre-built widget without chunks still gets filled via w.update(full_text)
- Full lost-frame fallback (no widget + full_text) is preserved
- Update BotOps/README.md protocol section to reflect new complete semantics
2026-08-01 02:56:29 +08:00
qing b86b089634 fix(BotOps): 修正 aibox 协议语义错配导致的输出丢失
- 服务端 chunk 为增量片段(chunk_buffer 发送后即清零),原 _delta
  按累计全文假设计算,相邻 chunk 前缀撞字时静默丢字符;改为直接拼接
- complete 携带完整全文,finish_ai 改为全文对齐(不一致时整体替换,
  可自愈丢帧/乱序),修复结尾全文重复 append
- 新增 line/reasoning/task_added/dialogue_reset/pong 消息路由:
  line(🔧 工具调用提示、工具输出)不再落入兜底污染聊天区,改投任务侧栏
- 发送失败时移除预建的空 AI 消息框并提示,避免空等回复
- 断线提示补充重连窗口期服务端输出丢失说明
- 同步修正 ws_client/README 中的协议描述
2026-07-29 09:32:30 +08:00
qing c93023bd5d fix(BotOps): 聊天区改用 anchor 锚定吸底,修复流式时滚动条不跟随 2026-07-28 22:48:45 +08:00
qing c064caa3ec chore: BotOps 添加 .gitignore 并移除误提交的 __pycache__ 2026-07-28 22:41:34 +08:00
qing f91d4166d6 feat: 新增 BotOps 子模块——aibox 的 Textual TUI 前端(流式 Markdown 渲染+自动重连+任务侧栏) 2026-07-28 22:41:15 +08:00