Merge remote-tracking branch 'origin/main' into feat/trigger

This commit is contained in:
yessenia
2025-09-25 17:14:24 +08:00
3013 changed files with 148826 additions and 44294 deletions

View File

@ -1,6 +1,5 @@
import {
useCallback,
useMemo,
} from 'react'
import { uniqBy } from 'lodash-es'
import { useTranslation } from 'react-i18next'
@ -13,21 +12,19 @@ import type {
Connection,
} from 'reactflow'
import type {
BlockEnum,
Edge,
Node,
ValueSelector,
} from '../types'
import {
BlockEnum,
WorkflowRunningStatus,
} from '../types'
import {
useStore,
useWorkflowStore,
} from '../store'
import {
getParallelInfo,
} from '../utils'
import { getParallelInfo } from '../utils'
import {
getWorkflowEntryNode,
isWorkflowEntryNode,
@ -36,9 +33,11 @@ import {
PARALLEL_DEPTH_LIMIT,
SUPPORT_OUTPUT_VARS_NODE,
} from '../constants'
import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types'
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
import { useNodesExtraData } from './use-nodes-data'
import { useAvailableBlocks } from './use-available-blocks'
import { useStore as useAppStore } from '@/app/components/app/store'
import {
fetchAllBuiltInTools,
@ -46,13 +45,11 @@ import {
fetchAllMCPTools,
fetchAllWorkflowTools,
} from '@/service/tools'
import { useAllTriggerPlugins } from '@/service/use-triggers'
import { CollectionType } from '@/app/components/tools/types'
import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
import { basePath } from '@/utils/var'
import { canFindTool } from '@/utils'
import { MAX_PARALLEL_LIMIT } from '@/config'
import { useNodesMetaData } from '.'
export const useIsChatMode = () => {
const appDetail = useAppStore(s => s.appDetail)
@ -64,7 +61,17 @@ export const useWorkflow = () => {
const { t } = useTranslation()
const store = useStoreApi()
const workflowStore = useWorkflowStore()
const nodesExtraData = useNodesExtraData()
const { getAvailableBlocks } = useAvailableBlocks()
const { nodesMap } = useNodesMetaData()
const getNodeById = useCallback((nodeId: string) => {
const {
getNodes,
} = store.getState()
const nodes = getNodes()
const currentNode = nodes.find(node => node.id === nodeId)
return currentNode
}, [store])
const getTreeLeafNodes = useCallback((nodeId: string) => {
const {
@ -72,13 +79,18 @@ export const useWorkflow = () => {
edges,
} = store.getState()
const nodes = getNodes()
let startNode = getWorkflowEntryNode(nodes)
// let startNode = getWorkflowEntryNode(nodes)
const currentNode = nodes.find(node => node.id === nodeId)
if (currentNode?.parentId)
startNode = nodes.find(node => node.parentId === currentNode.parentId && (node.type === CUSTOM_ITERATION_START_NODE || node.type === CUSTOM_LOOP_START_NODE))
let startNodes = nodes.filter(node => nodesMap?.[node.data.type as BlockEnum]?.metaData.isStart) || []
if (!startNode)
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]
}
if (!startNodes.length)
return []
const list: Node[] = []
@ -97,8 +109,10 @@ export const useWorkflow = () => {
callback(root)
}
}
preOrder(startNode, (node) => {
list.push(node)
startNodes.forEach((startNode) => {
preOrder(startNode, (node) => {
list.push(node)
})
})
const incomers = getIncomers({ id: nodeId } as Node, nodes, edges)
@ -108,7 +122,7 @@ export const useWorkflow = () => {
return uniqBy(list, 'id').filter((item: Node) => {
return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type)
})
}, [store])
}, [store, nodesMap])
const getBeforeNodesInSameBranch = useCallback((nodeId: string, newNodes?: Node[], newEdges?: Edge[]) => {
const {
@ -322,28 +336,102 @@ export const useWorkflow = () => {
return true
}, [store, workflowStore, t])
const checkNestedParallelLimit = useCallback((nodes: Node[], edges: Edge[], parentNodeId?: string) => {
const getRootNodesById = useCallback((nodeId: string) => {
const {
parallelList,
hasAbnormalEdges,
} = getParallelInfo(nodes, edges, parentNodeId)
const { workflowConfig } = workflowStore.getState()
getNodes,
edges,
} = store.getState()
const nodes = getNodes()
const currentNode = nodes.find(node => node.id === nodeId)
if (hasAbnormalEdges)
return false
const rootNodes: Node[] = []
for (let i = 0; i < parallelList.length; i++) {
const parallel = parallelList[i]
if (!currentNode)
return rootNodes
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) }))
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])
const getStartNodes = useCallback((nodes: Node[], currentNode?: Node) => {
const { id, parentId } = currentNode || {}
let startNodes: Node[] = []
if (parentId) {
const parentNode = nodes.find(node => node.id === parentId)
if (!parentNode)
throw new Error('Parent node not found')
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 || '')
return startNodes
}, [nodesMap, getRootNodesById])
const checkNestedParallelLimit = useCallback((nodes: Node[], edges: Edge[], targetNode?: Node) => {
const startNodes = getStartNodes(nodes, targetNode)
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
}
}
}
return true
}, [t, workflowStore])
}, [t, workflowStore, getStartNodes])
const isValidConnection = useCallback(({ source, sourceHandle, target }: Connection) => {
const {
@ -364,8 +452,8 @@ export const useWorkflow = () => {
return false
if (sourceNode && targetNode) {
const sourceNodeAvailableNextNodes = nodesExtraData[sourceNode.data.type].availableNextNodes
const targetNodeAvailablePrevNodes = [...nodesExtraData[targetNode.data.type].availablePrevNodes, BlockEnum.Start]
const sourceNodeAvailableNextNodes = getAvailableBlocks(sourceNode.data.type, !!sourceNode.parentId).availableNextBlocks
const targetNodeAvailablePrevNodes = getAvailableBlocks(targetNode.data.type, !!targetNode.parentId).availablePrevBlocks
if (!sourceNodeAvailableNextNodes.includes(targetNode.data.type))
return false
@ -389,7 +477,7 @@ export const useWorkflow = () => {
}
return !hasCycle(targetNode)
}, [store, nodesExtraData, checkParallelLimit])
}, [store, checkParallelLimit, getAvailableBlocks])
const getNode = useCallback((nodeId?: string) => {
const { getNodes } = store.getState()
@ -399,6 +487,7 @@ export const useWorkflow = () => {
}, [store])
return {
getNodeById,
getTreeLeafNodes,
getBeforeNodesInSameBranch,
getBeforeNodesInSameBranchIncludeParent,
@ -410,11 +499,13 @@ export const useWorkflow = () => {
checkParallelLimit,
checkNestedParallelLimit,
isValidConnection,
isFromStartNode,
getNode,
getBeforeNodeById,
getIterationNodeChildren,
getLoopNodeChildren,
getRootNodesById,
getStartNodes,
isFromStartNode,
getNode,
}
}
@ -476,6 +567,7 @@ export const useWorkflowReadOnly = () => {
getWorkflowReadOnly,
}
}
export const useNodesReadOnly = () => {
const workflowStore = useWorkflowStore()
const workflowRunningData = useStore(s => s.workflowRunningData)
@ -498,38 +590,6 @@ export const useNodesReadOnly = () => {
}
}
export const useToolIcon = (data: Node['data']) => {
const buildInTools = useStore(s => s.buildInTools)
const customTools = useStore(s => s.customTools)
const workflowTools = useStore(s => s.workflowTools)
const mcpTools = useStore(s => s.mcpTools)
const { data: triggerPlugins } = useAllTriggerPlugins()
const toolIcon = useMemo(() => {
if (!data)
return ''
if (data.type === BlockEnum.TriggerPlugin) {
const targetTools = triggerPlugins || []
return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon
}
if (data.type === BlockEnum.Tool) {
let targetTools = workflowTools
if (data.provider_type === CollectionType.builtIn)
targetTools = buildInTools
else if (data.provider_type === CollectionType.custom)
targetTools = customTools
else if (data.provider_type === CollectionType.mcp)
targetTools = mcpTools
return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon
}
}, [data, buildInTools, customTools, mcpTools, triggerPlugins, workflowTools])
return toolIcon
}
export const useIsNodeInIteration = (iterationId: string) => {
const store = useStoreApi()