mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-18 05:45:59 +08:00
fix: parallel branch limit
This commit is contained in:
parent
ae22015fe7
commit
f43596f226
@ -326,6 +326,7 @@ export const ITERATION_PADDING = {
|
|||||||
bottom: 20,
|
bottom: 20,
|
||||||
left: 16,
|
left: 16,
|
||||||
}
|
}
|
||||||
|
export const PARALLEL_LIMIT = 10
|
||||||
|
|
||||||
export const RETRIEVAL_OUTPUT_STRUCT = `{
|
export const RETRIEVAL_OUTPUT_STRUCT = `{
|
||||||
"content": "",
|
"content": "",
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
useReactFlow,
|
useReactFlow,
|
||||||
useStoreApi,
|
useStoreApi,
|
||||||
} from 'reactflow'
|
} from 'reactflow'
|
||||||
|
import { uniq } from 'lodash-es'
|
||||||
import type { ToolDefaultValue } from '../block-selector/types'
|
import type { ToolDefaultValue } from '../block-selector/types'
|
||||||
import type {
|
import type {
|
||||||
Edge,
|
Edge,
|
||||||
@ -212,7 +213,7 @@ export const useNodesInteractions = () => {
|
|||||||
setEdges(newEdges)
|
setEdges(newEdges)
|
||||||
const incomesNodes = getIncomers(node, nodes, edges)
|
const incomesNodes = getIncomers(node, nodes, edges)
|
||||||
if (incomesNodes.length) {
|
if (incomesNodes.length) {
|
||||||
const incomesNodesOutgoersId = incomesNodes.map(incomeNode => getOutgoers(incomeNode, nodes, edges)).flat().map(outgoer => outgoer.id)
|
const incomesNodesOutgoersId = uniq(incomesNodes.map(incomeNode => getOutgoers(incomeNode, nodes, edges)).flat().map(outgoer => outgoer.id))
|
||||||
|
|
||||||
if (incomesNodesOutgoersId.length > 1) {
|
if (incomesNodesOutgoersId.length > 1) {
|
||||||
const newNodes = produce(nodes, (draft) => {
|
const newNodes = produce(nodes, (draft) => {
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { uniqBy } from 'lodash-es'
|
import { uniqBy } from 'lodash-es'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import {
|
import {
|
||||||
getIncomers,
|
getIncomers,
|
||||||
@ -29,6 +30,7 @@ import {
|
|||||||
useWorkflowStore,
|
useWorkflowStore,
|
||||||
} from '../store'
|
} from '../store'
|
||||||
import {
|
import {
|
||||||
|
PARALLEL_LIMIT,
|
||||||
SUPPORT_OUTPUT_VARS_NODE,
|
SUPPORT_OUTPUT_VARS_NODE,
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
|
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
|
||||||
@ -59,6 +61,7 @@ export const useIsChatMode = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useWorkflow = () => {
|
export const useWorkflow = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const { locale } = useContext(I18n)
|
||||||
const store = useStoreApi()
|
const store = useStoreApi()
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
@ -288,6 +291,14 @@ export const useWorkflow = () => {
|
|||||||
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]
|
||||||
|
@ -69,6 +69,7 @@ import NodeContextmenu from './node-contextmenu'
|
|||||||
import SyncingDataModal from './syncing-data-modal'
|
import SyncingDataModal from './syncing-data-modal'
|
||||||
import UpdateDSLModal from './update-dsl-modal'
|
import UpdateDSLModal from './update-dsl-modal'
|
||||||
import DSLExportConfirmModal from './dsl-export-confirm-modal'
|
import DSLExportConfirmModal from './dsl-export-confirm-modal'
|
||||||
|
import LimitTips from './limit-tips'
|
||||||
import {
|
import {
|
||||||
useStore,
|
useStore,
|
||||||
useWorkflowStore,
|
useWorkflowStore,
|
||||||
@ -320,6 +321,7 @@ const Workflow: FC<WorkflowProps> = memo(({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
<LimitTips />
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
edgeTypes={edgeTypes}
|
edgeTypes={edgeTypes}
|
||||||
|
32
web/app/components/workflow/limit-tips.tsx
Normal file
32
web/app/components/workflow/limit-tips.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
RiAlertFill,
|
||||||
|
RiCloseLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import { useStore } from './store'
|
||||||
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
|
|
||||||
|
const LimitTips = () => {
|
||||||
|
const showTips = useStore(s => s.showTips)
|
||||||
|
const setShowTips = useStore(s => s.setShowTips)
|
||||||
|
|
||||||
|
if (!showTips)
|
||||||
|
return null
|
||||||
|
|
||||||
|
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='flex items-center justify-center w-5 h-5'>
|
||||||
|
<RiAlertFill className='w-4 h-4 text-text-warning-secondary' />
|
||||||
|
</div>
|
||||||
|
<div className='mx-1 px-1 system-xs-medium text-text-primary'>
|
||||||
|
{showTips}
|
||||||
|
</div>
|
||||||
|
<ActionButton
|
||||||
|
onClick={() => setShowTips('')}
|
||||||
|
>
|
||||||
|
<RiCloseLine className='w-4 h-4' />
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LimitTips
|
@ -16,10 +16,15 @@ import BlockSelector from '../../../block-selector'
|
|||||||
import type { ToolDefaultValue } from '../../../block-selector/types'
|
import type { ToolDefaultValue } from '../../../block-selector/types'
|
||||||
import {
|
import {
|
||||||
useAvailableBlocks,
|
useAvailableBlocks,
|
||||||
|
useIsChatMode,
|
||||||
useNodesInteractions,
|
useNodesInteractions,
|
||||||
useNodesReadOnly,
|
useNodesReadOnly,
|
||||||
} from '../../../hooks'
|
} from '../../../hooks'
|
||||||
import { useStore } from '../../../store'
|
import {
|
||||||
|
useStore,
|
||||||
|
useWorkflowStore,
|
||||||
|
} 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 = {
|
||||||
@ -114,11 +119,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 connected = data._connectedSourceHandleIds?.includes(handleId)
|
const connected = data._connectedSourceHandleIds?.includes(handleId)
|
||||||
const handleOpenChange = useCallback((v: boolean) => {
|
const handleOpenChange = useCallback((v: boolean) => {
|
||||||
@ -126,8 +133,13 @@ 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) {
|
||||||
|
const { setShowTips } = workflowStore.getState()
|
||||||
|
setShowTips(t('workflow.common.parallelTip.limit', { num: PARALLEL_LIMIT }))
|
||||||
|
return
|
||||||
|
}
|
||||||
setOpen(v => !v)
|
setOpen(v => !v)
|
||||||
}, [])
|
}, [data._connectedSourceHandleIds])
|
||||||
const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => {
|
const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => {
|
||||||
handleNodeAdd(
|
handleNodeAdd(
|
||||||
{
|
{
|
||||||
@ -142,7 +154,7 @@ export const NodeSourceHandle = memo(({
|
|||||||
}, [handleNodeAdd, id, handleId])
|
}, [handleNodeAdd, id, handleId])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (notInitialWorkflow && data.type === BlockEnum.Start)
|
if (notInitialWorkflow && data.type === BlockEnum.Start && !isChatMode)
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
}, [notInitialWorkflow, data.type])
|
}, [notInitialWorkflow, data.type])
|
||||||
|
|
||||||
|
@ -162,6 +162,8 @@ type Shape = {
|
|||||||
setControlPromptEditorRerenderKey: (controlPromptEditorRerenderKey: number) => void
|
setControlPromptEditorRerenderKey: (controlPromptEditorRerenderKey: number) => void
|
||||||
showImportDSLModal: boolean
|
showImportDSLModal: boolean
|
||||||
setShowImportDSLModal: (showImportDSLModal: boolean) => void
|
setShowImportDSLModal: (showImportDSLModal: boolean) => void
|
||||||
|
showTips: string
|
||||||
|
setShowTips: (showTips: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createWorkflowStore = () => {
|
export const createWorkflowStore = () => {
|
||||||
@ -262,6 +264,8 @@ export const createWorkflowStore = () => {
|
|||||||
setControlPromptEditorRerenderKey: controlPromptEditorRerenderKey => set(() => ({ controlPromptEditorRerenderKey })),
|
setControlPromptEditorRerenderKey: controlPromptEditorRerenderKey => set(() => ({ controlPromptEditorRerenderKey })),
|
||||||
showImportDSLModal: false,
|
showImportDSLModal: false,
|
||||||
setShowImportDSLModal: showImportDSLModal => set(() => ({ showImportDSLModal })),
|
setShowImportDSLModal: showImportDSLModal => set(() => ({ showImportDSLModal })),
|
||||||
|
showTips: '',
|
||||||
|
setShowTips: showTips => set(() => ({ showTips })),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ const translation = {
|
|||||||
title: 'Drag',
|
title: 'Drag',
|
||||||
desc: ' to connect',
|
desc: ' to connect',
|
||||||
},
|
},
|
||||||
|
limit: 'Parallelism is limited to {{num}} branches.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
|
@ -87,6 +87,7 @@ const translation = {
|
|||||||
title: '拖拽',
|
title: '拖拽',
|
||||||
desc: '连接节点',
|
desc: '连接节点',
|
||||||
},
|
},
|
||||||
|
limit: '并行分支限制为 {{num}} 个',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user