zxhlyh 915023b809
Chore/remove add node restrict of workflow (#26218)
Co-authored-by: -LAN- <laipz8200@outlook.com>
2025-09-25 18:02:43 +08:00

170 lines
5.3 KiB
TypeScript

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<string, any>
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<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[]]
}
export const hasErrorHandleNode = (nodeType?: BlockEnum) => {
return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code
}