mirror of https://github.com/langgenius/dify.git
154 lines
3.5 KiB
TypeScript
154 lines
3.5 KiB
TypeScript
import produce from 'immer'
|
|
import {
|
|
getConnectedEdges,
|
|
getOutgoers,
|
|
} from 'reactflow'
|
|
import dagre from 'dagre'
|
|
import type {
|
|
Edge,
|
|
Node,
|
|
} from './types'
|
|
import { BlockEnum } from './types'
|
|
|
|
export const nodesLevelOrderTraverse = (
|
|
firstNode: Node,
|
|
nodes: Node[],
|
|
edges: Edge[],
|
|
callback: (n: any) => void,
|
|
) => {
|
|
const queue = [{
|
|
node: firstNode,
|
|
depth: 0,
|
|
breath: 0,
|
|
}]
|
|
|
|
let currenDepth = 0
|
|
let currentBreath = 0
|
|
while (queue.length) {
|
|
const { node, depth, breath } = queue.shift()!
|
|
|
|
if (currenDepth !== depth) {
|
|
currenDepth = depth
|
|
currentBreath = 0
|
|
}
|
|
|
|
callback({ node, depth, breath })
|
|
|
|
const targetBranches = node.data._targetBranches
|
|
if (targetBranches?.length) {
|
|
const targetEdges = getConnectedEdges([node], edges)
|
|
|
|
if (targetEdges.length) {
|
|
const sortedTargetEdges = targetEdges
|
|
.filter(edge => edge.source === node.id)
|
|
.sort((a, b) => {
|
|
const aIndex = targetBranches.findIndex(branch => branch.id === a.sourceHandle)
|
|
const bIndex = targetBranches.findIndex(branch => branch.id === b.sourceHandle)
|
|
|
|
return aIndex - bIndex
|
|
})
|
|
|
|
const outgoers = getOutgoers(node, nodes, sortedTargetEdges)
|
|
|
|
queue.push(...outgoers.map((outgoer, index) => {
|
|
return {
|
|
node: outgoer,
|
|
depth: depth + 1,
|
|
breath: currentBreath + index,
|
|
}
|
|
}))
|
|
|
|
currentBreath += outgoers.length
|
|
}
|
|
else {
|
|
currentBreath += 1
|
|
}
|
|
}
|
|
else {
|
|
const outgoers = getOutgoers(node, nodes, edges)
|
|
|
|
if (outgoers.length === 1) {
|
|
queue.push({
|
|
node: outgoers[0],
|
|
depth: depth + 1,
|
|
breath: 0,
|
|
})
|
|
}
|
|
|
|
currentBreath += 1
|
|
}
|
|
}
|
|
}
|
|
|
|
export const initialNodesAndEdges = (nodes: Node[], edges: Edge[]) => {
|
|
const newNodes = produce(nodes, (draft) => {
|
|
draft.forEach((node) => {
|
|
node.type = 'custom'
|
|
})
|
|
})
|
|
const newEdges = produce(edges, (draft) => {
|
|
draft.forEach((edge) => {
|
|
edge.type = 'custom'
|
|
})
|
|
})
|
|
|
|
return [newNodes, newEdges]
|
|
}
|
|
|
|
export type PositionMap = {
|
|
index: {
|
|
x: number
|
|
y: number
|
|
}
|
|
}
|
|
|
|
export const getNodesPositionMap = (nodes: Node[], edges: Edge[]) => {
|
|
const startNode = nodes.find((node: Node) => node.data.type === BlockEnum.Start)
|
|
const positionMap: Record<string, PositionMap> = {}
|
|
|
|
if (startNode) {
|
|
nodesLevelOrderTraverse(startNode, nodes, edges, ({ node, depth, breath }) => {
|
|
positionMap[node.id] = {
|
|
index: {
|
|
x: depth,
|
|
y: breath,
|
|
},
|
|
}
|
|
})
|
|
}
|
|
|
|
return positionMap
|
|
}
|
|
|
|
export const getLayoutByDagre = (nodes: Node[], edges: Edge[]) => {
|
|
const dagreGraph = new dagre.graphlib.Graph()
|
|
dagreGraph.setGraph({
|
|
rankdir: 'LR',
|
|
align: 'UL',
|
|
nodesep: 64,
|
|
ranksep: 64,
|
|
})
|
|
nodes.forEach((node) => {
|
|
dagreGraph.setNode(node.id, { width: node.width, height: node.height })
|
|
})
|
|
|
|
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.IfElse
|
|
|| nodeType === BlockEnum.Code
|
|
|| nodeType === BlockEnum.TemplateTransform
|
|
|| nodeType === BlockEnum.QuestionClassifier
|
|
|| nodeType === BlockEnum.HttpRequest
|
|
|| nodeType === BlockEnum.Tool
|
|
}
|