import { getOutgoers, } from 'reactflow' import { v4 as uuid4 } from 'uuid' import { uniqBy, } from 'lodash-es' import type { Edge, Node, } from '../types' import { BlockEnum, } from '../types' export const canRunBySingle = (nodeType: BlockEnum, isChildNode: boolean) => { // child node means in iteration or loop. Set value to iteration(or loop) may cause variable not exit problem in backend. if(isChildNode && nodeType === BlockEnum.Assigner) return false return nodeType === BlockEnum.LLM || nodeType === BlockEnum.KnowledgeRetrieval || nodeType === BlockEnum.Code || nodeType === BlockEnum.TemplateTransform || nodeType === BlockEnum.QuestionClassifier || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Tool || nodeType === BlockEnum.ParameterExtractor || nodeType === BlockEnum.Iteration || nodeType === BlockEnum.Agent || nodeType === BlockEnum.DocExtractor || nodeType === BlockEnum.Loop || nodeType === BlockEnum.Start || nodeType === BlockEnum.IfElse || nodeType === BlockEnum.VariableAggregator || nodeType === BlockEnum.Assigner || nodeType === BlockEnum.DataSource } export const isSupportCustomRunForm = (nodeType: BlockEnum) => { return nodeType === BlockEnum.DataSource } type ConnectedSourceOrTargetNodesChange = { type: string edge: Edge }[] export const getNodesConnectedSourceOrTargetHandleIdsMap = (changes: ConnectedSourceOrTargetNodesChange, nodes: Node[]) => { const nodesConnectedSourceOrTargetHandleIdsMap = {} as Record changes.forEach((change) => { const { edge, type, } = change const sourceNode = nodes.find(node => node.id === edge.source)! if (sourceNode) { nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id] = nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id] || { _connectedSourceHandleIds: [...(sourceNode?.data._connectedSourceHandleIds || [])], _connectedTargetHandleIds: [...(sourceNode?.data._connectedTargetHandleIds || [])], } } const targetNode = nodes.find(node => node.id === edge.target)! if (targetNode) { nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id] = nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id] || { _connectedSourceHandleIds: [...(targetNode?.data._connectedSourceHandleIds || [])], _connectedTargetHandleIds: [...(targetNode?.data._connectedTargetHandleIds || [])], } } if (sourceNode) { if (type === 'remove') { const index = nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id]._connectedSourceHandleIds.findIndex((handleId: string) => handleId === edge.sourceHandle) nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id]._connectedSourceHandleIds.splice(index, 1) } if (type === 'add') nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id]._connectedSourceHandleIds.push(edge.sourceHandle || 'source') } if (targetNode) { if (type === 'remove') { const index = nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id]._connectedTargetHandleIds.findIndex((handleId: string) => handleId === edge.targetHandle) nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id]._connectedTargetHandleIds.splice(index, 1) } if (type === 'add') nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id]._connectedTargetHandleIds.push(edge.targetHandle || 'target') } }) return nodesConnectedSourceOrTargetHandleIdsMap } export const getValidTreeNodes = (startNode: Node, nodes: Node[], edges: Edge[]) => { if (!startNode) { return { validNodes: [], maxDepth: 0, } } const list: Node[] = [startNode] let maxDepth = 1 const traverse = (root: Node, depth: number) => { if (depth > maxDepth) maxDepth = depth const outgoers = getOutgoers(root, nodes, edges) if (outgoers.length) { outgoers.forEach((outgoer) => { list.push(outgoer) if (outgoer.data.type === BlockEnum.Iteration) list.push(...nodes.filter(node => node.parentId === outgoer.id)) if (outgoer.data.type === BlockEnum.Loop) list.push(...nodes.filter(node => node.parentId === outgoer.id)) traverse(outgoer, depth + 1) }) } else { list.push(root) if (root.data.type === BlockEnum.Iteration) list.push(...nodes.filter(node => node.parentId === root.id)) if (root.data.type === BlockEnum.Loop) list.push(...nodes.filter(node => node.parentId === root.id)) } } traverse(startNode, maxDepth) return { validNodes: uniqBy(list, 'id'), maxDepth, } } export const changeNodesAndEdgesId = (nodes: Node[], edges: Edge[]) => { const idMap = nodes.reduce((acc, node) => { acc[node.id] = uuid4() return acc }, {} as Record) const newNodes = nodes.map((node) => { return { ...node, id: idMap[node.id], } }) const newEdges = edges.map((edge) => { return { ...edge, source: idMap[edge.source], target: idMap[edge.target], } }) return [newNodes, newEdges] as [Node[], Edge[]] } export const hasErrorHandleNode = (nodeType?: BlockEnum) => { return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code }