llama1's picture
Upload 781 files
5da4770 verified
import { Node, Edge } from '@xyflow/react';
import { ConditionalStep } from '@/components/agents/workflows/conditional-workflow-builder';
import { uuid } from '../utils';
interface ConversionResult {
nodes: Node[];
edges: Edge[];
}
export function convertWorkflowToReactFlow(steps: ConditionalStep[]): ConversionResult {
const nodes: Node[] = [];
const edges: Edge[] = [];
if (steps.length === 0) {
return { nodes, edges };
}
const processSteps = (
stepList: ConditionalStep[],
parentId: string | null = null,
yOffset: number = 0,
xOffset: number = 0
): { nodes: Node[]; edges: Edge[]; lastNodes: string[]; maxY: number } => {
let previousNodeId = parentId;
let currentY = yOffset;
let lastNodes: string[] = [];
const localNodes: Node[] = [];
const localEdges: Edge[] = [];
for (let i = 0; i < stepList.length; i++) {
const step = stepList[i];
if (step.type === 'condition') {
const conditionGroup: ConditionalStep[] = [];
let j = i;
while (j < stepList.length && stepList[j].type === 'condition') {
conditionGroup.push(stepList[j]);
j++;
}
const branchResult = processConditionBranches(
conditionGroup,
previousNodeId,
currentY,
xOffset
);
localNodes.push(...branchResult.nodes);
localEdges.push(...branchResult.edges);
nodes.push(...branchResult.nodes);
edges.push(...branchResult.edges);
currentY = branchResult.maxY;
lastNodes = branchResult.lastNodes;
i = j - 1;
} else {
const nodeId = step.id || uuid();
const node: Node = {
id: nodeId,
type: 'step',
position: { x: xOffset, y: currentY },
data: {
name: step.name,
description: step.description,
hasIssues: step.hasIssues,
tool: step.config?.tool_name,
},
};
localNodes.push(node);
nodes.push(node);
if (previousNodeId) {
const edgeId = `${previousNodeId}->${nodeId}`;
if (!edges.find(e => e.id === edgeId)) {
const edge = {
id: edgeId,
source: previousNodeId,
target: nodeId,
type: 'workflow',
};
localEdges.push(edge);
edges.push(edge);
}
}
if (step.children && step.children.length > 0) {
const conditionChildren = step.children.filter(child => child.type === 'condition');
const regularChildren = step.children.filter(child => child.type !== 'condition');
if (conditionChildren.length > 0) {
const branchResult = processConditionBranches(
conditionChildren,
nodeId,
currentY + 120,
xOffset
);
localNodes.push(...branchResult.nodes);
localEdges.push(...branchResult.edges);
nodes.push(...branchResult.nodes);
edges.push(...branchResult.edges);
currentY = branchResult.maxY;
lastNodes = branchResult.lastNodes;
} else if (regularChildren.length > 0) {
const childResult = processSteps(
regularChildren,
nodeId,
currentY + 150,
xOffset
);
localNodes.push(...childResult.nodes);
localEdges.push(...childResult.edges);
nodes.push(...childResult.nodes);
edges.push(...childResult.edges);
currentY = childResult.maxY;
lastNodes = childResult.lastNodes;
} else {
previousNodeId = nodeId;
lastNodes = [nodeId];
currentY += 120;
}
} else {
previousNodeId = nodeId;
lastNodes = [nodeId];
currentY += 120;
}
}
}
return { nodes: localNodes, edges: localEdges, lastNodes, maxY: currentY };
};
const processConditionBranches = (
conditions: ConditionalStep[],
parentId: string | null,
yOffset: number,
xOffset: number
): { nodes: Node[]; edges: Edge[]; lastNodes: string[]; maxY: number } => {
const branchNodes: Node[] = [];
const branchEdges: Edge[] = [];
const branchLastNodes: string[] = [];
let maxY = yOffset;
const branchCount = conditions.length;
const xSpacing = 200;
const startX = xOffset - ((branchCount - 1) * xSpacing / 2);
conditions.forEach((condition, index) => {
const conditionNodeId = condition.id || uuid();
const branchXPos = startX + index * xSpacing;
const conditionNode: Node = {
id: conditionNodeId,
type: 'condition',
position: { x: branchXPos, y: yOffset + 100 },
data: {
conditionType: condition.conditions?.type || 'if',
expression: condition.conditions?.expression || '',
},
};
branchNodes.push(conditionNode);
if (parentId) {
const edgeId = `${parentId}->${conditionNodeId}`;
if (!edges.find(e => e.id === edgeId)) {
const edge = {
id: edgeId,
source: parentId,
target: conditionNodeId,
type: 'workflow',
label: condition.conditions?.type === 'if' ? 'if' :
condition.conditions?.type === 'elseif' ? 'else if' : 'else',
labelStyle: { fill: '#666', fontSize: 12 },
labelBgStyle: { fill: '#fff' },
};
branchEdges.push(edge);
edges.push(edge);
}
}
if (condition.children && condition.children.length > 0) {
const childResult = processSteps(
condition.children,
conditionNodeId,
yOffset + 180,
branchXPos
);
branchNodes.push(...childResult.nodes);
branchEdges.push(...childResult.edges);
branchLastNodes.push(...childResult.lastNodes);
maxY = Math.max(maxY, childResult.maxY);
} else {
branchLastNodes.push(conditionNodeId);
maxY = Math.max(maxY, yOffset + 180);
}
});
return {
nodes: branchNodes,
edges: branchEdges,
lastNodes: branchLastNodes,
maxY,
};
};
const result = processSteps(steps, null, 0, 0);
const uniqueEdges = edges.filter((edge, index, self) =>
index === self.findIndex(e => e.id === edge.id)
);
return { nodes, edges: uniqueEdges };
}
export function convertReactFlowToWorkflow(nodes: Node[], edges: Edge[]): ConditionalStep[] {
if (nodes.length === 0) {
return [];
}
const childrenMap = new Map<string, Node[]>();
const parentMap = new Map<string, string>();
const edgeLabels = new Map<string, string>();
nodes.forEach(node => {
childrenMap.set(node.id, []);
});
edges.forEach(edge => {
const children = childrenMap.get(edge.source) || [];
const targetNode = nodes.find(n => n.id === edge.target);
if (targetNode) {
children.push(targetNode);
parentMap.set(edge.target, edge.source);
if (edge.label) {
edgeLabels.set(edge.target, String(edge.label));
}
}
});
const rootNode = nodes.find(node => !parentMap.has(node.id));
if (!rootNode) {
return [];
}
const visited = new Set<string>();
const convertNode = (node: Node): ConditionalStep => {
visited.add(node.id);
const nodeData = node.data as any;
const children = childrenMap.get(node.id) || [];
if (node.type === 'step') {
const step: ConditionalStep = {
id: node.id,
name: nodeData.name || 'Unnamed Step',
description: nodeData.description || '',
type: 'instruction',
config: nodeData.tool ? { tool_name: nodeData.tool } : {},
order: 0,
enabled: true,
hasIssues: nodeData.hasIssues || false,
children: []
};
const conditionChildren = children.filter(child => child.type === 'condition');
const regularChildren = children.filter(child => child.type !== 'condition');
conditionChildren.forEach(conditionChild => {
if (!visited.has(conditionChild.id)) {
const conditionStep = convertNode(conditionChild);
step.children!.push(conditionStep);
}
});
return step;
} else if (node.type === 'condition') {
const edgeLabel = edgeLabels.get(node.id) || 'Condition';
const step: ConditionalStep = {
id: node.id,
name: edgeLabel,
description: '',
type: 'condition',
config: {},
conditions: {
type: nodeData.conditionType || 'if',
expression: nodeData.expression || '',
},
order: 0,
enabled: true,
hasIssues: false,
children: []
};
children.forEach(child => {
if (!visited.has(child.id)) {
const processSequentialChain = (startNode: Node): ConditionalStep[] => {
const chainSteps: ConditionalStep[] = [];
let currentNode = startNode;
while (currentNode && !visited.has(currentNode.id)) {
const chainStep = convertNode(currentNode);
chainSteps.push(chainStep);
const nodeChildren = childrenMap.get(currentNode.id) || [];
const nextStepNode = nodeChildren.find(c => c.type !== 'condition' && !visited.has(c.id));
if (nextStepNode) {
currentNode = nextStepNode;
} else {
break;
}
}
return chainSteps;
};
const chainSteps = processSequentialChain(child);
step.children!.push(...chainSteps);
}
});
return step;
}
return {
id: node.id,
name: 'Unknown',
description: '',
type: 'instruction',
config: {},
order: 0,
enabled: true,
hasIssues: true,
children: []
};
};
const result: ConditionalStep[] = [];
const processSequentialSteps = (startNode: Node) => {
let currentNode = startNode;
while (currentNode && !visited.has(currentNode.id)) {
const step = convertNode(currentNode);
result.push(step);
const children = childrenMap.get(currentNode.id) || [];
const nextStepNode = children.find(child => child.type !== 'condition' && !visited.has(child.id));
if (nextStepNode) {
currentNode = nextStepNode;
} else {
break;
}
}
};
processSequentialSteps(rootNode);
nodes.forEach(node => {
if (!visited.has(node.id)) {
const step = convertNode(node);
result.push(step);
}
});
return result;
}