Files
sundynix-agentix/sundynix-desktop/frontend/src/lib/run.ts
T
Blizzard cdc5b3a847 feat(observability): 执行可视化 — 节点级实时轨迹(运行·观测)
把任务执行做成可观测:Dispatcher 在每个节点/阶段发结构化 ExecEvent,
经独立 NATS 通道回流,前端逐节点点亮(状态/耗时/工具入参产出)。

- shared: contract.ExecEvent + ExecSubject(sundynix.exec.<id>,与 Token 流分流);
  bus.PublishExec/CompleteExec/SubscribeExec(core NATS,复用结束头)
- dispatcher: execTracer(自增 Seq 保序 + span 自动计耗时);
  Orchestrator 加 ExecSink;通用图(init 召回 / 各 tool 入参→产出 / prompt / model
  首token+token数)与报告编排(规划大纲 / 各章并行 start-end / 渲染)全程埋点
- gateway: SubscribeExec + GET /tasks/:id/exec SSE(与 token 流并行)
- desktop: streamExec + deriveNodes(按 node 归并 start/end/error/info);
  复用组件 ExecTrace(竖向轨道,按 kind 着色,运行中脉冲灯);
  新 RunsView(运行·观测:轨迹+输出双栏);BottomDrawer 轨迹/工具调用 tab 接真实数据;
  ReportView 加执行轨迹栏;左导航「运行」置就绪

实测:
- 报告任务 /exec:规划(2680ms,4章) → 4 章并行(seq 交错,各~7-8s 重叠=真并行,
  每章带 docs 知识库检索预览+成稿字数) → 渲染(docx 落盘)
- 通用图 /exec:tool:kb_search(678ms,入参→Milvus 产出) → prompt(2消息) →
  model(首token 860ms / 4 tokens)
- 浏览器(Preview):报告页执行轨迹逐节点点亮、章节带耗时/字数/检索片段,完成后下载 Word

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 14:29:28 +08:00

71 lines
2.1 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.
// 运行状态 —— 跨 Studio 与底部抽屉共享。
import type { ExecEvent } from "./api";
export type RunPhase = "idle" | "submitting" | "streaming" | "done" | "error";
export interface RunEvent {
t: number; // 相对开始的毫秒
label: string;
}
export interface RunState {
phase: RunPhase;
taskId?: string;
output: string;
events: RunEvent[];
exec: ExecEvent[]; // 后端回流的节点级执行轨迹(运行·观测)
error?: string;
}
export const emptyRun: RunState = { phase: "idle", output: "", events: [], exec: [] };
// ---- 执行轨迹派生:把扁平 ExecEvent 流归并为按节点聚合的轨迹 ----
export type NodeStatus = "running" | "done" | "error" | "info";
export interface NodeTrace {
node: string;
kind: string; // memory|tool|prompt|model|plan|section|render|system
label: string;
status: NodeStatus;
ms?: number;
detail?: string;
notes: string[]; // info 子事件(如检索到的参考资料预览)
order: number;
}
// deriveNodes 把事件流按 node 归并:start→runningend→done(带耗时)error→errorinfo→点事件/附注。
export function deriveNodes(events: ExecEvent[]): NodeTrace[] {
const map = new Map<string, NodeTrace>();
let order = 0;
for (const e of events) {
let n = map.get(e.node);
if (!n) {
n = { node: e.node, kind: e.kind, label: e.label, status: "info", notes: [], order: order++ };
map.set(e.node, n);
}
if (e.label) n.label = e.label;
n.kind = e.kind;
switch (e.phase) {
case "start":
if (n.status !== "done" && n.status !== "error") n.status = "running";
break;
case "end":
n.status = "done";
n.ms = e.ms;
if (e.detail) n.detail = e.detail;
break;
case "error":
n.status = "error";
n.ms = e.ms;
if (e.detail) n.detail = e.detail;
break;
case "info":
if (e.detail) n.notes.push(e.detail);
else if (e.label) n.notes.push(e.label);
break;
}
}
return [...map.values()].sort((a, b) => a.order - b.order);
}