Files
Cyrene_v2/frontend/web/src/admin/modules/flow/FlowSnapshotHint.tsx
T
AskaEth a6cb44b8a8
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(flow): 链路图本地落盘 + 多轮历史回放(design/19 §P3,契约 v0.73)
主人要求「链路流程应该有本地数据,不然每次重载之后页面就没数据了」:
- 落盘:迁移 0022_flow_graphs(session_id/turn_id/graph_json/时间戳 + 唯一键 upsert)+ internal/runtime/flowgraph(RecordRound/Graph/Latest/Turns/Turn + 保留期 GC)
- 写入:loop.EndRound **每轮一条**(锁外深拷贝后写;失败 WARN 且不打印图内容);只落发布会话白名单(复用 flowSessionAllowed)
- 读回:端点加 turnId + source(live/persisted/none) + turns[](最新在前,空为 []);**内存优先、miss 才落盘**(最新一轮永远最新)
- 前端:flowGraphs 纳入 persistOptions.partialize(**刷新即刻有图**,version 3→4 + migrate)+ 历史轮选择器 + 诚实文案;顺带收 L1 转交的 flow/NodeDetail.tsx 节点日志倒序
- 新 env 键:FLOW_GRAPH_RETENTION=168h / FLOW_GRAPH_GC_INTERVAL=1h
- 变异实测:每 span 一行 / 保留期守卫失效 / partialize 去掉 flowGraphs / 去掉内存优先 / 白名单失效 ⇒ 全部打红
- 3 个禁动面文件的纯追加改动(Flow.tsx 挂选择器、FlowSnapshotHint 纠已成假话的文案、其渲染断言随之更新)由主会话裁定认可
- 登记存疑:图桶持久化无体积上限;useFlowTurns 的 effect 无自动化证据(项目无 jsdom)
2026-10-05 16:19:32 +08:00

51 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { BACKTHINK_SESSION_ID } from "../../../store/sessionCache";
/** 会话 id → 人话标签(图上「这是哪个会话的图」必须看得懂)。未知会话
* 原样显示 id,绝不猜一个更好听的名字。 */
function sessionLabel(sessionId: string): string {
if (sessionId === BACKTHINK_SESSION_ID) return "后台思考会话";
if (sessionId === "web") return "Web 主会话";
return sessionId;
}
/** 链路图快照的诚实说明条(契约 v0.71 §4.11(b)-1)。
*
* 为什么必须有它:后端只保留**每个会话最近一轮**的图,而补拉通路的用途正是
* 「轮次结束后才打开页面」。于是图上画的到底是哪一种来源必须讲清:
* - `snapshot` = 打开页面时**只读拉取**的那一份最近一轮(不是实时帧,也**不一定
* 是你眼前这一行那一轮** —— 后台思考行尤其如此);
* - `live` = 这次页面存活期间**实时收到**的图(正在跑 / 刚跑完的那一轮)。
*
* 不写这一条就等于让用户以为「这行思考有自己的图」,那是把推导当事实:
* 多轮历史回放仍未实现(`design/19 §4` P3)。 */
export function FlowSnapshotHint({
sessionId,
source,
}: {
sessionId: string;
source: "live" | "snapshot";
}): React.ReactNode {
const label = sessionLabel(sessionId);
return (
<div className="rounded-[var(--mu-radius)] border border-dashed border-[var(--mu-glass-border)] px-3 py-2 text-xs text-[var(--mu-text-lo)]">
{source === "snapshot" ? (
<>
以上是 <span className="text-[var(--mu-text-mid)]">{label}</span>(
<code>{sessionId}</code>)的
<span className="text-[var(--mu-text-mid)]">最近一轮</span>
链路图快照 —— 打开本页时只读拉取(<code>GET /api/v1/admin/flow/graph</code>)。
它不一定是你要找的那一轮 —— 这是**最新**的一份。要精确看某一轮,用上方的
「历史轮次」选择器(那些轮已落盘,刷新/重启后仍在;契约 v0.73)。
</>
) : (
<>
以上是 <span className="text-[var(--mu-text-mid)]">{label}</span>(
<code>{sessionId}</code>)在本次页面存活期间
<span className="text-[var(--mu-text-mid)]">实时收到</span>
的链路图(正在跑或最近跑完的那一轮)。刷新页面后它由「最近一轮快照」补上。
</>
)}
</div>
);
}