Feature/newnew workflow loop node (#14863)

Co-authored-by: arkunzz <4873204@qq.com>
This commit is contained in:
Wood
2025-03-05 17:41:15 +08:00
committed by GitHub
parent da91217bc9
commit 2c17bb2c36
131 changed files with 6031 additions and 159 deletions

View File

@@ -30,15 +30,19 @@ import {
DEFAULT_RETRY_MAX,
ITERATION_CHILDREN_Z_INDEX,
ITERATION_NODE_Z_INDEX,
LOOP_CHILDREN_Z_INDEX,
LOOP_NODE_Z_INDEX,
NODE_WIDTH_X_OFFSET,
START_INITIAL_POSITION,
} from './constants'
import { CUSTOM_ITERATION_START_NODE } from './nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from './nodes/loop-start/constants'
import type { QuestionClassifierNodeType } from './nodes/question-classifier/types'
import type { IfElseNodeType } from './nodes/if-else/types'
import { branchNameCorrect } from './nodes/if-else/utils'
import type { ToolNodeType } from './nodes/tool/types'
import type { IterationNodeType } from './nodes/iteration/types'
import type { LoopNodeType } from './nodes/loop/types'
import { CollectionType } from '@/app/components/tools/types'
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import { canFindTool, correctModelProvider } from '@/utils'
@@ -118,9 +122,31 @@ export function getIterationStartNode(iterationId: string): Node {
}).newNode
}
export function getLoopStartNode(loopId: string): Node {
return generateNewNode({
id: `${loopId}start`,
type: CUSTOM_LOOP_START_NODE,
data: {
title: '',
desc: '',
type: BlockEnum.LoopStart,
isInLoop: true,
},
position: {
x: 24,
y: 68,
},
zIndex: LOOP_CHILDREN_Z_INDEX,
parentId: loopId,
selectable: false,
draggable: false,
}).newNode
}
export function generateNewNode({ data, position, id, zIndex, type, ...rest }: Omit<Node, 'id'> & { id?: string }): {
newNode: Node
newIterationStartNode?: Node
newLoopStartNode?: Node
} {
const newNode = {
id: id || `${Date.now()}`,
@@ -129,7 +155,7 @@ export function generateNewNode({ data, position, id, zIndex, type, ...rest }: O
position,
targetPosition: Position.Left,
sourcePosition: Position.Right,
zIndex: data.type === BlockEnum.Iteration ? ITERATION_NODE_Z_INDEX : zIndex,
zIndex: data.type === BlockEnum.Iteration ? ITERATION_NODE_Z_INDEX : (data.type === BlockEnum.Loop ? LOOP_NODE_Z_INDEX : zIndex),
...rest,
} as Node
@@ -143,6 +169,16 @@ export function generateNewNode({ data, position, id, zIndex, type, ...rest }: O
}
}
if (data.type === BlockEnum.Loop) {
const newLoopStartNode = getLoopStartNode(newNode.id);
(newNode.data as LoopNodeType).start_node_id = newLoopStartNode.id;
(newNode.data as LoopNodeType)._children = [newLoopStartNode.id]
return {
newNode,
newLoopStartNode,
}
}
return {
newNode,
}
@@ -150,6 +186,7 @@ export function generateNewNode({ data, position, id, zIndex, type, ...rest }: O
export const preprocessNodesAndEdges = (nodes: Node[], edges: Edge[]) => {
const hasIterationNode = nodes.some(node => node.data.type === BlockEnum.Iteration)
const hasLoopNode = nodes.some(node => node.data.type === BlockEnum.Loop)
if (!hasIterationNode) {
return {
@@ -157,15 +194,26 @@ export const preprocessNodesAndEdges = (nodes: Node[], edges: Edge[]) => {
edges,
}
}
if (!hasLoopNode) {
return {
nodes,
edges,
}
}
const nodesMap = nodes.reduce((prev, next) => {
prev[next.id] = next
return prev
}, {} as Record<string, Node>)
const iterationNodesWithStartNode = []
const iterationNodesWithoutStartNode = []
const loopNodesWithStartNode = []
const loopNodesWithoutStartNode = []
for (let i = 0; i < nodes.length; i++) {
const currentNode = nodes[i] as Node<IterationNodeType>
const currentNode = nodes[i] as Node<IterationNodeType | LoopNodeType>
if (currentNode.data.type === BlockEnum.Iteration) {
if (currentNode.data.start_node_id) {
@@ -176,7 +224,18 @@ export const preprocessNodesAndEdges = (nodes: Node[], edges: Edge[]) => {
iterationNodesWithoutStartNode.push(currentNode)
}
}
if (currentNode.data.type === BlockEnum.Loop) {
if (currentNode.data.start_node_id) {
if (nodesMap[currentNode.data.start_node_id]?.type !== CUSTOM_LOOP_START_NODE)
loopNodesWithStartNode.push(currentNode)
}
else {
loopNodesWithoutStartNode.push(currentNode)
}
}
}
const newIterationStartNodesMap = {} as Record<string, Node>
const newIterationStartNodes = [...iterationNodesWithStartNode, ...iterationNodesWithoutStartNode].map((iterationNode, index) => {
const newNode = getIterationStartNode(iterationNode.id)
@@ -184,13 +243,28 @@ export const preprocessNodesAndEdges = (nodes: Node[], edges: Edge[]) => {
newIterationStartNodesMap[iterationNode.id] = newNode
return newNode
})
const newEdges = iterationNodesWithStartNode.map((iterationNode) => {
const newNode = newIterationStartNodesMap[iterationNode.id]
const startNode = nodesMap[iterationNode.data.start_node_id]
const newLoopStartNodesMap = {} as Record<string, Node>
const newLoopStartNodes = [...loopNodesWithStartNode, ...loopNodesWithoutStartNode].map((loopNode, index) => {
const newNode = getLoopStartNode(loopNode.id)
newNode.id = newNode.id + index
newLoopStartNodesMap[loopNode.id] = newNode
return newNode
})
const newEdges = [...iterationNodesWithStartNode, ...loopNodesWithStartNode].map((nodeItem) => {
const isIteration = nodeItem.data.type === BlockEnum.Iteration
const newNode = (isIteration ? newIterationStartNodesMap : newLoopStartNodesMap)[nodeItem.id]
const startNode = nodesMap[nodeItem.data.start_node_id]
const source = newNode.id
const sourceHandle = 'source'
const target = startNode.id
const targetHandle = 'target'
const parentNode = nodes.find(node => node.id === startNode.parentId) || null
const isInIteration = !!parentNode && parentNode.data.type === BlockEnum.Iteration
const isInLoop = !!parentNode && parentNode.data.type === BlockEnum.Loop
return {
id: `${source}-${sourceHandle}-${target}-${targetHandle}`,
type: 'custom',
@@ -201,20 +275,25 @@ export const preprocessNodesAndEdges = (nodes: Node[], edges: Edge[]) => {
data: {
sourceType: newNode.data.type,
targetType: startNode.data.type,
isInIteration: true,
iteration_id: startNode.parentId,
isInIteration,
iteration_id: isInIteration ? startNode.parentId : undefined,
isInLoop,
loop_id: isInLoop ? startNode.parentId : undefined,
_connectedNodeIsSelected: true,
},
zIndex: ITERATION_CHILDREN_Z_INDEX,
zIndex: isIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX,
}
})
nodes.forEach((node) => {
if (node.data.type === BlockEnum.Iteration && newIterationStartNodesMap[node.id])
(node.data as IterationNodeType).start_node_id = newIterationStartNodesMap[node.id].id
if (node.data.type === BlockEnum.Loop && newLoopStartNodesMap[node.id])
(node.data as LoopNodeType).start_node_id = newLoopStartNodesMap[node.id].id
})
return {
nodes: [...nodes, ...newIterationStartNodes],
nodes: [...nodes, ...newIterationStartNodes, ...newLoopStartNodes],
edges: [...edges, ...newEdges],
}
}
@@ -232,7 +311,7 @@ export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => {
})
}
const iterationNodeMap = nodes.reduce((acc, node) => {
const iterationOrLoopNodeMap = nodes.reduce((acc, node) => {
if (node.parentId) {
if (acc[node.parentId])
acc[node.parentId].push(node.id)
@@ -276,12 +355,19 @@ export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => {
if (node.data.type === BlockEnum.Iteration) {
const iterationNodeData = node.data as IterationNodeType
iterationNodeData._children = iterationNodeMap[node.id] || []
iterationNodeData._children = iterationOrLoopNodeMap[node.id] || []
iterationNodeData.is_parallel = iterationNodeData.is_parallel || false
iterationNodeData.parallel_nums = iterationNodeData.parallel_nums || 10
iterationNodeData.error_handle_mode = iterationNodeData.error_handle_mode || ErrorHandleMode.Terminated
}
// TODO: loop error handle mode
if (node.data.type === BlockEnum.Loop) {
const loopNodeData = node.data as LoopNodeType
loopNodeData._children = iterationOrLoopNodeMap[node.id] || []
loopNodeData.error_handle_mode = loopNodeData.error_handle_mode || ErrorHandleMode.Terminated
}
// legacy provider handle
if (node.data.type === BlockEnum.LLM)
(node as any).data.model.provider = correctModelProvider((node as any).data.model.provider)
@@ -359,7 +445,7 @@ export const getLayoutByDagre = (originNodes: Node[], originEdges: Edge[]) => {
const dagreGraph = new dagre.graphlib.Graph()
dagreGraph.setDefaultEdgeLabel(() => ({}))
const nodes = cloneDeep(originNodes).filter(node => !node.parentId && node.type === CUSTOM_NODE)
const edges = cloneDeep(originEdges).filter(edge => !edge.data?.isInIteration)
const edges = cloneDeep(originEdges).filter(edge => (!edge.data?.isInIteration && !edge.data?.isInLoop))
dagreGraph.setGraph({
rankdir: 'LR',
align: 'UL',
@@ -397,6 +483,7 @@ export const canRunBySingle = (nodeType: BlockEnum) => {
|| nodeType === BlockEnum.Iteration
|| nodeType === BlockEnum.Agent
|| nodeType === BlockEnum.DocExtractor
|| nodeType === BlockEnum.Loop
}
type ConnectedSourceOrTargetNodesChange = {
@@ -487,15 +574,22 @@ export const getValidTreeNodes = (nodes: Node[], edges: Edge[]) => {
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))
}
}
@@ -654,7 +748,7 @@ export const getParallelInfo = (nodes: Node[], edges: Edge[], parentNodeId?: str
if (!parentNode)
throw new Error('Parent node not found')
startNode = nodes.find(node => node.id === (parentNode.data as IterationNodeType).start_node_id)
startNode = nodes.find(node => node.id === (parentNode.data as (IterationNodeType | LoopNodeType)).start_node_id)
}
else {
startNode = nodes.find(node => node.data.type === BlockEnum.Start)