diff --git a/docs/todo.md b/docs/todo.md index f58bf95..1b2c81e 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -735,3 +735,6 @@ 902. [ ] 超时失败后的一键继续入口: 流式首字节超时(120s,服务收了不响应,不可自动重试)报「响应超时,请重试」后流程中断——轮次重入预算耗尽(3/3)即彻底停,用户只见裸文案。修:超时失败落停靠语义(复用 emit_task_parked),停靠卡带「继续」(ai_continue_loop parked 分流已修好)——错误后一键续跑而非中断。体验缺口实证 2026-10-07 晚 u-switch 故障窗口。 903. [ ] u-switch 20:38-21:02 故障窗口根因深查: 11 连败+120s 首字节超时风暴(多会话多 provider 同时),21:0x 自愈。查 moyu-ops 机 u-switch 日志定因(上游抖动/服务重启/连接池耗尽),根治或加监控告警。**【21:5x AI-Ops 定因已实锤,journalctl 实测】上游连环降级雪崩非服务故障:api.deepseek.com 402 余额不足(冷却10m)+enterprise.moyu.info 401 认证失效(冷却10m)+sensenova kimi-k3 429/SSE空响应体(30s冷却循环)+bigmodel glm 429/SSE截断缺 message_stop——多通道同时打光致无首字节,冷却走完自愈。剩余工作=根治项:①switch-gw 的 deepseek 系 4 通道确认摘除(用户指认已摘但配置仍在役)②429 风暴时客户端退避协同(30s 冷却对重试风暴无效)③故障窗口告警。**(10-08 已处置:moyu 令牌过期续期+主链切 ds-flash,见 900 区段外 905) + +911. [x] **「流式后气泡被清空」真根因(2026-10-10 定位并修复,历次修补无效)**:用户现象=正文流式进气泡 → 轮末气泡清空回到「等待」,每轮工具后都发生、且文本「彻底丢」。**根因(实测定位)**:`MessageList.vue` `renderItems` 的**完成轮分桶**把整轮(含模型正文)折进「执行轨迹卡」且**默认收起**(`flushBucket` 只把最后一个有正文的消息当 finalMsg 出气泡,其余全进 traceMsgs);叠加 `thinkPlainText` 取 `reasoning_content || content` 把正文也当「思考」塞进单行 pill。故流式期间正文可见(currentText 直渲),轮末消息转轨迹卡即「消失」。**修复**:①过程轮里**有正文**的各成一个常显气泡(剥 `toolCalls`,工具卡仍归轨迹卡不重复);②`thinkPlainText` 只认 `reasoning_content`(思考 pill 不再吞正文)。**教训(必须留痕)**:此前 4-5 次修复(`40c4a21` 闪烁/`4652112` 迟到收尾仲裁/`524120b` 切会话残片/`BUG-260624-01` 重叠/`flushCurrentText` guard)全在**状态清空/骨架层**,从未碰**渲染折叠/分桶层** → 故反复不好;凡「流式后内容消失」类投诉,第一嫌疑应是**渲染折叠/分桶**(`renderItems`/trace 桶),而非 streaming/currentText 状态。 + diff --git a/src/components/ai/MessageList.vue b/src/components/ai/MessageList.vue index 91f42d6..19cf885 100644 --- a/src/components/ai/MessageList.vue +++ b/src/components/ai/MessageList.vue @@ -946,7 +946,9 @@ const { ids: expandedThinkIds, toggle: toggleThink } = useToggleSet() /** 轮次思考纯文本:reasoning_content 优先(DeepSeek/Anthropic thinking 同通道),content 兜底; * 压平 markdown 换行;两者皆空回退「过程记录」占位(纯工具轮也有锚点) */ function thinkPlainText(m: AiMessage): string { - const src = m.reasoning_content || m.content || '' + // 只承载「推理内容」(正文不折叠,2026-10-10):原 `reasoning_content || content` 会把模型正文 + // 也当思考吞进单行 pill——正文改由常显气泡渲染(见 renderItems),此处只认 reasoning_content。 + const src = m.reasoning_content || '' return src.replace(/\s+/g, ' ').trim() } /** pill 单行文本:纯文本截断 + 空轮回退文案(title 承载全文,此处只管单行) */ @@ -1126,6 +1128,16 @@ const renderItems = computed(() => { } else { pushTimeSepIfNeeded(items, finalMsg.timestamp) } + // 正文不折叠(2026-10-10 根因修复):此前整轮(含模型正文)折进轨迹卡默认收起 → 流式期间 + // 能看到正文、轮末正文随卡收起,用户表象为「气泡被清空/进入等待」,历次在 flush/清空/骨架层 + // 修补均无效。现把过程消息里**有正文**的各成一个常显气泡(剥掉 toolCalls——工具卡仍归轨迹卡, + // 不重复渲染),轨迹卡只折叠工具步骤与思考。 + for (const m of traceMsgs) { + const c = m.content + if (m.role === 'assistant' && c && c.trim() && !isToolResultJson(c)) { + items.push({ kind: 'msg', key: 'm-' + m.id, msg: { ...m, toolCalls: undefined } }) + } + } items.push({ kind: 'msg', key: 'm-' + finalMsg.id, msg: finalMsg, final: true }) lastMsgTs = finalMsg.timestamp }