mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 17:38:04 +08:00
fix: restore unified workflow validation system
Major fixes to workflow checklist validation: ## Fixed getValidTreeNodes function (workflow.ts) - Restore original function signature: (nodes, edges) instead of (startNode, nodes, edges) - Re-implement automatic start node discovery for all entry types - Unified traversal from Start, TriggerWebhook, TriggerSchedule, TriggerPlugin nodes - Single call now discovers all valid connected nodes correctly ## Simplified useChecklist validation (use-checklist.ts) - Remove complex manual start node iteration and result aggregation - Unified entry node validation concept for all start node types - Remove dependency on getStartNodes() utility - Simplified validation logic matching backup branch approach ## Resolved Issues - ✅ End node connectivity: Now correctly detects connections from any entry node - ✅ Unified entry validation: All start types (Start/Triggers) validated consistently - ✅ Simplified architecture: Restored proven validation approach from backup branch This restores the reliable workflow validation system while maintaining trigger node support.
This commit is contained in:
@ -27,7 +27,6 @@ import {
|
|||||||
} from '../constants'
|
} from '../constants'
|
||||||
import {
|
import {
|
||||||
useGetToolIcon,
|
useGetToolIcon,
|
||||||
useWorkflow,
|
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
import type { ToolNodeType } from '../nodes/tool/types'
|
import type { ToolNodeType } from '../nodes/tool/types'
|
||||||
import type { DataSourceNodeType } from '../nodes/data-source/types'
|
import type { DataSourceNodeType } from '../nodes/data-source/types'
|
||||||
@ -54,7 +53,6 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
|||||||
const dataSourceList = useStore(s => s.dataSourceList)
|
const dataSourceList = useStore(s => s.dataSourceList)
|
||||||
const { data: strategyProviders } = useStrategyProviders()
|
const { data: strategyProviders } = useStrategyProviders()
|
||||||
const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail)
|
const datasetsDetail = useDatasetsDetailStore(s => s.datasetsDetail)
|
||||||
const { getStartNodes } = useWorkflow()
|
|
||||||
const getToolIcon = useGetToolIcon()
|
const getToolIcon = useGetToolIcon()
|
||||||
|
|
||||||
const map = useNodesAvailableVarList(nodes)
|
const map = useNodesAvailableVarList(nodes)
|
||||||
@ -79,13 +77,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
|||||||
const needWarningNodes = useMemo(() => {
|
const needWarningNodes = useMemo(() => {
|
||||||
const list = []
|
const list = []
|
||||||
const filteredNodes = nodes.filter(node => node.type === CUSTOM_NODE)
|
const filteredNodes = nodes.filter(node => node.type === CUSTOM_NODE)
|
||||||
const startNodes = getStartNodes(filteredNodes)
|
const { validNodes } = getValidTreeNodes(filteredNodes, edges)
|
||||||
const validNodesFlattened = startNodes.map(startNode => getValidTreeNodes(startNode, filteredNodes, edges))
|
|
||||||
const validNodes = validNodesFlattened.reduce((acc, curr) => {
|
|
||||||
if (curr.validNodes)
|
|
||||||
acc.push(...curr.validNodes)
|
|
||||||
return acc
|
|
||||||
}, [] as Node[])
|
|
||||||
|
|
||||||
for (let i = 0; i < filteredNodes.length; i++) {
|
for (let i = 0; i < filteredNodes.length; i++) {
|
||||||
const node = filteredNodes[i]
|
const node = filteredNodes[i]
|
||||||
@ -192,7 +184,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}, [nodes, getStartNodes, nodesExtraData, edges, buildInTools, customTools, workflowTools, language, dataSourceList, getToolIcon, strategyProviders, getCheckData, t, map])
|
}, [nodes, nodesExtraData, edges, buildInTools, customTools, workflowTools, language, dataSourceList, getToolIcon, strategyProviders, getCheckData, t, map])
|
||||||
|
|
||||||
return needWarningNodes
|
return needWarningNodes
|
||||||
}
|
}
|
||||||
@ -206,7 +198,6 @@ export const useChecklistBeforePublish = () => {
|
|||||||
const { data: strategyProviders } = useStrategyProviders()
|
const { data: strategyProviders } = useStrategyProviders()
|
||||||
const updateDatasetsDetail = useDatasetsDetailStore(s => s.updateDatasetsDetail)
|
const updateDatasetsDetail = useDatasetsDetailStore(s => s.updateDatasetsDetail)
|
||||||
const updateTime = useRef(0)
|
const updateTime = useRef(0)
|
||||||
const { getStartNodes } = useWorkflow()
|
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
const { getNodesAvailableVarList } = useGetNodesAvailableVarList()
|
const { getNodesAvailableVarList } = useGetNodesAvailableVarList()
|
||||||
|
|
||||||
@ -244,20 +235,11 @@ export const useChecklistBeforePublish = () => {
|
|||||||
} = workflowStore.getState()
|
} = workflowStore.getState()
|
||||||
const nodes = getNodes()
|
const nodes = getNodes()
|
||||||
const filteredNodes = nodes.filter(node => node.type === CUSTOM_NODE)
|
const filteredNodes = nodes.filter(node => node.type === CUSTOM_NODE)
|
||||||
const startNodes = getStartNodes(filteredNodes)
|
const { validNodes, maxDepth } = getValidTreeNodes(filteredNodes, edges)
|
||||||
const validNodesFlattened = startNodes.map(startNode => getValidTreeNodes(startNode, filteredNodes, edges))
|
|
||||||
const validNodes = validNodesFlattened.reduce((acc, curr) => {
|
|
||||||
if (curr.validNodes)
|
|
||||||
acc.push(...curr.validNodes)
|
|
||||||
return acc
|
|
||||||
}, [] as Node[])
|
|
||||||
const maxDepthArr = validNodesFlattened.map(item => item.maxDepth)
|
|
||||||
|
|
||||||
for (let i = 0; i < maxDepthArr.length; i++) {
|
if (maxDepth > MAX_TREE_DEPTH) {
|
||||||
if (maxDepthArr[i] > MAX_TREE_DEPTH) {
|
notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) })
|
||||||
notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) })
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Before publish, we need to fetch datasets detail, in case of the settings of datasets have been changed
|
// Before publish, we need to fetch datasets detail, in case of the settings of datasets have been changed
|
||||||
const knowledgeRetrievalNodes = filteredNodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval)
|
const knowledgeRetrievalNodes = filteredNodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval)
|
||||||
@ -360,7 +342,7 @@ export const useChecklistBeforePublish = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, [store, notify, t, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, getStartNodes, workflowStore])
|
}, [store, notify, t, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, workflowStore])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleCheckBeforePublish,
|
handleCheckBeforePublish,
|
||||||
|
|||||||
@ -92,8 +92,16 @@ export const getNodesConnectedSourceOrTargetHandleIdsMap = (changes: ConnectedSo
|
|||||||
return nodesConnectedSourceOrTargetHandleIdsMap
|
return nodesConnectedSourceOrTargetHandleIdsMap
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getValidTreeNodes = (startNode: Node, nodes: Node[], edges: Edge[]) => {
|
export const getValidTreeNodes = (nodes: Node[], edges: Edge[]) => {
|
||||||
if (!startNode) {
|
// Find all start nodes (Start and Trigger nodes)
|
||||||
|
const startNodes = nodes.filter(node =>
|
||||||
|
node.data.type === BlockEnum.Start
|
||||||
|
|| node.data.type === BlockEnum.TriggerSchedule
|
||||||
|
|| node.data.type === BlockEnum.TriggerWebhook
|
||||||
|
|| node.data.type === BlockEnum.TriggerPlugin,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (startNodes.length === 0) {
|
||||||
return {
|
return {
|
||||||
validNodes: [],
|
validNodes: [],
|
||||||
maxDepth: 0,
|
maxDepth: 0,
|
||||||
@ -134,18 +142,11 @@ export const getValidTreeNodes = (startNode: Node, nodes: Node[], edges: Edge[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const startNodes = nodes.filter(node =>
|
// Start traversal from all start nodes
|
||||||
// node.data.type === BlockEnum.Start
|
startNodes.forEach((startNode) => {
|
||||||
// || node.data.type === BlockEnum.TriggerSchedule
|
if (!list.find(n => n.id === startNode.id))
|
||||||
// || node.data.type === BlockEnum.TriggerWebhook
|
traverse(startNode, 1)
|
||||||
// || node.data.type === BlockEnum.TriggerPlugin,
|
})
|
||||||
// )
|
|
||||||
|
|
||||||
// // Start traversal from all start nodes
|
|
||||||
// startNodes.forEach((startNode) => {
|
|
||||||
// if (!list.find(n => n.id === startNode.id))
|
|
||||||
// traverse(startNode, 1)
|
|
||||||
// })
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
validNodes: uniqBy(list, 'id'),
|
validNodes: uniqBy(list, 'id'),
|
||||||
|
|||||||
Reference in New Issue
Block a user