| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   Position, | 
					
						
							|  |  |  |   getConnectedEdges, | 
					
						
							|  |  |  |   getOutgoers, | 
					
						
							|  |  |  | } from 'reactflow' | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  | import dagre from '@dagrejs/dagre' | 
					
						
							| 
									
										
										
										
											2024-04-23 17:02:23 +08:00
										 |  |  | import { v4 as uuid4 } from 'uuid' | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   cloneDeep, | 
					
						
							|  |  |  |   uniqBy, | 
					
						
							|  |  |  | } from 'lodash-es' | 
					
						
							|  |  |  | import type { | 
					
						
							|  |  |  |   Edge, | 
					
						
							|  |  |  |   InputVar, | 
					
						
							|  |  |  |   Node, | 
					
						
							|  |  |  |   ToolWithProvider, | 
					
						
							| 
									
										
										
										
											2024-07-10 18:20:13 +08:00
										 |  |  |   ValueSelector, | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | } from './types' | 
					
						
							|  |  |  | import { BlockEnum } from './types' | 
					
						
							|  |  |  | import { | 
					
						
							| 
									
										
										
										
											2024-06-14 17:08:11 +08:00
										 |  |  |   CUSTOM_NODE, | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   ITERATION_NODE_Z_INDEX, | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   NODE_WIDTH_X_OFFSET, | 
					
						
							|  |  |  |   START_INITIAL_POSITION, | 
					
						
							|  |  |  | } from './constants' | 
					
						
							|  |  |  | import type { QuestionClassifierNodeType } from './nodes/question-classifier/types' | 
					
						
							| 
									
										
										
										
											2024-07-10 18:20:13 +08:00
										 |  |  | import type { IfElseNodeType } from './nodes/if-else/types' | 
					
						
							|  |  |  | import { branchNameCorrect } from './nodes/if-else/utils' | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | import type { ToolNodeType } from './nodes/tool/types' | 
					
						
							|  |  |  | import { CollectionType } from '@/app/components/tools/types' | 
					
						
							|  |  |  | import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-09 12:24:41 +08:00
										 |  |  | const WHITE = 'WHITE' | 
					
						
							|  |  |  | const GRAY = 'GRAY' | 
					
						
							|  |  |  | const BLACK = 'BLACK' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const isCyclicUtil = (nodeId: string, color: Record<string, string>, adjaList: Record<string, string[]>, stack: string[]) => { | 
					
						
							|  |  |  |   color[nodeId] = GRAY | 
					
						
							|  |  |  |   stack.push(nodeId) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (let i = 0; i < adjaList[nodeId].length; ++i) { | 
					
						
							|  |  |  |     const childId = adjaList[nodeId][i] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (color[childId] === GRAY) { | 
					
						
							|  |  |  |       stack.push(childId) | 
					
						
							|  |  |  |       return true | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (color[childId] === WHITE && isCyclicUtil(childId, color, adjaList, stack)) | 
					
						
							|  |  |  |       return true | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   color[nodeId] = BLACK | 
					
						
							|  |  |  |   if (stack.length > 0 && stack[stack.length - 1] === nodeId) | 
					
						
							|  |  |  |     stack.pop() | 
					
						
							|  |  |  |   return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const getCycleEdges = (nodes: Node[], edges: Edge[]) => { | 
					
						
							|  |  |  |   const adjaList: Record<string, string[]> = {} | 
					
						
							|  |  |  |   const color: Record<string, string> = {} | 
					
						
							|  |  |  |   const stack: string[] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (const node of nodes) { | 
					
						
							|  |  |  |     color[node.id] = WHITE | 
					
						
							|  |  |  |     adjaList[node.id] = [] | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (const edge of edges) | 
					
						
							| 
									
										
										
										
											2024-04-10 13:49:21 +08:00
										 |  |  |     adjaList[edge.source]?.push(edge.target) | 
					
						
							| 
									
										
										
										
											2024-04-09 12:24:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   for (let i = 0; i < nodes.length; i++) { | 
					
						
							|  |  |  |     if (color[nodes[i].id] === WHITE) | 
					
						
							|  |  |  |       isCyclicUtil(nodes[i].id, color, adjaList, stack) | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const cycleEdges = [] | 
					
						
							|  |  |  |   if (stack.length > 0) { | 
					
						
							|  |  |  |     const cycleNodes = new Set(stack) | 
					
						
							|  |  |  |     for (const edge of edges) { | 
					
						
							|  |  |  |       if (cycleNodes.has(edge.source) && cycleNodes.has(edge.target)) | 
					
						
							|  |  |  |         cycleEdges.push(edge) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return cycleEdges | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-28 17:09:56 +08:00
										 |  |  | export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => { | 
					
						
							|  |  |  |   const nodes = cloneDeep(originNodes) | 
					
						
							|  |  |  |   const edges = cloneDeep(originEdges) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   const firstNode = nodes[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!firstNode?.position) { | 
					
						
							|  |  |  |     nodes.forEach((node, index) => { | 
					
						
							|  |  |  |       node.position = { | 
					
						
							|  |  |  |         x: START_INITIAL_POSITION.x + index * NODE_WIDTH_X_OFFSET, | 
					
						
							|  |  |  |         y: START_INITIAL_POSITION.y, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-04-09 12:24:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   const iterationNodeMap = nodes.reduce((acc, node) => { | 
					
						
							|  |  |  |     if (node.parentId) { | 
					
						
							|  |  |  |       if (acc[node.parentId]) | 
					
						
							|  |  |  |         acc[node.parentId].push(node.id) | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         acc[node.parentId] = [node.id] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return acc | 
					
						
							|  |  |  |   }, {} as Record<string, string[]>) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   return nodes.map((node) => { | 
					
						
							| 
									
										
										
										
											2024-06-14 17:08:11 +08:00
										 |  |  |     if (!node.type) | 
					
						
							|  |  |  |       node.type = CUSTOM_NODE | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const connectedEdges = getConnectedEdges([node], edges) | 
					
						
							|  |  |  |     node.data._connectedSourceHandleIds = connectedEdges.filter(edge => edge.source === node.id).map(edge => edge.sourceHandle || 'source') | 
					
						
							|  |  |  |     node.data._connectedTargetHandleIds = connectedEdges.filter(edge => edge.target === node.id).map(edge => edge.targetHandle || 'target') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (node.data.type === BlockEnum.IfElse) { | 
					
						
							| 
									
										
										
										
											2024-07-10 18:20:13 +08:00
										 |  |  |       const nodeData = node.data as IfElseNodeType | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!nodeData.cases && nodeData.logical_operator && nodeData.conditions) { | 
					
						
							|  |  |  |         (node.data as IfElseNodeType).cases = [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             case_id: 'true', | 
					
						
							|  |  |  |             logical_operator: nodeData.logical_operator, | 
					
						
							|  |  |  |             conditions: nodeData.conditions, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       node.data._targetBranches = branchNameCorrect([ | 
					
						
							|  |  |  |         ...(node.data as IfElseNodeType).cases.map(item => ({ id: item.case_id, name: '' })), | 
					
						
							|  |  |  |         { id: 'false', name: '' }, | 
					
						
							|  |  |  |       ]) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (node.data.type === BlockEnum.QuestionClassifier) { | 
					
						
							|  |  |  |       node.data._targetBranches = (node.data as QuestionClassifierNodeType).classes.map((topic) => { | 
					
						
							|  |  |  |         return topic | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |     if (node.data.type === BlockEnum.Iteration) | 
					
						
							|  |  |  |       node.data._children = iterationNodeMap[node.id] || [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     return node | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-28 17:09:56 +08:00
										 |  |  | export const initialEdges = (originEdges: Edge[], originNodes: Node[]) => { | 
					
						
							|  |  |  |   const nodes = cloneDeep(originNodes) | 
					
						
							|  |  |  |   const edges = cloneDeep(originEdges) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   let selectedNode: Node | null = null | 
					
						
							|  |  |  |   const nodesMap = nodes.reduce((acc, node) => { | 
					
						
							|  |  |  |     acc[node.id] = node | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (node.data?.selected) | 
					
						
							|  |  |  |       selectedNode = node | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return acc | 
					
						
							|  |  |  |   }, {} as Record<string, Node>) | 
					
						
							| 
									
										
										
										
											2024-04-09 12:24:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const cycleEdges = getCycleEdges(nodes, edges) | 
					
						
							|  |  |  |   return edges.filter((edge) => { | 
					
						
							|  |  |  |     return !cycleEdges.find(cycEdge => cycEdge.source === edge.source && cycEdge.target === edge.target) | 
					
						
							|  |  |  |   }).map((edge) => { | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     edge.type = 'custom' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!edge.sourceHandle) | 
					
						
							|  |  |  |       edge.sourceHandle = 'source' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!edge.targetHandle) | 
					
						
							|  |  |  |       edge.targetHandle = 'target' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-14 15:51:08 +08:00
										 |  |  |     if (!edge.data?.sourceType && edge.source && nodesMap[edge.source]) { | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       edge.data = { | 
					
						
							|  |  |  |         ...edge.data, | 
					
						
							|  |  |  |         sourceType: nodesMap[edge.source].data.type!, | 
					
						
							|  |  |  |       } as any | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-14 15:51:08 +08:00
										 |  |  |     if (!edge.data?.targetType && edge.target && nodesMap[edge.target]) { | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |       edge.data = { | 
					
						
							|  |  |  |         ...edge.data, | 
					
						
							|  |  |  |         targetType: nodesMap[edge.target].data.type!, | 
					
						
							|  |  |  |       } as any | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (selectedNode) { | 
					
						
							|  |  |  |       edge.data = { | 
					
						
							|  |  |  |         ...edge.data, | 
					
						
							|  |  |  |         _connectedNodeIsSelected: edge.source === selectedNode.id || edge.target === selectedNode.id, | 
					
						
							|  |  |  |       } as any | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-07-10 18:20:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     return edge | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getLayoutByDagre = (originNodes: Node[], originEdges: Edge[]) => { | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   const dagreGraph = new dagre.graphlib.Graph() | 
					
						
							|  |  |  |   dagreGraph.setDefaultEdgeLabel(() => ({})) | 
					
						
							| 
									
										
										
										
											2024-06-14 17:08:11 +08:00
										 |  |  |   const nodes = cloneDeep(originNodes).filter(node => !node.parentId && node.type === CUSTOM_NODE) | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   const edges = cloneDeep(originEdges).filter(edge => !edge.data?.isInIteration) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   dagreGraph.setGraph({ | 
					
						
							|  |  |  |     rankdir: 'LR', | 
					
						
							|  |  |  |     align: 'UL', | 
					
						
							| 
									
										
										
										
											2024-04-15 14:05:20 +08:00
										 |  |  |     nodesep: 40, | 
					
						
							|  |  |  |     ranksep: 60, | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |     ranker: 'tight-tree', | 
					
						
							|  |  |  |     marginx: 30, | 
					
						
							|  |  |  |     marginy: 200, | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   }) | 
					
						
							|  |  |  |   nodes.forEach((node) => { | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |     dagreGraph.setNode(node.id, { | 
					
						
							|  |  |  |       width: node.width!, | 
					
						
							|  |  |  |       height: node.height!, | 
					
						
							|  |  |  |     }) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   edges.forEach((edge) => { | 
					
						
							|  |  |  |     dagreGraph.setEdge(edge.source, edge.target) | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   dagre.layout(dagreGraph) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return dagreGraph | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const canRunBySingle = (nodeType: BlockEnum) => { | 
					
						
							|  |  |  |   return nodeType === BlockEnum.LLM | 
					
						
							|  |  |  |     || nodeType === BlockEnum.KnowledgeRetrieval | 
					
						
							|  |  |  |     || nodeType === BlockEnum.Code | 
					
						
							|  |  |  |     || nodeType === BlockEnum.TemplateTransform | 
					
						
							|  |  |  |     || nodeType === BlockEnum.QuestionClassifier | 
					
						
							|  |  |  |     || nodeType === BlockEnum.HttpRequest | 
					
						
							|  |  |  |     || nodeType === BlockEnum.Tool | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |     || nodeType === BlockEnum.ParameterExtractor | 
					
						
							|  |  |  |     || nodeType === BlockEnum.Iteration | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ConnectedSourceOrTargetNodesChange = { | 
					
						
							|  |  |  |   type: string | 
					
						
							|  |  |  |   edge: Edge | 
					
						
							|  |  |  | }[] | 
					
						
							|  |  |  | export const getNodesConnectedSourceOrTargetHandleIdsMap = (changes: ConnectedSourceOrTargetNodesChange, nodes: Node[]) => { | 
					
						
							|  |  |  |   const nodesConnectedSourceOrTargetHandleIdsMap = {} as Record<string, any> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   changes.forEach((change) => { | 
					
						
							|  |  |  |     const { | 
					
						
							|  |  |  |       edge, | 
					
						
							|  |  |  |       type, | 
					
						
							|  |  |  |     } = change | 
					
						
							|  |  |  |     const sourceNode = nodes.find(node => node.id === edge.source)! | 
					
						
							| 
									
										
										
										
											2024-04-10 11:16:54 +08:00
										 |  |  |     if (sourceNode) { | 
					
						
							|  |  |  |       nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id] = nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id] || { | 
					
						
							|  |  |  |         _connectedSourceHandleIds: [...(sourceNode?.data._connectedSourceHandleIds || [])], | 
					
						
							|  |  |  |         _connectedTargetHandleIds: [...(sourceNode?.data._connectedTargetHandleIds || [])], | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-04-10 11:16:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     const targetNode = nodes.find(node => node.id === edge.target)! | 
					
						
							| 
									
										
										
										
											2024-04-10 11:16:54 +08:00
										 |  |  |     if (targetNode) { | 
					
						
							|  |  |  |       nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id] = nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id] || { | 
					
						
							|  |  |  |         _connectedSourceHandleIds: [...(targetNode?.data._connectedSourceHandleIds || [])], | 
					
						
							|  |  |  |         _connectedTargetHandleIds: [...(targetNode?.data._connectedTargetHandleIds || [])], | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sourceNode) { | 
					
						
							| 
									
										
										
										
											2024-05-30 18:54:58 +08:00
										 |  |  |       if (type === 'remove') { | 
					
						
							|  |  |  |         const index = nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id]._connectedSourceHandleIds.findIndex((handleId: string) => handleId === edge.sourceHandle) | 
					
						
							|  |  |  |         nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id]._connectedSourceHandleIds.splice(index, 1) | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (type === 'add') | 
					
						
							|  |  |  |         nodesConnectedSourceOrTargetHandleIdsMap[sourceNode.id]._connectedSourceHandleIds.push(edge.sourceHandle || 'source') | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (targetNode) { | 
					
						
							| 
									
										
										
										
											2024-05-30 18:54:58 +08:00
										 |  |  |       if (type === 'remove') { | 
					
						
							|  |  |  |         const index = nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id]._connectedTargetHandleIds.findIndex((handleId: string) => handleId === edge.targetHandle) | 
					
						
							|  |  |  |         nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id]._connectedTargetHandleIds.splice(index, 1) | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (type === 'add') | 
					
						
							|  |  |  |         nodesConnectedSourceOrTargetHandleIdsMap[targetNode.id]._connectedTargetHandleIds.push(edge.targetHandle || 'target') | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return nodesConnectedSourceOrTargetHandleIdsMap | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-14 17:08:11 +08:00
										 |  |  | export const generateNewNode = ({ data, position, id, zIndex, type, ...rest }: Omit<Node, 'id'> & { id?: string }) => { | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   return { | 
					
						
							|  |  |  |     id: id || `${Date.now()}`, | 
					
						
							| 
									
										
										
										
											2024-06-14 17:08:11 +08:00
										 |  |  |     type: type || CUSTOM_NODE, | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     data, | 
					
						
							|  |  |  |     position, | 
					
						
							|  |  |  |     targetPosition: Position.Left, | 
					
						
							|  |  |  |     sourcePosition: Position.Right, | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |     zIndex: data.type === BlockEnum.Iteration ? ITERATION_NODE_Z_INDEX : zIndex, | 
					
						
							|  |  |  |     ...rest, | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   } as Node | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-28 13:50:43 +08:00
										 |  |  | export const genNewNodeTitleFromOld = (oldTitle: string) => { | 
					
						
							|  |  |  |   const regex = /^(.+?)\s*\((\d+)\)\s*$/ | 
					
						
							|  |  |  |   const match = oldTitle.match(regex) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (match) { | 
					
						
							|  |  |  |     const title = match[1] | 
					
						
							|  |  |  |     const num = parseInt(match[2], 10) | 
					
						
							|  |  |  |     return `${title} (${num + 1})` | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     return `${oldTitle} (1)` | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | export const getValidTreeNodes = (nodes: Node[], edges: Edge[]) => { | 
					
						
							|  |  |  |   const startNode = nodes.find(node => node.data.type === BlockEnum.Start) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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) | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |         if (outgoer.data.type === BlockEnum.Iteration) | 
					
						
							|  |  |  |           list.push(...nodes.filter(node => node.parentId === outgoer.id)) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |         traverse(outgoer, depth + 1) | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       list.push(root) | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |       if (root.data.type === BlockEnum.Iteration) | 
					
						
							|  |  |  |         list.push(...nodes.filter(node => node.parentId === root.id)) | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   traverse(startNode, maxDepth) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     validNodes: uniqBy(list, 'id'), | 
					
						
							|  |  |  |     maxDepth, | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getToolCheckParams = ( | 
					
						
							|  |  |  |   toolData: ToolNodeType, | 
					
						
							|  |  |  |   buildInTools: ToolWithProvider[], | 
					
						
							|  |  |  |   customTools: ToolWithProvider[], | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   workflowTools: ToolWithProvider[], | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   language: string, | 
					
						
							|  |  |  | ) => { | 
					
						
							|  |  |  |   const { provider_id, provider_type, tool_name } = toolData | 
					
						
							|  |  |  |   const isBuiltIn = provider_type === CollectionType.builtIn | 
					
						
							| 
									
										
										
										
											2024-05-27 21:57:08 +08:00
										 |  |  |   const currentTools = provider_type === CollectionType.builtIn ? buildInTools : provider_type === CollectionType.custom ? customTools : workflowTools | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |   const currCollection = currentTools.find(item => item.id === provider_id) | 
					
						
							|  |  |  |   const currTool = currCollection?.tools.find(tool => tool.name === tool_name) | 
					
						
							|  |  |  |   const formSchemas = currTool ? toolParametersToFormSchemas(currTool.parameters) : [] | 
					
						
							|  |  |  |   const toolInputVarSchema = formSchemas.filter((item: any) => item.form === 'llm') | 
					
						
							|  |  |  |   const toolSettingSchema = formSchemas.filter((item: any) => item.form !== 'llm') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     toolInputsSchema: (() => { | 
					
						
							|  |  |  |       const formInputs: InputVar[] = [] | 
					
						
							|  |  |  |       toolInputVarSchema.forEach((item: any) => { | 
					
						
							|  |  |  |         formInputs.push({ | 
					
						
							|  |  |  |           label: item.label[language] || item.label.en_US, | 
					
						
							|  |  |  |           variable: item.variable, | 
					
						
							|  |  |  |           type: item.type, | 
					
						
							|  |  |  |           required: item.required, | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       return formInputs | 
					
						
							|  |  |  |     })(), | 
					
						
							|  |  |  |     notAuthed: isBuiltIn && !!currCollection?.allow_delete && !currCollection?.is_team_authorization, | 
					
						
							|  |  |  |     toolSettingSchema, | 
					
						
							|  |  |  |     language, | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-04-23 17:02:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const changeNodesAndEdgesId = (nodes: Node[], edges: Edge[]) => { | 
					
						
							|  |  |  |   const idMap = nodes.reduce((acc, node) => { | 
					
						
							|  |  |  |     acc[node.id] = uuid4() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return acc | 
					
						
							|  |  |  |   }, {} as Record<string, string>) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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[]] | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const isMac = () => { | 
					
						
							|  |  |  |   return navigator.userAgent.toUpperCase().includes('MAC') | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const specialKeysNameMap: Record<string, string | undefined> = { | 
					
						
							|  |  |  |   ctrl: '⌘', | 
					
						
							|  |  |  |   alt: '⌥', | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getKeyboardKeyNameBySystem = (key: string) => { | 
					
						
							|  |  |  |   if (isMac()) | 
					
						
							|  |  |  |     return specialKeysNameMap[key] || key | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return key | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const specialKeysCodeMap: Record<string, string | undefined> = { | 
					
						
							|  |  |  |   ctrl: 'meta', | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getKeyboardKeyCodeBySystem = (key: string) => { | 
					
						
							|  |  |  |   if (isMac()) | 
					
						
							|  |  |  |     return specialKeysCodeMap[key] || key | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return key | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getTopLeftNodePosition = (nodes: Node[]) => { | 
					
						
							|  |  |  |   let minX = Infinity | 
					
						
							|  |  |  |   let minY = Infinity | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   nodes.forEach((node) => { | 
					
						
							|  |  |  |     if (node.position.x < minX) | 
					
						
							|  |  |  |       minX = node.position.x | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (node.position.y < minY) | 
					
						
							|  |  |  |       minY = node.position.y | 
					
						
							|  |  |  |   }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     x: minX, | 
					
						
							|  |  |  |     y: minY, | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-05-11 10:38:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const isEventTargetInputArea = (target: HTMLElement) => { | 
					
						
							|  |  |  |   if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') | 
					
						
							|  |  |  |     return true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (target.contentEditable === 'true') | 
					
						
							|  |  |  |     return true | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-07-10 18:20:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const variableTransformer = (v: ValueSelector | string) => { | 
					
						
							|  |  |  |   if (typeof v === 'string') | 
					
						
							|  |  |  |     return v.replace(/^{{#|#}}$/g, '').split('.') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return `{{#${v.join('.')}#}}` | 
					
						
							|  |  |  | } |