fix(web): 跳秒指示覆盖刷新重连 + 发送后空窗(bump 0.43.1)

- 刷新/重连接管:phaseSince 随页面内存丢失,原先要等下一个 llm_start/
  tool_call 才恢复跳秒——恰是用户因疑似卡死而刷新的场景;重建直播卡时
  立即按"思考中"起跳,后续阶段事件校正(秒数从重连时刻起,非真实累计)
- 发消息 POST 返回即起跳:覆盖 build_agent + 首轮 TTFT,原先等 llm_start
  才开始计时

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-07 09:16:00 +08:00
parent 463e282afd
commit 50e1d57ed5
3 changed files with 8 additions and 2 deletions

View File

@ -2,7 +2,7 @@
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9` > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`
最后更新:2026-07-07(run 长耗时可视化:reasoning 事件流 + 阶段跳秒指示,bump 0.43.0) 最后更新:2026-07-07(跳秒指示覆盖刷新重连 + 发送后空窗,bump 0.43.1)
--- ---
@ -23,6 +23,7 @@
### 2026-07 ### 2026-07
- **07-07 / 0.43.1**:跳秒指示补两处空窗——① 刷新/重连接管后 phaseSince 随页面内存丢失,退回静态"思考中"不跳秒(恰是用户因疑似卡死而刷新的场景),重建直播卡时立即按"思考中"起跳,下一个阶段事件校正;② 发消息 POST 返回即起跳,覆盖 build_agent + 首轮 TTFT(原先要等 llm_start 才开始计时)。重连后秒数从重连时刻起算(非真实累计),可接受。
- **07-07 / 0.43.0**:run 长耗时可视化——修"前端一直静止显示思考中被当成卡死":① 新增 SSE `reasoning{delta}` 事件(loop 提取 `delta.reasoning_content`,thinking 模型分钟级推理原先整段丢弃、前端零反馈),前端渲染灰色折叠"思考过程"卡,历史消息里持久化的 reasoning_content 同款渲染;② 前端活跃状态指示:TTFT/推理期占位段每秒跳"思考中/深度思考中 · Ns"(CSS attr(data-status)),工具卡运行中加 spinner + 跳秒、结果到达定格为执行耗时;每轮 llm_start 重建占位段,工具轮之后不再空窗。 - **07-07 / 0.43.0**:run 长耗时可视化——修"前端一直静止显示思考中被当成卡死":① 新增 SSE `reasoning{delta}` 事件(loop 提取 `delta.reasoning_content`,thinking 模型分钟级推理原先整段丢弃、前端零反馈),前端渲染灰色折叠"思考过程"卡,历史消息里持久化的 reasoning_content 同款渲染;② 前端活跃状态指示:TTFT/推理期占位段每秒跳"思考中/深度思考中 · Ns"(CSS attr(data-status)),工具卡运行中加 spinner + 跳秒、结果到达定格为执行耗时;每轮 llm_start 重建占位段,工具轮之后不再空窗。
- **07-06 / 0.42.5**:497 跳转示例 301→308——平台走 http 入口 POST login 被客户端按 301 语义降级成 GET 报 405;308 保留方法/body。治本仍是调用方直配 https(明文首跳暴露 platform_key)。 - **07-06 / 0.42.5**:497 跳转示例 301→308——平台走 http 入口 POST login 被客户端按 301 语义降级成 GET 报 405;308 保留方法/body。治本仍是调用方直配 https(明文首跳暴露 platform_key)。
- **07-06 / 0.42.4**:497 跳转目标示例改写死域名(http://IP 进来被带回域名无证书警告;https://IP 直连警告 nginx 层无解,入口统一域名),进故障兜底。 - **07-06 / 0.42.4**:497 跳转目标示例改写死域名(http://IP 进来被带回域名无证书警告;https://IP 直连警告 nginx 层无解,入口统一域名),进故障兜底。

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.43.0" __version__ = "0.43.1"

View File

@ -943,6 +943,10 @@ function createLiveAssistantCard(run) {
} else { } else {
ensureTextSeg(run); // 空占位段:首字到达前显示「思考中」 ensureTextSeg(run); // 空占位段:首字到达前显示「思考中」
} }
// 刷新/重连接管:phaseSince 随页面内存丢了,阶段未知先按"思考中"起跳
// (计时从重连时刻起,非真实累计);下一个 llm_start/tool_call 事件会校正。
// 不跳秒的静态"思考中"恰是用户刷新的诱因,重连后必须立刻恢复活跃信号。
if (!run.phaseSince) setRunPhase(run, run.phase || "llm");
return card; return card;
} }
@ -1540,6 +1544,7 @@ async function sendMessage(overrideText) {
}; };
// 预建的空占位 .body 即首个文字段(首字到达前显示「思考中」) // 预建的空占位 .body 即首个文字段(首字到达前显示「思考中」)
run.curSeg = { el: asstCard.querySelector(".body"), acc: "", pending: false }; run.curSeg = { el: asstCard.querySelector(".body"), acc: "", pending: false };
setRunPhase(run, "llm"); // POST 返回即起跳:覆盖 build_agent + 首轮 TTFT 的空窗
state.liveRuns.set(taskId, run); state.liveRuns.set(taskId, run);
state.streaming = true; state.streaming = true;
syncTaskRowRunIndicator(taskId); syncTaskRowRunIndicator(taskId);