feat(static/aibox.js): 同步BotOps修复到Web前端

同步以下修复:
1. endReasoning()/abortReasoning() 结束后清除引用(DOM保留显示),
   多轮工具调用时每轮创建独立思考区域,按时间顺序分布
2. 新增 breakAi() 分段函数,处理 ai_break 信号:工具调用前分段
   AI文本,后续chunk创建新气泡,避免思考区域被夹在AI文本中间
3. line消息过滤前缀扩展:增加分析工具结果/需要调用工具等
4. 未知消息类型fallback从appendAi改为addTaskLine,防止非AI内容污染聊天区
This commit is contained in:
qing committed 2026-08-01 22:19:54 +08:00
1 parent 392a734707
commit 2bb1d69d8a
1 file changed
+21 -5
+21 -5
View File
@@ -3,8 +3,8 @@
* aibox Web 客户端 · 移动端版(经 gateway WS 反向代理,仅管理员可用) * aibox Web 客户端 · 移动端版(经 gateway WS 反向代理,仅管理员可用)
* 协议与 BotOps/aibox client.py 完全兼容: * 协议与 BotOps/aibox client.py 完全兼容:
* 发送:{"type":"message","content":...} / {"type":"command","command":"/..."} / {"type":"interrupt",...} * 发送:{"type":"message","content":...} / {"type":"command","command":"/..."} / {"type":"interrupt",...}
* 接收:chunk(增量片段) / reasoning_start / reasoning / reasoning_end / complete / * 接收:chunk(增量片段) / reasoning_start / reasoning / reasoning_end / ai_break /
* line(过程行) / task_process / task_status / task_error / task_added / * complete / line(过程行) / task_process / task_status / task_error / task_added /
* dialogue_ended / dialogue_reset / interrupt_ack / error / info / * dialogue_ended / dialogue_reset / interrupt_ack / error / info /
* command_result / connection_ack / notification / pong * command_result / connection_ack / notification / pong
* ============================================================ */ * ============================================================ */
@@ -152,6 +152,8 @@ function endReasoning() {
reasoningHead.textContent = '💭 思考过程(' + reasoningText.length + ' 字符,点击折叠/展开)'; reasoningHead.textContent = '💭 思考过程(' + reasoningText.length + ' 字符,点击折叠/展开)';
reasoningBody.classList.add('folded'); /* 结束后默认折叠,避免占屏 */ reasoningBody.classList.add('folded'); /* 结束后默认折叠,避免占屏 */
} }
/* 清除引用(DOM元素保留显示),下一轮 reasoning_start 创建新的思考区域 */
reasoningBox = null; reasoningHead = null; reasoningBody = null; reasoningText = '';
} }
function abortReasoning() { function abortReasoning() {
/* 用户中断:更新思考区域标签为已中断,保留已展示内容 */ /* 用户中断:更新思考区域标签为已中断,保留已展示内容 */
@@ -162,6 +164,16 @@ function abortReasoning() {
reasoningHead.textContent = '🛑 思考已中断'; reasoningHead.textContent = '🛑 思考已中断';
} }
} }
/* 清除引用,DOM元素保留显示 */
reasoningBox = null; reasoningHead = null; reasoningBody = null; reasoningText = '';
}
/* ---------- AI 文本分段(工具调用时 breakAi,后续 chunk 创建新气泡) ---------- */
function breakAi() {
if (aiBubble) {
aiBubble.classList.remove('streaming');
aiBubble = null; aiText = '';
}
} }
/* ---------- 连接状态 ---------- */ /* ---------- 连接状态 ---------- */
@@ -242,11 +254,14 @@ function routeMessage(data) {
appendReasoning(content); appendReasoning(content);
} else if (t === 'reasoning_end') { } else if (t === 'reasoning_end') {
endReasoning(); endReasoning();
} else if (t === 'ai_break') {
/* 工具调用前分段:结束当前AI文本段(保留已显示内容),后续chunk创建新气泡 */
breakAi();
} else if (t === 'line') { } else if (t === 'line') {
/* 过程行:工具调用提示 / 工具输出 → 任务面板;助手前缀 / 思考提示聊天区已有承载,跳过 */ /* 过程行:工具调用提示 / 工具输出 → 任务面板;助手前缀 / 思考提示等聊天区已有承载,跳过 */
const stripped = content.trim(); const stripped = content.trim();
if (!stripped) return; if (!stripped) return;
if (/^(💬 助手|💬 Assistant|🤔 思考中|🤔 Thinking|🤔 深度思考中)/.test(stripped)) return; if (/^(💬 助手|💬 Assistant|🤔 思考中|🤔 Thinking|🤔 深度思考中|🤔 分析工具结果|🤔 Analyzing|🔧 需要调用工具|🔧 Need to call)/.test(stripped)) return;
addTaskLine(stripped); addTaskLine(stripped);
} else if (t === 'complete') { } else if (t === 'complete') {
finishAi(content); finishAi(content);
@@ -290,7 +305,8 @@ function routeMessage(data) {
} else if (t === 'notification') { } else if (t === 'notification') {
addSystem('🔔 ' + content); addSystem('🔔 ' + content);
} else if (content) { } else if (content) {
appendAi(content); /* 未知类型兜底:按 AI 增量处理(与 BotOps 一致) */ /* 未知消息类型不追加到AI聊天区(避免非AI内容污染回复),记入任务面板 */
addTaskLine('[未知消息 type=' + t + '] ' + content.slice(0, 200));
} }
} }