2024-04-08 18:51:46 +08:00
|
|
|
import {
|
|
|
|
useCallback,
|
|
|
|
} from 'react'
|
|
|
|
import { uniqBy } from 'lodash-es'
|
2024-09-10 15:23:16 +08:00
|
|
|
import { useTranslation } from 'react-i18next'
|
2024-04-08 18:51:46 +08:00
|
|
|
import {
|
|
|
|
getIncomers,
|
|
|
|
getOutgoers,
|
|
|
|
useStoreApi,
|
|
|
|
} from 'reactflow'
|
|
|
|
import type {
|
|
|
|
Connection,
|
|
|
|
} from 'reactflow'
|
|
|
|
import type {
|
2025-05-21 15:50:36 +08:00
|
|
|
BlockEnum,
|
2024-05-27 21:57:08 +08:00
|
|
|
Edge,
|
2024-04-08 18:51:46 +08:00
|
|
|
Node,
|
|
|
|
ValueSelector,
|
|
|
|
} from '../types'
|
|
|
|
import {
|
|
|
|
WorkflowRunningStatus,
|
|
|
|
} from '../types'
|
|
|
|
import {
|
|
|
|
useStore,
|
|
|
|
useWorkflowStore,
|
|
|
|
} from '../store'
|
2025-05-21 16:34:41 +08:00
|
|
|
import { getParallelInfo } from '../utils'
|
2024-04-08 18:51:46 +08:00
|
|
|
import {
|
2025-05-21 16:34:41 +08:00
|
|
|
PARALLEL_DEPTH_LIMIT,
|
2024-09-10 15:23:16 +08:00
|
|
|
PARALLEL_LIMIT,
|
2024-04-08 18:51:46 +08:00
|
|
|
SUPPORT_OUTPUT_VARS_NODE,
|
|
|
|
} from '../constants'
|
2025-05-21 16:34:41 +08:00
|
|
|
import type { IterationNodeType } from '../nodes/iteration/types'
|
|
|
|
import type { LoopNodeType } from '../nodes/loop/types'
|
2024-06-14 17:08:11 +08:00
|
|
|
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
|
2024-04-08 18:51:46 +08:00
|
|
|
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
|
2025-04-24 16:29:58 +08:00
|
|
|
import { useAvailableBlocks } from './use-available-blocks'
|
2024-04-08 18:51:46 +08:00
|
|
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
|
|
|
import {
|
|
|
|
fetchAllBuiltInTools,
|
|
|
|
fetchAllCustomTools,
|
2024-05-27 21:57:08 +08:00
|
|
|
fetchAllWorkflowTools,
|
2024-04-08 18:51:46 +08:00
|
|
|
} from '@/service/tools'
|
2024-09-10 15:23:16 +08:00
|
|
|
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
|
2025-03-05 17:41:15 +08:00
|
|
|
import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
|
2025-04-15 17:05:50 +08:00
|
|
|
import { basePath } from '@/utils/var'
|
2025-05-21 15:50:36 +08:00
|
|
|
import { useNodesMetaData } from '.'
|
2024-04-23 17:02:23 +08:00
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
export const useIsChatMode = () => {
|
|
|
|
const appDetail = useAppStore(s => s.appDetail)
|
|
|
|
|
|
|
|
return appDetail?.mode === 'advanced-chat'
|
|
|
|
}
|
|
|
|
|
|
|
|
export const useWorkflow = () => {
|
2024-09-10 15:23:16 +08:00
|
|
|
const { t } = useTranslation()
|
2024-04-08 18:51:46 +08:00
|
|
|
const store = useStoreApi()
|
|
|
|
const workflowStore = useWorkflowStore()
|
2025-04-24 16:29:58 +08:00
|
|
|
const { getAvailableBlocks } = useAvailableBlocks()
|
2025-05-21 15:50:36 +08:00
|
|
|
const { nodesMap } = useNodesMetaData()
|
2024-04-26 14:23:13 +08:00
|
|
|
const setPanelWidth = useCallback((width: number) => {
|
|
|
|
localStorage.setItem('workflow-node-panel-width', `${width}`)
|
|
|
|
workflowStore.setState({ panelWidth: width })
|
|
|
|
}, [workflowStore])
|
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
const getTreeLeafNodes = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
edges,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
2024-05-27 21:57:08 +08:00
|
|
|
const currentNode = nodes.find(node => node.id === nodeId)
|
|
|
|
|
2025-05-21 15:50:36 +08:00
|
|
|
let startNodes = nodes.filter(node => nodesMap?.[node.data.type as BlockEnum]?.metaData.isStart) || []
|
|
|
|
|
|
|
|
if (currentNode?.parentId) {
|
|
|
|
const startNode = nodes.find(node => node.parentId === currentNode.parentId && (node.type === CUSTOM_ITERATION_START_NODE || node.type === CUSTOM_LOOP_START_NODE))
|
|
|
|
if (startNode)
|
|
|
|
startNodes = [startNode]
|
|
|
|
}
|
2024-04-08 18:51:46 +08:00
|
|
|
|
2025-05-21 15:50:36 +08:00
|
|
|
if (!startNodes.length)
|
2024-04-08 18:51:46 +08:00
|
|
|
return []
|
|
|
|
|
|
|
|
const list: Node[] = []
|
|
|
|
const preOrder = (root: Node, callback: (node: Node) => void) => {
|
|
|
|
if (root.id === nodeId)
|
|
|
|
return
|
|
|
|
const outgoers = getOutgoers(root, nodes, edges)
|
|
|
|
|
|
|
|
if (outgoers.length) {
|
|
|
|
outgoers.forEach((outgoer) => {
|
|
|
|
preOrder(outgoer, callback)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (root.id !== nodeId)
|
|
|
|
callback(root)
|
|
|
|
}
|
|
|
|
}
|
2025-05-21 15:50:36 +08:00
|
|
|
startNodes.forEach((startNode) => {
|
|
|
|
preOrder(startNode, (node) => {
|
|
|
|
list.push(node)
|
|
|
|
})
|
2024-04-08 18:51:46 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
const incomers = getIncomers({ id: nodeId } as Node, nodes, edges)
|
|
|
|
|
|
|
|
list.push(...incomers)
|
|
|
|
|
2025-04-18 13:59:12 +08:00
|
|
|
return uniqBy(list, 'id').filter((item: Node) => {
|
2024-04-08 18:51:46 +08:00
|
|
|
return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type)
|
|
|
|
})
|
2025-05-21 15:50:36 +08:00
|
|
|
}, [store, nodesMap])
|
2024-04-08 18:51:46 +08:00
|
|
|
|
2024-05-27 21:57:08 +08:00
|
|
|
const getBeforeNodesInSameBranch = useCallback((nodeId: string, newNodes?: Node[], newEdges?: Edge[]) => {
|
2024-04-08 18:51:46 +08:00
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
edges,
|
|
|
|
} = store.getState()
|
2024-05-27 21:57:08 +08:00
|
|
|
const nodes = newNodes || getNodes()
|
2024-04-08 18:51:46 +08:00
|
|
|
const currentNode = nodes.find(node => node.id === nodeId)
|
2024-05-27 21:57:08 +08:00
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
const list: Node[] = []
|
|
|
|
|
|
|
|
if (!currentNode)
|
|
|
|
return list
|
|
|
|
|
2024-05-27 21:57:08 +08:00
|
|
|
if (currentNode.parentId) {
|
|
|
|
const parentNode = nodes.find(node => node.id === currentNode.parentId)
|
|
|
|
if (parentNode) {
|
|
|
|
const parentList = getBeforeNodesInSameBranch(parentNode.id)
|
|
|
|
|
|
|
|
list.push(...parentList)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
const traverse = (root: Node, callback: (node: Node) => void) => {
|
|
|
|
if (root) {
|
2024-05-27 21:57:08 +08:00
|
|
|
const incomers = getIncomers(root, nodes, newEdges || edges)
|
2024-04-08 18:51:46 +08:00
|
|
|
|
|
|
|
if (incomers.length) {
|
|
|
|
incomers.forEach((node) => {
|
2024-04-09 12:24:41 +08:00
|
|
|
if (!list.find(n => node.id === n.id)) {
|
|
|
|
callback(node)
|
|
|
|
traverse(node, callback)
|
|
|
|
}
|
2024-04-08 18:51:46 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
traverse(currentNode, (node) => {
|
|
|
|
list.push(node)
|
|
|
|
})
|
|
|
|
|
|
|
|
const length = list.length
|
|
|
|
if (length) {
|
2025-04-18 13:59:12 +08:00
|
|
|
return uniqBy(list, 'id').reverse().filter((item: Node) => {
|
2024-04-08 18:51:46 +08:00
|
|
|
return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return []
|
|
|
|
}, [store])
|
|
|
|
|
2024-05-27 21:57:08 +08:00
|
|
|
const getBeforeNodesInSameBranchIncludeParent = useCallback((nodeId: string, newNodes?: Node[], newEdges?: Edge[]) => {
|
|
|
|
const nodes = getBeforeNodesInSameBranch(nodeId, newNodes, newEdges)
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
} = store.getState()
|
|
|
|
const allNodes = getNodes()
|
|
|
|
const node = allNodes.find(n => n.id === nodeId)
|
|
|
|
const parentNodeId = node?.parentId
|
|
|
|
const parentNode = allNodes.find(n => n.id === parentNodeId)
|
|
|
|
if (parentNode)
|
|
|
|
nodes.push(parentNode)
|
|
|
|
|
|
|
|
return nodes
|
|
|
|
}, [getBeforeNodesInSameBranch, store])
|
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
const getAfterNodesInSameBranch = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
edges,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
|
|
|
const currentNode = nodes.find(node => node.id === nodeId)!
|
|
|
|
|
|
|
|
if (!currentNode)
|
|
|
|
return []
|
|
|
|
const list: Node[] = [currentNode]
|
|
|
|
|
|
|
|
const traverse = (root: Node, callback: (node: Node) => void) => {
|
|
|
|
if (root) {
|
|
|
|
const outgoers = getOutgoers(root, nodes, edges)
|
|
|
|
|
|
|
|
if (outgoers.length) {
|
|
|
|
outgoers.forEach((node) => {
|
|
|
|
callback(node)
|
|
|
|
traverse(node, callback)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
traverse(currentNode, (node) => {
|
|
|
|
list.push(node)
|
|
|
|
})
|
|
|
|
|
|
|
|
return uniqBy(list, 'id')
|
|
|
|
}, [store])
|
|
|
|
|
|
|
|
const getBeforeNodeById = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
edges,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
|
|
|
const node = nodes.find(node => node.id === nodeId)!
|
|
|
|
|
|
|
|
return getIncomers(node, nodes, edges)
|
|
|
|
}, [store])
|
|
|
|
|
2024-05-27 21:57:08 +08:00
|
|
|
const getIterationNodeChildren = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
|
|
|
|
|
|
|
return nodes.filter(node => node.parentId === nodeId)
|
|
|
|
}, [store])
|
|
|
|
|
2025-03-05 17:41:15 +08:00
|
|
|
const getLoopNodeChildren = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
|
|
|
|
|
|
|
return nodes.filter(node => node.parentId === nodeId)
|
|
|
|
}, [store])
|
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
const handleOutVarRenameChange = useCallback((nodeId: string, oldValeSelector: ValueSelector, newVarSelector: ValueSelector) => {
|
|
|
|
const { getNodes, setNodes } = store.getState()
|
|
|
|
const afterNodes = getAfterNodesInSameBranch(nodeId)
|
|
|
|
const effectNodes = findUsedVarNodes(oldValeSelector, afterNodes)
|
|
|
|
if (effectNodes.length > 0) {
|
|
|
|
const newNodes = getNodes().map((node) => {
|
|
|
|
if (effectNodes.find(n => n.id === node.id))
|
|
|
|
return updateNodeVars(node, oldValeSelector, newVarSelector)
|
|
|
|
|
|
|
|
return node
|
|
|
|
})
|
|
|
|
setNodes(newNodes)
|
|
|
|
}
|
|
|
|
|
2025-03-05 17:41:15 +08:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-04-08 18:51:46 +08:00
|
|
|
}, [store])
|
|
|
|
|
|
|
|
const isVarUsedInNodes = useCallback((varSelector: ValueSelector) => {
|
|
|
|
const nodeId = varSelector[0]
|
|
|
|
const afterNodes = getAfterNodesInSameBranch(nodeId)
|
|
|
|
const effectNodes = findUsedVarNodes(varSelector, afterNodes)
|
|
|
|
return effectNodes.length > 0
|
|
|
|
}, [getAfterNodesInSameBranch])
|
|
|
|
|
|
|
|
const removeUsedVarInNodes = useCallback((varSelector: ValueSelector) => {
|
|
|
|
const nodeId = varSelector[0]
|
|
|
|
const { getNodes, setNodes } = store.getState()
|
|
|
|
const afterNodes = getAfterNodesInSameBranch(nodeId)
|
|
|
|
const effectNodes = findUsedVarNodes(varSelector, afterNodes)
|
|
|
|
if (effectNodes.length > 0) {
|
|
|
|
const newNodes = getNodes().map((node) => {
|
|
|
|
if (effectNodes.find(n => n.id === node.id))
|
|
|
|
return updateNodeVars(node, varSelector, [])
|
|
|
|
|
|
|
|
return node
|
|
|
|
})
|
|
|
|
setNodes(newNodes)
|
|
|
|
}
|
|
|
|
}, [getAfterNodesInSameBranch, store])
|
|
|
|
|
|
|
|
const isNodeVarsUsedInNodes = useCallback((node: Node, isChatMode: boolean) => {
|
|
|
|
const outputVars = getNodeOutputVars(node, isChatMode)
|
|
|
|
const isUsed = outputVars.some((varSelector) => {
|
|
|
|
return isVarUsedInNodes(varSelector)
|
|
|
|
})
|
|
|
|
return isUsed
|
|
|
|
}, [isVarUsedInNodes])
|
|
|
|
|
2024-09-11 11:30:33 +08:00
|
|
|
const checkParallelLimit = useCallback((nodeId: string, nodeHandle = 'source') => {
|
2024-09-10 15:23:16 +08:00
|
|
|
const {
|
|
|
|
edges,
|
|
|
|
} = store.getState()
|
2024-09-11 11:30:33 +08:00
|
|
|
const connectedEdges = edges.filter(edge => edge.source === nodeId && edge.sourceHandle === nodeHandle)
|
|
|
|
if (connectedEdges.length > PARALLEL_LIMIT - 1) {
|
2024-09-10 15:23:16 +08:00
|
|
|
const { setShowTips } = workflowStore.getState()
|
|
|
|
setShowTips(t('workflow.common.parallelTip.limit', { num: PARALLEL_LIMIT }))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}, [store, workflowStore, t])
|
|
|
|
|
2025-05-21 16:34:41 +08:00
|
|
|
const getRootNodesById = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
edges,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
|
|
|
const currentNode = nodes.find(node => node.id === nodeId)
|
|
|
|
|
|
|
|
const rootNodes: Node[] = []
|
|
|
|
|
|
|
|
if (!currentNode)
|
|
|
|
return rootNodes
|
|
|
|
|
|
|
|
if (currentNode.parentId) {
|
|
|
|
const parentNode = nodes.find(node => node.id === currentNode.parentId)
|
|
|
|
if (parentNode) {
|
|
|
|
const parentList = getRootNodesById(parentNode.id)
|
|
|
|
|
|
|
|
rootNodes.push(...parentList)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const traverse = (root: Node, callback: (node: Node) => void) => {
|
|
|
|
if (root) {
|
|
|
|
const incomers = getIncomers(root, nodes, edges)
|
|
|
|
|
|
|
|
if (incomers.length) {
|
|
|
|
incomers.forEach((node) => {
|
|
|
|
traverse(node, callback)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
callback(root)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
traverse(currentNode, (node) => {
|
|
|
|
rootNodes.push(node)
|
|
|
|
})
|
|
|
|
|
|
|
|
const length = rootNodes.length
|
|
|
|
if (length)
|
|
|
|
return uniqBy(rootNodes, 'id')
|
|
|
|
|
|
|
|
return []
|
|
|
|
}, [store])
|
2025-04-22 16:46:33 +08:00
|
|
|
|
2025-05-23 14:25:38 +08:00
|
|
|
const getStartNodes = useCallback((nodes: Node[], currentNode?: Node) => {
|
|
|
|
const { id, parentId } = currentNode || {}
|
2025-05-21 16:34:41 +08:00
|
|
|
let startNodes: Node[] = []
|
2025-04-22 16:46:33 +08:00
|
|
|
|
2025-05-21 16:34:41 +08:00
|
|
|
if (parentId) {
|
|
|
|
const parentNode = nodes.find(node => node.id === parentId)
|
|
|
|
if (!parentNode)
|
|
|
|
throw new Error('Parent node not found')
|
2025-04-22 16:46:33 +08:00
|
|
|
|
2025-05-21 16:34:41 +08:00
|
|
|
const startNode = nodes.find(node => node.id === (parentNode.data as (IterationNodeType | LoopNodeType)).start_node_id)
|
|
|
|
if (startNode)
|
|
|
|
startNodes = [startNode]
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
startNodes = nodes.filter(node => nodesMap?.[node.data.type as BlockEnum]?.metaData.isStart) || []
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!startNodes.length)
|
|
|
|
startNodes = getRootNodesById(id || '')
|
|
|
|
|
2025-05-23 14:25:38 +08:00
|
|
|
return startNodes
|
|
|
|
}, [nodesMap, getRootNodesById])
|
|
|
|
|
|
|
|
const checkNestedParallelLimit = useCallback((nodes: Node[], edges: Edge[], targetNode?: Node) => {
|
|
|
|
const startNodes = getStartNodes(nodes, targetNode)
|
|
|
|
|
2025-05-21 16:34:41 +08:00
|
|
|
for (let i = 0; i < startNodes.length; i++) {
|
|
|
|
const {
|
|
|
|
parallelList,
|
|
|
|
hasAbnormalEdges,
|
|
|
|
} = getParallelInfo(startNodes[i], nodes, edges)
|
|
|
|
const { workflowConfig } = workflowStore.getState()
|
|
|
|
|
|
|
|
if (hasAbnormalEdges)
|
|
|
|
return false
|
|
|
|
|
|
|
|
for (let i = 0; i < parallelList.length; i++) {
|
|
|
|
const parallel = parallelList[i]
|
|
|
|
|
|
|
|
if (parallel.depth > (workflowConfig?.parallel_depth_limit || PARALLEL_DEPTH_LIMIT)) {
|
|
|
|
const { setShowTips } = workflowStore.getState()
|
|
|
|
setShowTips(t('workflow.common.parallelTip.depthLimit', { num: (workflowConfig?.parallel_depth_limit || PARALLEL_DEPTH_LIMIT) }))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-09-10 15:23:16 +08:00
|
|
|
|
|
|
|
return true
|
2025-05-23 14:25:38 +08:00
|
|
|
}, [t, workflowStore, getStartNodes])
|
2024-09-10 15:23:16 +08:00
|
|
|
|
2024-09-11 11:30:33 +08:00
|
|
|
const isValidConnection = useCallback(({ source, sourceHandle, target }: Connection) => {
|
2024-04-09 12:24:41 +08:00
|
|
|
const {
|
|
|
|
edges,
|
|
|
|
getNodes,
|
|
|
|
} = store.getState()
|
2024-04-08 18:51:46 +08:00
|
|
|
const nodes = getNodes()
|
|
|
|
const sourceNode: Node = nodes.find(node => node.id === source)!
|
|
|
|
const targetNode: Node = nodes.find(node => node.id === target)!
|
|
|
|
|
2024-09-11 11:30:33 +08:00
|
|
|
if (!checkParallelLimit(source!, sourceHandle || 'source'))
|
2024-05-27 21:57:08 +08:00
|
|
|
return false
|
|
|
|
|
2024-06-14 17:08:11 +08:00
|
|
|
if (sourceNode.type === CUSTOM_NOTE_NODE || targetNode.type === CUSTOM_NOTE_NODE)
|
|
|
|
return false
|
|
|
|
|
2024-09-10 15:23:16 +08:00
|
|
|
if (sourceNode.parentId !== targetNode.parentId)
|
|
|
|
return false
|
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
if (sourceNode && targetNode) {
|
2025-04-24 16:29:58 +08:00
|
|
|
const sourceNodeAvailableNextNodes = getAvailableBlocks(sourceNode.data.type, !!sourceNode.parentId).availableNextBlocks
|
|
|
|
const targetNodeAvailablePrevNodes = getAvailableBlocks(targetNode.data.type, !!targetNode.parentId).availablePrevBlocks
|
2024-05-27 21:57:08 +08:00
|
|
|
|
2024-04-08 18:51:46 +08:00
|
|
|
if (!sourceNodeAvailableNextNodes.includes(targetNode.data.type))
|
|
|
|
return false
|
|
|
|
|
|
|
|
if (!targetNodeAvailablePrevNodes.includes(sourceNode.data.type))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-04-09 12:24:41 +08:00
|
|
|
const hasCycle = (node: Node, visited = new Set()) => {
|
|
|
|
if (visited.has(node.id))
|
|
|
|
return false
|
|
|
|
|
|
|
|
visited.add(node.id)
|
|
|
|
|
|
|
|
for (const outgoer of getOutgoers(node, nodes, edges)) {
|
|
|
|
if (outgoer.id === source)
|
|
|
|
return true
|
|
|
|
if (hasCycle(outgoer, visited))
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return !hasCycle(targetNode)
|
2025-04-24 16:29:58 +08:00
|
|
|
}, [store, checkParallelLimit, getAvailableBlocks])
|
2024-04-08 18:51:46 +08:00
|
|
|
|
|
|
|
return {
|
2024-04-26 14:23:13 +08:00
|
|
|
setPanelWidth,
|
2024-04-08 18:51:46 +08:00
|
|
|
getTreeLeafNodes,
|
|
|
|
getBeforeNodesInSameBranch,
|
2024-05-27 21:57:08 +08:00
|
|
|
getBeforeNodesInSameBranchIncludeParent,
|
2024-04-08 18:51:46 +08:00
|
|
|
getAfterNodesInSameBranch,
|
|
|
|
handleOutVarRenameChange,
|
|
|
|
isVarUsedInNodes,
|
|
|
|
removeUsedVarInNodes,
|
|
|
|
isNodeVarsUsedInNodes,
|
2024-09-10 15:23:16 +08:00
|
|
|
checkParallelLimit,
|
|
|
|
checkNestedParallelLimit,
|
2024-04-08 18:51:46 +08:00
|
|
|
isValidConnection,
|
|
|
|
getBeforeNodeById,
|
2024-05-27 21:57:08 +08:00
|
|
|
getIterationNodeChildren,
|
2025-03-05 17:41:15 +08:00
|
|
|
getLoopNodeChildren,
|
2025-05-21 16:34:41 +08:00
|
|
|
getRootNodesById,
|
2025-05-23 14:25:38 +08:00
|
|
|
getStartNodes,
|
2024-04-08 18:51:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const useFetchToolsData = () => {
|
|
|
|
const workflowStore = useWorkflowStore()
|
|
|
|
|
|
|
|
const handleFetchAllTools = useCallback(async (type: string) => {
|
|
|
|
if (type === 'builtin') {
|
|
|
|
const buildInTools = await fetchAllBuiltInTools()
|
|
|
|
|
2025-04-15 17:05:50 +08:00
|
|
|
if (basePath) {
|
|
|
|
buildInTools.forEach((item) => {
|
|
|
|
if (typeof item.icon == 'string' && !item.icon.includes(basePath))
|
|
|
|
item.icon = `${basePath}${item.icon}`
|
|
|
|
})
|
|
|
|
}
|
2024-04-08 18:51:46 +08:00
|
|
|
workflowStore.setState({
|
|
|
|
buildInTools: buildInTools || [],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (type === 'custom') {
|
|
|
|
const customTools = await fetchAllCustomTools()
|
|
|
|
|
|
|
|
workflowStore.setState({
|
|
|
|
customTools: customTools || [],
|
|
|
|
})
|
|
|
|
}
|
2024-05-27 21:57:08 +08:00
|
|
|
if (type === 'workflow') {
|
|
|
|
const workflowTools = await fetchAllWorkflowTools()
|
|
|
|
|
|
|
|
workflowStore.setState({
|
|
|
|
workflowTools: workflowTools || [],
|
|
|
|
})
|
|
|
|
}
|
2024-04-08 18:51:46 +08:00
|
|
|
}, [workflowStore])
|
|
|
|
|
|
|
|
return {
|
|
|
|
handleFetchAllTools,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const useWorkflowReadOnly = () => {
|
|
|
|
const workflowStore = useWorkflowStore()
|
|
|
|
const workflowRunningData = useStore(s => s.workflowRunningData)
|
|
|
|
|
|
|
|
const getWorkflowReadOnly = useCallback(() => {
|
|
|
|
return workflowStore.getState().workflowRunningData?.result.status === WorkflowRunningStatus.Running
|
|
|
|
}, [workflowStore])
|
|
|
|
|
|
|
|
return {
|
|
|
|
workflowReadOnly: workflowRunningData?.result.status === WorkflowRunningStatus.Running,
|
|
|
|
getWorkflowReadOnly,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export const useNodesReadOnly = () => {
|
|
|
|
const workflowStore = useWorkflowStore()
|
|
|
|
const workflowRunningData = useStore(s => s.workflowRunningData)
|
|
|
|
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
|
|
|
const isRestoring = useStore(s => s.isRestoring)
|
|
|
|
|
|
|
|
const getNodesReadOnly = useCallback(() => {
|
|
|
|
const {
|
|
|
|
workflowRunningData,
|
|
|
|
historyWorkflowData,
|
|
|
|
isRestoring,
|
|
|
|
} = workflowStore.getState()
|
|
|
|
|
2024-04-28 17:09:56 +08:00
|
|
|
return workflowRunningData?.result.status === WorkflowRunningStatus.Running || historyWorkflowData || isRestoring
|
2024-04-08 18:51:46 +08:00
|
|
|
}, [workflowStore])
|
|
|
|
|
|
|
|
return {
|
2024-04-28 17:09:56 +08:00
|
|
|
nodesReadOnly: !!(workflowRunningData?.result.status === WorkflowRunningStatus.Running || historyWorkflowData || isRestoring),
|
2024-04-08 18:51:46 +08:00
|
|
|
getNodesReadOnly,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-27 21:57:08 +08:00
|
|
|
export const useIsNodeInIteration = (iterationId: string) => {
|
|
|
|
const store = useStoreApi()
|
|
|
|
|
|
|
|
const isNodeInIteration = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
|
|
|
const node = nodes.find(node => node.id === nodeId)
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return false
|
|
|
|
|
|
|
|
if (node.parentId === iterationId)
|
|
|
|
return true
|
|
|
|
|
|
|
|
return false
|
|
|
|
}, [iterationId, store])
|
|
|
|
return {
|
|
|
|
isNodeInIteration,
|
|
|
|
}
|
|
|
|
}
|
2025-03-05 17:41:15 +08:00
|
|
|
|
|
|
|
export const useIsNodeInLoop = (loopId: string) => {
|
|
|
|
const store = useStoreApi()
|
|
|
|
|
|
|
|
const isNodeInLoop = useCallback((nodeId: string) => {
|
|
|
|
const {
|
|
|
|
getNodes,
|
|
|
|
} = store.getState()
|
|
|
|
const nodes = getNodes()
|
|
|
|
const node = nodes.find(node => node.id === nodeId)
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return false
|
|
|
|
|
|
|
|
if (node.parentId === loopId)
|
|
|
|
return true
|
|
|
|
|
|
|
|
return false
|
|
|
|
}, [loopId, store])
|
|
|
|
return {
|
|
|
|
isNodeInLoop,
|
|
|
|
}
|
|
|
|
}
|