| import { | |
| useCallback, | |
| useMemo, | |
| } from 'react' | |
| import { useTranslation } from 'react-i18next' | |
| import { useStoreApi } from 'reactflow' | |
| import type { | |
| Edge, | |
| Node, | |
| } from '../types' | |
| import { BlockEnum } from '../types' | |
| import { useStore } from '../store' | |
| import { | |
| getToolCheckParams, | |
| getValidTreeNodes, | |
| } from '../utils' | |
| import { | |
| CUSTOM_NODE, | |
| MAX_TREE_DEPTH, | |
| } from '../constants' | |
| import type { ToolNodeType } from '../nodes/tool/types' | |
| import { useIsChatMode } from './use-workflow' | |
| import { useNodesExtraData } from './use-nodes-data' | |
| import { useToastContext } from '@/app/components/base/toast' | |
| import { CollectionType } from '@/app/components/tools/types' | |
| import { useGetLanguage } from '@/context/i18n' | |
| export const useChecklist = (nodes: Node[], edges: Edge[]) => { | |
| const { t } = useTranslation() | |
| const language = useGetLanguage() | |
| const nodesExtraData = useNodesExtraData() | |
| const isChatMode = useIsChatMode() | |
| const buildInTools = useStore(s => s.buildInTools) | |
| const customTools = useStore(s => s.customTools) | |
| const workflowTools = useStore(s => s.workflowTools) | |
| const needWarningNodes = useMemo(() => { | |
| const list = [] | |
| const { validNodes } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges) | |
| for (let i = 0; i < nodes.length; i++) { | |
| const node = nodes[i] | |
| let toolIcon | |
| let moreDataForCheckValid | |
| if (node.data.type === BlockEnum.Tool) { | |
| const { provider_type } = node.data | |
| moreDataForCheckValid = getToolCheckParams(node.data as ToolNodeType, buildInTools, customTools, workflowTools, language) | |
| if (provider_type === CollectionType.builtIn) | |
| toolIcon = buildInTools.find(tool => tool.id === node.data.provider_id)?.icon | |
| if (provider_type === CollectionType.custom) | |
| toolIcon = customTools.find(tool => tool.id === node.data.provider_id)?.icon | |
| if (provider_type === CollectionType.workflow) | |
| toolIcon = workflowTools.find(tool => tool.id === node.data.provider_id)?.icon | |
| } | |
| if (node.type === CUSTOM_NODE) { | |
| const { errorMessage } = nodesExtraData[node.data.type].checkValid(node.data, t, moreDataForCheckValid) | |
| if (errorMessage || !validNodes.find(n => n.id === node.id)) { | |
| list.push({ | |
| id: node.id, | |
| type: node.data.type, | |
| title: node.data.title, | |
| toolIcon, | |
| unConnected: !validNodes.find(n => n.id === node.id), | |
| errorMessage, | |
| }) | |
| } | |
| } | |
| } | |
| if (isChatMode && !nodes.find(node => node.data.type === BlockEnum.Answer)) { | |
| list.push({ | |
| id: 'answer-need-added', | |
| type: BlockEnum.Answer, | |
| title: t('workflow.blocks.answer'), | |
| errorMessage: t('workflow.common.needAnswerNode'), | |
| }) | |
| } | |
| if (!isChatMode && !nodes.find(node => node.data.type === BlockEnum.End)) { | |
| list.push({ | |
| id: 'end-need-added', | |
| type: BlockEnum.End, | |
| title: t('workflow.blocks.end'), | |
| errorMessage: t('workflow.common.needEndNode'), | |
| }) | |
| } | |
| return list | |
| }, [t, nodes, edges, nodesExtraData, buildInTools, customTools, workflowTools, language, isChatMode]) | |
| return needWarningNodes | |
| } | |
| export const useChecklistBeforePublish = () => { | |
| const { t } = useTranslation() | |
| const language = useGetLanguage() | |
| const buildInTools = useStore(s => s.buildInTools) | |
| const customTools = useStore(s => s.customTools) | |
| const workflowTools = useStore(s => s.workflowTools) | |
| const { notify } = useToastContext() | |
| const isChatMode = useIsChatMode() | |
| const store = useStoreApi() | |
| const nodesExtraData = useNodesExtraData() | |
| const handleCheckBeforePublish = useCallback(() => { | |
| const { | |
| getNodes, | |
| edges, | |
| } = store.getState() | |
| const nodes = getNodes().filter(node => node.type === CUSTOM_NODE) | |
| const { | |
| validNodes, | |
| maxDepth, | |
| } = getValidTreeNodes(nodes.filter(node => node.type === CUSTOM_NODE), edges) | |
| if (maxDepth > MAX_TREE_DEPTH) { | |
| notify({ type: 'error', message: t('workflow.common.maxTreeDepth', { depth: MAX_TREE_DEPTH }) }) | |
| return false | |
| } | |
| for (let i = 0; i < nodes.length; i++) { | |
| const node = nodes[i] | |
| let moreDataForCheckValid | |
| if (node.data.type === BlockEnum.Tool) | |
| moreDataForCheckValid = getToolCheckParams(node.data as ToolNodeType, buildInTools, customTools, workflowTools, language) | |
| const { errorMessage } = nodesExtraData[node.data.type as BlockEnum].checkValid(node.data, t, moreDataForCheckValid) | |
| if (errorMessage) { | |
| notify({ type: 'error', message: `[${node.data.title}] ${errorMessage}` }) | |
| return false | |
| } | |
| if (!validNodes.find(n => n.id === node.id)) { | |
| notify({ type: 'error', message: `[${node.data.title}] ${t('workflow.common.needConnectTip')}` }) | |
| return false | |
| } | |
| } | |
| if (isChatMode && !nodes.find(node => node.data.type === BlockEnum.Answer)) { | |
| notify({ type: 'error', message: t('workflow.common.needAnswerNode') }) | |
| return false | |
| } | |
| if (!isChatMode && !nodes.find(node => node.data.type === BlockEnum.End)) { | |
| notify({ type: 'error', message: t('workflow.common.needEndNode') }) | |
| return false | |
| } | |
| return true | |
| }, [nodesExtraData, notify, t, store, isChatMode, buildInTools, customTools, workflowTools, language]) | |
| return { | |
| handleCheckBeforePublish, | |
| } | |
| } | |