Summary
Build the serialization layer that converts between React Flow graph state (nodes, edges, positions) and the workflow REST API format (CreateWorkflowRequest / WorkflowResponse). This enables saving canvas compositions as real workflows and loading existing workflows back into the canvas.
Context
The workflow API is designed around individual workflows: one trigger config, one agent, one prompt template. The visual canvas represents multiple workflows as a connected graph. The serialization layer must bridge these two models:
- Save (graph to API): Each trigger-to-agent edge in the graph becomes one
CreateWorkflowRequest
- Load (API to graph): Each
WorkflowResponse becomes a trigger node + agent node + edge, with layout positions reconstructed or auto-generated
Data model mapping
Canvas Graph REST API
----------- --------
TriggerNode --PromptEdge--> AgentNode = WorkflowConfig {
triggerConfig trigger_config: TriggerConfig,
promptTemplate prompt_template: String,
pollIntervalSecs poll_interval_secs: u64,
agentId agent_id: Uuid,
name: String,
enabled: bool,
tool_policy: ToolPolicy,
}
Implementation Details
1. Graph-to-API serializer
Create ui/src/components/workflows/canvas/serialization.ts:
/**
* Convert React Flow graph state into an array of workflow API requests.
* Each trigger->agent edge becomes one CreateWorkflowRequest.
*/
export function graphToWorkflows(
nodes: Node<TriggerNodeData | AgentNodeData>[],
edges: Edge<PromptEdgeData>[],
): CreateWorkflowRequest[];
/**
* Convert a set of workflow API responses into React Flow graph state.
* Reconstructs nodes, edges, and layout positions.
*/
export function workflowsToGraph(
workflows: Workflow[],
agents: Agent[],
): { nodes: Node[]; edges: Edge[] };
2. Serialization rules
Graph to API:
- Each edge with a trigger source and agent target creates one
CreateWorkflowRequest
- Workflow
name derived from: "[trigger_type]-to-[agent_name]" (user can override)
trigger_config from trigger node data
agent_id from agent node data
prompt_template from edge data
poll_interval_secs from edge data
enabled from trigger node data
tool_policy defaults to agent's policy (can be overridden)
- Validation: reject graphs with unconnected nodes, missing required config fields
API to Graph:
- Group workflows by
agent_id to deduplicate agent nodes
- Create one trigger node per workflow
- Create one agent node per unique
agent_id
- Create one edge per workflow connecting its trigger to its agent
- Populate edge data with
prompt_template and poll_interval_secs
- Generate layout positions (simple grid or dagre layout)
3. Position metadata
Store canvas layout positions as part of the serialized state:
interface CanvasLayout {
nodes: Record<string, { x: number; y: number }>; // nodeId -> position
viewport: { x: number; y: number; zoom: number };
}
Since the workflow API doesn't have a layout field, positions can be:
- Stored in localStorage keyed by a hash of workflow IDs
- Or auto-generated on load using a simple layout algorithm
4. Validation
export interface SerializationError {
type: "unconnected_trigger" | "unconnected_agent" | "missing_config" | "invalid_edge";
nodeId?: string;
edgeId?: string;
message: string;
}
export function validateGraph(
nodes: Node[],
edges: Edge[],
): SerializationError[];
5. Tests
- Round-trip test: create graph -> serialize -> deserialize -> compare
- Single workflow serialization
- Multi-workflow graph with shared agent
- Validation catches unconnected nodes
- Validation catches missing required trigger config fields
- Edge cases: empty graph, single node, cycles
Files to Create
ui/src/components/workflows/canvas/serialization.ts
ui/src/components/workflows/canvas/serialization.test.ts
ui/src/components/workflows/canvas/validation.ts (optional, can be in serialization.ts)
Acceptance Criteria
Blocked By
Stack Base
Stack on: feature/autonomous-pipeline after #1096, #1097, and #1098 merge.
Summary
Build the serialization layer that converts between React Flow graph state (nodes, edges, positions) and the workflow REST API format (
CreateWorkflowRequest/WorkflowResponse). This enables saving canvas compositions as real workflows and loading existing workflows back into the canvas.Context
The workflow API is designed around individual workflows: one trigger config, one agent, one prompt template. The visual canvas represents multiple workflows as a connected graph. The serialization layer must bridge these two models:
CreateWorkflowRequestWorkflowResponsebecomes a trigger node + agent node + edge, with layout positions reconstructed or auto-generatedData model mapping
Implementation Details
1. Graph-to-API serializer
Create
ui/src/components/workflows/canvas/serialization.ts:2. Serialization rules
Graph to API:
CreateWorkflowRequestnamederived from:"[trigger_type]-to-[agent_name]"(user can override)trigger_configfrom trigger node dataagent_idfrom agent node dataprompt_templatefrom edge datapoll_interval_secsfrom edge dataenabledfrom trigger node datatool_policydefaults to agent's policy (can be overridden)API to Graph:
agent_idto deduplicate agent nodesagent_idprompt_templateandpoll_interval_secs3. Position metadata
Store canvas layout positions as part of the serialized state:
Since the workflow API doesn't have a layout field, positions can be:
4. Validation
5. Tests
Files to Create
ui/src/components/workflows/canvas/serialization.tsui/src/components/workflows/canvas/serialization.test.tsui/src/components/workflows/canvas/validation.ts(optional, can be in serialization.ts)Acceptance Criteria
graphToWorkflows()converts canvas graph to validCreateWorkflowRequest[]workflowsToGraph()convertsWorkflow[]to React Flow nodes/edgesBlocked By
Stack Base
Stack on:
feature/autonomous-pipelineafter #1096, #1097, and #1098 merge.