mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-20 21:39:21 +08:00
Merge branch 'feat/workflow-parallel-support' of github.com:langgenius/dify into feat/workflow-parallel-support
This commit is contained in:
commit
708256ef1d
@ -327,6 +327,7 @@ export const ITERATION_PADDING = {
|
|||||||
left: 16,
|
left: 16,
|
||||||
}
|
}
|
||||||
export const PARALLEL_LIMIT = 10
|
export const PARALLEL_LIMIT = 10
|
||||||
|
export const PARALLEL_DEPTH_LIMIT = 3
|
||||||
|
|
||||||
export const RETRIEVAL_OUTPUT_STRUCT = `{
|
export const RETRIEVAL_OUTPUT_STRUCT = `{
|
||||||
"content": "",
|
"content": "",
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
useWorkflowStore,
|
useWorkflowStore,
|
||||||
} from '../store'
|
} from '../store'
|
||||||
import {
|
import {
|
||||||
|
// PARALLEL_DEPTH_LIMIT,
|
||||||
PARALLEL_LIMIT,
|
PARALLEL_LIMIT,
|
||||||
SUPPORT_OUTPUT_VARS_NODE,
|
SUPPORT_OUTPUT_VARS_NODE,
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
@ -279,6 +280,53 @@ export const useWorkflow = () => {
|
|||||||
return isUsed
|
return isUsed
|
||||||
}, [isVarUsedInNodes])
|
}, [isVarUsedInNodes])
|
||||||
|
|
||||||
|
const checkParallelLimit = useCallback((nodeId: string) => {
|
||||||
|
const {
|
||||||
|
getNodes,
|
||||||
|
edges,
|
||||||
|
} = store.getState()
|
||||||
|
const nodes = getNodes()
|
||||||
|
const currentNode = nodes.find(node => node.id === nodeId)!
|
||||||
|
const sourceNodeOutgoers = getOutgoers(currentNode, nodes, edges)
|
||||||
|
if (sourceNodeOutgoers.length > PARALLEL_LIMIT - 1) {
|
||||||
|
const { setShowTips } = workflowStore.getState()
|
||||||
|
setShowTips(t('workflow.common.parallelTip.limit', { num: PARALLEL_LIMIT }))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// if (sourceNodeOutgoers.length > 0) {
|
||||||
|
// let hasOverDepth = false
|
||||||
|
// let parallelDepth = 1
|
||||||
|
// const traverse = (root: Node, depth: number) => {
|
||||||
|
// if (depth > PARALLEL_DEPTH_LIMIT) {
|
||||||
|
// hasOverDepth = true
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// if (depth > parallelDepth)
|
||||||
|
// parallelDepth = depth
|
||||||
|
|
||||||
|
// const incomerNodes = getIncomers(root, nodes, edges)
|
||||||
|
|
||||||
|
// if (incomerNodes.length) {
|
||||||
|
// incomerNodes.forEach((incomer) => {
|
||||||
|
// const incomerOutgoers = getOutgoers(incomer, nodes, edges)
|
||||||
|
|
||||||
|
// if (incomerOutgoers.length > 1)
|
||||||
|
// traverse(incomer, depth + 1)
|
||||||
|
// else
|
||||||
|
// traverse(incomer, depth)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// traverse(currentNode, parallelDepth)
|
||||||
|
// if (hasOverDepth) {
|
||||||
|
// const { setShowTips } = workflowStore.getState()
|
||||||
|
// setShowTips(t('workflow.common.parallelTip.depthLimit', { num: PARALLEL_DEPTH_LIMIT }))
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return true
|
||||||
|
}, [store, workflowStore, t])
|
||||||
|
|
||||||
const isValidConnection = useCallback(({ source, target }: Connection) => {
|
const isValidConnection = useCallback(({ source, target }: Connection) => {
|
||||||
const {
|
const {
|
||||||
edges,
|
edges,
|
||||||
@ -288,17 +336,12 @@ export const useWorkflow = () => {
|
|||||||
const sourceNode: Node = nodes.find(node => node.id === source)!
|
const sourceNode: Node = nodes.find(node => node.id === source)!
|
||||||
const targetNode: Node = nodes.find(node => node.id === target)!
|
const targetNode: Node = nodes.find(node => node.id === target)!
|
||||||
|
|
||||||
|
if (!checkParallelLimit(source!))
|
||||||
|
return false
|
||||||
|
|
||||||
if (sourceNode.type === CUSTOM_NOTE_NODE || targetNode.type === CUSTOM_NOTE_NODE)
|
if (sourceNode.type === CUSTOM_NOTE_NODE || targetNode.type === CUSTOM_NOTE_NODE)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
const sourceNodeOutgoers = getOutgoers(sourceNode, nodes, edges)
|
|
||||||
|
|
||||||
if (sourceNodeOutgoers.length > PARALLEL_LIMIT - 1) {
|
|
||||||
const { setShowTips } = workflowStore.getState()
|
|
||||||
setShowTips(t('workflow.common.parallelTip.limit', { num: PARALLEL_LIMIT }))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sourceNode && targetNode) {
|
if (sourceNode && targetNode) {
|
||||||
const sourceNodeAvailableNextNodes = nodesExtraData[sourceNode.data.type].availableNextNodes
|
const sourceNodeAvailableNextNodes = nodesExtraData[sourceNode.data.type].availableNextNodes
|
||||||
const targetNodeAvailablePrevNodes = [...nodesExtraData[targetNode.data.type].availablePrevNodes, BlockEnum.Start]
|
const targetNodeAvailablePrevNodes = [...nodesExtraData[targetNode.data.type].availablePrevNodes, BlockEnum.Start]
|
||||||
@ -325,7 +368,7 @@ export const useWorkflow = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !hasCycle(targetNode)
|
return !hasCycle(targetNode)
|
||||||
}, [store, nodesExtraData])
|
}, [store, nodesExtraData, checkParallelLimit])
|
||||||
|
|
||||||
const formatTimeFromNow = useCallback((time: number) => {
|
const formatTimeFromNow = useCallback((time: number) => {
|
||||||
return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow()
|
return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow()
|
||||||
@ -348,6 +391,7 @@ export const useWorkflow = () => {
|
|||||||
isVarUsedInNodes,
|
isVarUsedInNodes,
|
||||||
removeUsedVarInNodes,
|
removeUsedVarInNodes,
|
||||||
isNodeVarsUsedInNodes,
|
isNodeVarsUsedInNodes,
|
||||||
|
checkParallelLimit,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
formatTimeFromNow,
|
formatTimeFromNow,
|
||||||
getNode,
|
getNode,
|
||||||
|
@ -14,6 +14,12 @@ const LimitTips = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='absolute bottom-16 left-1/2 -translate-x-1/2 flex items-center rounded-xl p-2 h-10 border border-components-panel-border bg-components-panel-bg-blur shadow-md z-[9]'>
|
<div className='absolute bottom-16 left-1/2 -translate-x-1/2 flex items-center rounded-xl p-2 h-10 border border-components-panel-border bg-components-panel-bg-blur shadow-md z-[9]'>
|
||||||
|
<div
|
||||||
|
className='absolute inset-0 opacity-[0.4] rounded-xl'
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(92deg, rgba(247, 144, 9, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%)',
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
<div className='flex items-center justify-center w-5 h-5'>
|
<div className='flex items-center justify-center w-5 h-5'>
|
||||||
<RiAlertFill className='w-4 h-4 text-text-warning-secondary' />
|
<RiAlertFill className='w-4 h-4 text-text-warning-secondary' />
|
||||||
</div>
|
</div>
|
||||||
@ -21,6 +27,7 @@ const LimitTips = () => {
|
|||||||
{showTips}
|
{showTips}
|
||||||
</div>
|
</div>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className='z-[1]'
|
||||||
onClick={() => setShowTips('')}
|
onClick={() => setShowTips('')}
|
||||||
>
|
>
|
||||||
<RiCloseLine className='w-4 h-4' />
|
<RiCloseLine className='w-4 h-4' />
|
||||||
|
@ -19,12 +19,11 @@ import {
|
|||||||
useIsChatMode,
|
useIsChatMode,
|
||||||
useNodesInteractions,
|
useNodesInteractions,
|
||||||
useNodesReadOnly,
|
useNodesReadOnly,
|
||||||
|
useWorkflow,
|
||||||
} from '../../../hooks'
|
} from '../../../hooks'
|
||||||
import {
|
import {
|
||||||
useStore,
|
useStore,
|
||||||
useWorkflowStore,
|
|
||||||
} from '../../../store'
|
} from '../../../store'
|
||||||
import { PARALLEL_LIMIT } from '../../../constants'
|
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
|
||||||
type NodeHandleProps = {
|
type NodeHandleProps = {
|
||||||
@ -119,13 +118,13 @@ export const NodeSourceHandle = memo(({
|
|||||||
}: NodeHandleProps) => {
|
}: NodeHandleProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const notInitialWorkflow = useStore(s => s.notInitialWorkflow)
|
const notInitialWorkflow = useStore(s => s.notInitialWorkflow)
|
||||||
const workflowStore = useWorkflowStore()
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const { handleNodeAdd } = useNodesInteractions()
|
const { handleNodeAdd } = useNodesInteractions()
|
||||||
const { getNodesReadOnly } = useNodesReadOnly()
|
const { getNodesReadOnly } = useNodesReadOnly()
|
||||||
const { availableNextBlocks } = useAvailableBlocks(data.type, data.isInIteration)
|
const { availableNextBlocks } = useAvailableBlocks(data.type, data.isInIteration)
|
||||||
const isConnectable = !!availableNextBlocks.length
|
const isConnectable = !!availableNextBlocks.length
|
||||||
const isChatMode = useIsChatMode()
|
const isChatMode = useIsChatMode()
|
||||||
|
const { checkParallelLimit } = useWorkflow()
|
||||||
|
|
||||||
const connected = data._connectedSourceHandleIds?.includes(handleId)
|
const connected = data._connectedSourceHandleIds?.includes(handleId)
|
||||||
const handleOpenChange = useCallback((v: boolean) => {
|
const handleOpenChange = useCallback((v: boolean) => {
|
||||||
@ -133,13 +132,9 @@ export const NodeSourceHandle = memo(({
|
|||||||
}, [])
|
}, [])
|
||||||
const handleHandleClick = useCallback((e: MouseEvent) => {
|
const handleHandleClick = useCallback((e: MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (data._connectedSourceHandleIds!.length > PARALLEL_LIMIT - 1) {
|
if (checkParallelLimit(id))
|
||||||
const { setShowTips } = workflowStore.getState()
|
setOpen(v => !v)
|
||||||
setShowTips(t('workflow.common.parallelTip.limit', { num: PARALLEL_LIMIT }))
|
}, [checkParallelLimit, id])
|
||||||
return
|
|
||||||
}
|
|
||||||
setOpen(v => !v)
|
|
||||||
}, [data._connectedSourceHandleIds])
|
|
||||||
const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => {
|
const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => {
|
||||||
handleNodeAdd(
|
handleNodeAdd(
|
||||||
{
|
{
|
||||||
@ -156,7 +151,7 @@ export const NodeSourceHandle = memo(({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (notInitialWorkflow && data.type === BlockEnum.Start && !isChatMode)
|
if (notInitialWorkflow && data.type === BlockEnum.Start && !isChatMode)
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
}, [notInitialWorkflow, data.type])
|
}, [notInitialWorkflow, data.type, isChatMode])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -95,6 +95,7 @@ export function getIterationStartNode(iterationId: string): Node {
|
|||||||
title: '',
|
title: '',
|
||||||
desc: '',
|
desc: '',
|
||||||
type: BlockEnum.IterationStart,
|
type: BlockEnum.IterationStart,
|
||||||
|
isInIteration: true,
|
||||||
},
|
},
|
||||||
position: {
|
position: {
|
||||||
x: 24,
|
x: 24,
|
||||||
|
@ -88,6 +88,7 @@ const translation = {
|
|||||||
desc: ' to connect',
|
desc: ' to connect',
|
||||||
},
|
},
|
||||||
limit: 'Parallelism is limited to {{num}} branches.',
|
limit: 'Parallelism is limited to {{num}} branches.',
|
||||||
|
depthLimit: 'Parallel nesting layer limit of {{num}} layers',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
|
@ -88,6 +88,7 @@ const translation = {
|
|||||||
desc: '连接节点',
|
desc: '连接节点',
|
||||||
},
|
},
|
||||||
limit: '并行分支限制为 {{num}} 个',
|
limit: '并行分支限制为 {{num}} 个',
|
||||||
|
depthLimit: '并行嵌套层数限制 {{num}} 层',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user