feat: Dashboard workflow graph visualization (React Flow) #204

Merged
xiaomo merged 1 commits from feat/198-dashboard-workflow-graph into main 2026-05-12 02:28:40 +00:00
Owner

What

在 Dashboard 的 ThreadDetail 页面中嵌入交互式流程图,将 workflow 的 ModeratorTable 可视化为有向图。

Why

  • 用户无法一眼看出 workflow 的角色流转结构和当前执行进度
  • #201 已将 graph 数据嵌入 WorkflowDescriptor,现在可以渲染了

Changes

Backend (cli-workflow/src/commands/serve/routes-workflow.ts)

  • GET /workflows/:name 现在读取 bundles/{hash}.yaml 并返回 descriptor(含 graph)
  • YAML 缺失或无效时 descriptor 为 null

Dashboard API (workflow-dashboard/src/api.ts)

  • 新增 WorkflowGraphEdgeWorkflowGraphWorkflowDescriptor 等类型
  • 新增 getWorkflowDescriptor(agent, name) 函数

流程图组件 (workflow-dashboard/src/components/workflow-graph/)

7 个新文件:

  • types.ts — NodeState、节点/边数据类型
  • use-layout.ts — dagre TB 自动布局 hook,处理自循环边
  • role-node.tsx — 圆角矩形 role 节点,按状态着色 + active 脉冲
  • terminal-node.tsx — START/END 圆形节点
  • condition-edge.tsx — FALLBACK 虚线 / 条件边实线 + label + tooltip
  • workflow-graph.tsx — ReactFlow 容器,fitView、dark mode
  • index.ts — re-exports

集成 (workflow-dashboard/src/components/thread-detail.tsx)

  • 从 thread-start record 提取 workflow name
  • 请求 descriptor → 计算 nodeStates → 渲染可折叠 graph panel (300px)
  • 节点状态:default / completed(绿色✓) / active(蓝色脉冲)

样式 (index.css)

  • 新增 wf-node-pulse keyframe 动画

Ref

Closes #198 (Phase 1)

## What 在 Dashboard 的 ThreadDetail 页面中嵌入交互式流程图,将 workflow 的 ModeratorTable 可视化为有向图。 ## Why - 用户无法一眼看出 workflow 的角色流转结构和当前执行进度 - #201 已将 graph 数据嵌入 WorkflowDescriptor,现在可以渲染了 ## Changes ### Backend (`cli-workflow/src/commands/serve/routes-workflow.ts`) - `GET /workflows/:name` 现在读取 `bundles/{hash}.yaml` 并返回 `descriptor`(含 graph) - YAML 缺失或无效时 descriptor 为 null ### Dashboard API (`workflow-dashboard/src/api.ts`) - 新增 `WorkflowGraphEdge`、`WorkflowGraph`、`WorkflowDescriptor` 等类型 - 新增 `getWorkflowDescriptor(agent, name)` 函数 ### 流程图组件 (`workflow-dashboard/src/components/workflow-graph/`) 7 个新文件: - `types.ts` — NodeState、节点/边数据类型 - `use-layout.ts` — dagre TB 自动布局 hook,处理自循环边 - `role-node.tsx` — 圆角矩形 role 节点,按状态着色 + active 脉冲 - `terminal-node.tsx` — START/END 圆形节点 - `condition-edge.tsx` — FALLBACK 虚线 / 条件边实线 + label + tooltip - `workflow-graph.tsx` — ReactFlow 容器,fitView、dark mode - `index.ts` — re-exports ### 集成 (`workflow-dashboard/src/components/thread-detail.tsx`) - 从 thread-start record 提取 workflow name - 请求 descriptor → 计算 nodeStates → 渲染可折叠 graph panel (300px) - 节点状态:default / completed(绿色✓) / active(蓝色脉冲) ### 样式 (`index.css`) - 新增 `wf-node-pulse` keyframe 动画 ## Ref Closes #198 (Phase 1)
xingyue added 1 commit 2026-05-12 02:27:28 +00:00
Phase 1: API + static graph rendering

Backend:
- GET /workflows/:name now returns descriptor (with graph) from bundle YAML
- Graceful fallback to null if YAML missing/invalid

Frontend:
- New workflow-graph/ component module (7 files)
- React Flow + dagre auto-layout (TB direction)
- Custom nodes: RoleNode (rounded rect) + TerminalNode (circle for START/END)
- Custom edges: dashed for FALLBACK, solid with label for conditions
- Self-loop edges supported (e.g. coder → coder)
- Node states: default/completed/active with color-coded borders
- Active node pulse animation
- Collapsible graph panel (300px) above thread records
- Dark theme using existing CSS variables

Integration:
- ThreadDetail extracts workflow name → fetches descriptor → computes node states → renders graph
- Node states derived from ThreadRecord[] (completed/active/default)
xiaomo approved these changes 2026-05-12 02:28:39 +00:00
xiaomo left a comment
Owner

Phase 1 可视化实现得很完整

亮点:

  • 组件拆分清晰:7 个文件各司其职,types / layout hook / node / edge / container 层次分明
  • dagre 自动布局 + multigraph 支持自循环边,e.from === e.to 跳过 dagre 但保留 React Flow 渲染
  • 节点状态机 computeNodeStates 逻辑正确:最后一个 role 无 result 时 active,有 result 全部 completed
  • FALLBACK 虚线 vs 条件实线 + label + tooltip 区分度好
  • 可折叠面板不占固定空间,300px 高度合理
  • Backend 的 descriptor 读取优雅降级(YAML 缺失/无效 → null)
  • getWorkflowDescriptor.then() 解包——虽然项目约定 async/await,但这里是单行 transform 还行

两个小 nit(不阻塞):

  1. api.tsgetWorkflowDescriptor 用了 .then() 链,项目约定 async/await。改成 async 函数更一致
  2. useLayout 的 useMemo deps 是 [input.edges, input.roles, input.nodeStates]——Map 对象每次 render 都是新引用,会导致 useMemo 失效。如果 computeNodeStates 返回的 Map 没 memo 住,layout 会每次重算。当前 computeNodeStatesuseMemo 里所以应该没问题,但值得留意

LGTM 🚀

Phase 1 可视化实现得很完整 ✅ **亮点:** - 组件拆分清晰:7 个文件各司其职,types / layout hook / node / edge / container 层次分明 - dagre 自动布局 + multigraph 支持自循环边,`e.from === e.to` 跳过 dagre 但保留 React Flow 渲染 - 节点状态机 `computeNodeStates` 逻辑正确:最后一个 role 无 result 时 active,有 result 全部 completed - FALLBACK 虚线 vs 条件实线 + label + tooltip 区分度好 - 可折叠面板不占固定空间,300px 高度合理 - Backend 的 descriptor 读取优雅降级(YAML 缺失/无效 → null) - `getWorkflowDescriptor` 用 `.then()` 解包——虽然项目约定 async/await,但这里是单行 transform 还行 **两个小 nit(不阻塞):** 1. `api.ts` 里 `getWorkflowDescriptor` 用了 `.then()` 链,项目约定 async/await。改成 `async` 函数更一致 2. `useLayout` 的 useMemo deps 是 `[input.edges, input.roles, input.nodeStates]`——Map 对象每次 render 都是新引用,会导致 useMemo 失效。如果 `computeNodeStates` 返回的 Map 没 memo 住,layout 会每次重算。当前 `computeNodeStates` 在 `useMemo` 里所以应该没问题,但值得留意 LGTM 🚀
xiaomo merged commit 81a7a8c7c1 into main 2026-05-12 02:28:40 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#204