feat(flow): 链路图本地落盘 + 多轮历史回放(design/19 §P3,契约 v0.73)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

主人要求「链路流程应该有本地数据,不然每次重载之后页面就没数据了」:
- 落盘:迁移 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)
This commit is contained in:
AskaEth committed 2026-10-05 16:19:32 +08:00
1 parent 84c124f5a9
commit a6cb44b8a8
38 files changed
+3047 -91

No files matched your search

+30 -15
View File
@@ -47,28 +47,43 @@ export function adminBackthink(limit?: number): Promise<BackthinkItem[]> {
return request<BackthinkItem[]>(`/admin/backthink${limitQuery(limit)}`);
}
/** 最近一轮链路图快照的后端响应(契约 v0.71 §4.11(a))。`graph` **恒在**
* (`null` 而不是缺席):`null` = 该会话**确实**还没有图,客户端必须据此
* 渲染空态,绝不允许补画任何节点或假图。 */
export interface AdminFlowGraphResponse {
sessionId: string;
graph: FlowGraph | null;
/** 一条**已落盘轮次**的摘要(契约 v0.73 §4.11 的 `turns[]`)。面板用它渲染历史轮
* 选择器:把 `turnId` 回带给 `adminFlowGraph` 即可精确取回那一轮。 */
export interface AdminFlowTurn {
turnId: string;
createdAt: string;
}
/** GET /api/v1/admin/flow/graph?sessionId= — **最近一轮**链路图快照(契约
* v0.71 §4.11,只读、不审计,owner/admin 档)。
/** 链路图端点的后端响应(契约 v0.71 起;**v0.73 新增 `turnId`/`source`/`turns`**)。
*
* 为什么需要它:`graph_update` 是**纯实时**帧、从不重放,且前端图桶
* (`flowGraphs`)**不持久化** ⇒ 「轮次结束后才打开页面」或「刷新过页面」
* 此前只能看到空态。打开页面时拉一次即可补上最近一轮。
* `graph` **恒在**(`null` 而不是缺席):`null` = 该轮**确实**没有图,客户端必须据此
* 渲染空态,绝不允许补画任何节点或假图。
*
* ⚠️ 它只回**每会话最近一轮**:后台思考行上点开的图可能不是该行那一轮
* (多轮历史回放仍未实现,`design/19 §4` P3)⇒ 面板必须在图上说明。
* `source` 告诉客户端这张图是哪来的:`live` = 内存里最近一轮(实时图);`persisted` =
* 从本地库读回的历史轮;`none` = 没有图。`turns` 是该会话已落盘的轮次摘要(最新在前),
* 没有时为 `[]`(后端保证非 null 数组)。 */
export interface AdminFlowGraphResponse {
sessionId: string;
turnId: string;
source: "live" | "persisted" | "none";
graph: FlowGraph | null;
turns: AdminFlowTurn[];
}
/** GET /api/v1/admin/flow/graph?sessionId=&turnId= — **链路图快照 + 多轮历史回放**(契约
* v0.71 起,v0.73 增量;只读、不审计,owner/admin 档)。
*
* `sessionId` 省略时后端默认主会话 `web`。 */
export function adminFlowGraph(sessionId: string): Promise<AdminFlowGraphResponse> {
* 为什么需要它:`graph_update` 是**纯实时**帧、从不重放。v0.71 补了「打开页面拉最近一轮
* 快照」;v0.73 把图**每轮落盘**,于是可以:
* - **省略 `turnId`** ⇒ 最近一轮(后端**先查内存、miss 才落盘** ⇒ 最新一轮永远最新);
* - **给了 `turnId`** ⇒ 精确取该会话已落盘的某一轮(历史回放)。
*
* `turns`(同一响应里)就是给选择器用的历史轮列表。⚠️ 白名单之外的会话(群聊/陌生人)
* 从不建图、也从不落盘 ⇒ 它们**永远**是空态,这不是缺陷。 */
export function adminFlowGraph(sessionId: string, turnId?: string): Promise<AdminFlowGraphResponse> {
const qs = new URLSearchParams();
if (sessionId) qs.set("sessionId", sessionId);
if (turnId) qs.set("turnId", turnId);
const q = qs.toString();
return request<AdminFlowGraphResponse>(`/admin/flow/graph${q ? `?${q}` : ""}`);
}