From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001 From: wwf <yearningwang@iqtogether.com> Date: 星期三, 04 六月 2025 15:17:49 +0800 Subject: [PATCH] 初始化 --- app/components/workflow/hooks/use-workflow.ts | 179 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 files changed, 130 insertions(+), 49 deletions(-) diff --git a/app/components/workflow/hooks/use-workflow.ts b/app/components/workflow/hooks/use-workflow.ts index 99dce4d..2eafb4a 100644 --- a/app/components/workflow/hooks/use-workflow.ts +++ b/app/components/workflow/hooks/use-workflow.ts @@ -1,9 +1,13 @@ import { useCallback, + useEffect, useMemo, + useState, } from 'react' +import dayjs from 'dayjs' import { uniqBy } from 'lodash-es' import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' import { getIncomers, getOutgoers, @@ -36,17 +40,24 @@ 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 { useWorkflowTemplate } from './use-workflow-template' import { useStore as useAppStore } from '@/app/components/app/store' +import { + fetchNodesDefaultConfigs, + fetchPublishedWorkflow, + fetchWorkflowDraft, + syncWorkflowDraft, +} from '@/service/workflow' +import type { FetchWorkflowDraftResponse } from '@/types/workflow' import { fetchAllBuiltInTools, fetchAllCustomTools, fetchAllWorkflowTools, } from '@/service/tools' +import I18n from '@/context/i18n' 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 { useWorkflowConfig } from '@/service/use-workflow' export const useIsChatMode = () => { const appDetail = useAppStore(s => s.appDetail) @@ -56,9 +67,12 @@ export const useWorkflow = () => { const { t } = useTranslation() + const { locale } = useContext(I18n) const store = useStoreApi() const workflowStore = useWorkflowStore() + const appId = useStore(s => s.appId) const nodesExtraData = useNodesExtraData() + const { data: workflowConfig } = useWorkflowConfig(appId) const setPanelWidth = useCallback((width: number) => { localStorage.setItem('workflow-node-panel-width', `${width}`) workflowStore.setState({ panelWidth: width }) @@ -74,7 +88,7 @@ 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)) + startNode = nodes.find(node => node.parentId === currentNode.parentId && node.type === CUSTOM_ITERATION_START_NODE) if (!startNode) return [] @@ -103,7 +117,7 @@ list.push(...incomers) - return uniqBy(list, 'id').filter((item: Node) => { + return uniqBy(list, 'id').filter((item) => { return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type) }) }, [store]) @@ -150,7 +164,7 @@ const length = list.length if (length) { - return uniqBy(list, 'id').reverse().filter((item: Node) => { + return uniqBy(list, 'id').reverse().filter((item) => { return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type) }) } @@ -224,15 +238,6 @@ return nodes.filter(node => node.parentId === nodeId) }, [store]) - const getLoopNodeChildren = useCallback((nodeId: string) => { - const { - getNodes, - } = store.getState() - const nodes = getNodes() - - return nodes.filter(node => node.parentId === nodeId) - }, [store]) - const isFromStartNode = useCallback((nodeId: string) => { const { getNodes } = store.getState() const nodes = getNodes() @@ -274,7 +279,7 @@ setNodes(newNodes) } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [store]) const isVarUsedInNodes = useCallback((varSelector: ValueSelector) => { @@ -327,7 +332,6 @@ parallelList, hasAbnormalEdges, } = getParallelInfo(nodes, edges, parentNodeId) - const { workflowConfig } = workflowStore.getState() if (hasAbnormalEdges) return false @@ -343,7 +347,7 @@ } return true - }, [t, workflowStore]) + }, [t, workflowStore, workflowConfig?.parallel_depth_limit]) const isValidConnection = useCallback(({ source, sourceHandle, target }: Connection) => { const { @@ -391,6 +395,10 @@ return !hasCycle(targetNode) }, [store, nodesExtraData, checkParallelLimit]) + const formatTimeFromNow = useCallback((time: number) => { + return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow() + }, [locale]) + const getNode = useCallback((nodeId?: string) => { const { getNodes } = store.getState() const nodes = getNodes() @@ -412,10 +420,10 @@ checkNestedParallelLimit, isValidConnection, isFromStartNode, + formatTimeFromNow, getNode, getBeforeNodeById, getIterationNodeChildren, - getLoopNodeChildren, } } @@ -426,12 +434,6 @@ if (type === 'builtin') { const buildInTools = await fetchAllBuiltInTools() - if (basePath) { - buildInTools.forEach((item) => { - if (typeof item.icon == 'string' && !item.icon.includes(basePath)) - item.icon = `${basePath}${item.icon}` - }) - } workflowStore.setState({ buildInTools: buildInTools || [], }) @@ -454,6 +456,108 @@ return { handleFetchAllTools, + } +} + +export const useWorkflowInit = () => { + const workflowStore = useWorkflowStore() + const { + nodes: nodesTemplate, + edges: edgesTemplate, + } = useWorkflowTemplate() + const { handleFetchAllTools } = useFetchToolsData() + const appDetail = useAppStore(state => state.appDetail)! + const setSyncWorkflowDraftHash = useStore(s => s.setSyncWorkflowDraftHash) + const [data, setData] = useState<FetchWorkflowDraftResponse>() + const [isLoading, setIsLoading] = useState(true) + useEffect(() => { + workflowStore.setState({ appId: appDetail.id }) + }, [appDetail.id, workflowStore]) + + const handleGetInitialWorkflowData = useCallback(async () => { + try { + const res = await fetchWorkflowDraft(`/apps/${appDetail.id}/workflows/draft`) + setData(res) + workflowStore.setState({ + envSecrets: (res.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => { + acc[env.id] = env.value + return acc + }, {} as Record<string, string>), + environmentVariables: res.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [], + // #TODO chatVar sync# + conversationVariables: res.conversation_variables || [], + }) + setSyncWorkflowDraftHash(res.hash) + setIsLoading(false) + } + catch (error: any) { + if (error && error.json && !error.bodyUsed && appDetail) { + error.json().then((err: any) => { + if (err.code === 'draft_workflow_not_exist') { + workflowStore.setState({ notInitialWorkflow: true }) + syncWorkflowDraft({ + url: `/apps/${appDetail.id}/workflows/draft`, + params: { + graph: { + nodes: nodesTemplate, + edges: edgesTemplate, + }, + features: { + retriever_resource: { enabled: true }, + }, + environment_variables: [], + conversation_variables: [], + }, + }).then((res) => { + workflowStore.getState().setDraftUpdatedAt(res.updated_at) + handleGetInitialWorkflowData() + }) + } + }) + } + } + }, [appDetail, nodesTemplate, edgesTemplate, workflowStore, setSyncWorkflowDraftHash]) + + useEffect(() => { + handleGetInitialWorkflowData() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const handleFetchPreloadData = useCallback(async () => { + try { + const nodesDefaultConfigsData = await fetchNodesDefaultConfigs(`/apps/${appDetail?.id}/workflows/default-workflow-block-configs`) + const publishedWorkflow = await fetchPublishedWorkflow(`/apps/${appDetail?.id}/workflows/publish`) + workflowStore.setState({ + nodesDefaultConfigs: nodesDefaultConfigsData.reduce((acc, block) => { + if (!acc[block.type]) + acc[block.type] = { ...block.config } + return acc + }, {} as Record<string, any>), + }) + workflowStore.getState().setPublishedAt(publishedWorkflow?.created_at) + } + catch (e) { + + } + }, [workflowStore, appDetail]) + + useEffect(() => { + handleFetchPreloadData() + handleFetchAllTools('builtin') + handleFetchAllTools('custom') + handleFetchAllTools('workflow') + }, [handleFetchPreloadData, handleFetchAllTools]) + + useEffect(() => { + if (data) { + workflowStore.getState().setDraftUpdatedAt(data.updated_at) + workflowStore.getState().setToolPublished(data.tool_published) + } + }, [data, workflowStore]) + + return { + data, + isLoading, } } @@ -505,7 +609,7 @@ targetTools = customTools else targetTools = workflowTools - return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon + return targetTools.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.icon } }, [data, buildInTools, customTools, workflowTools]) @@ -532,28 +636,5 @@ }, [iterationId, store]) return { isNodeInIteration, - } -} - -export const useIsNodeInLoop = (loopId: string) => { - const store = useStoreApi() - - const isNodeInLoop = useCallback((nodeId: string) => { - const { - getNodes, - } = store.getState() - const nodes = getNodes() - const node = nodes.find(node => node.id === nodeId) - - if (!node) - return false - - if (node.parentId === loopId) - return true - - return false - }, [loopId, store]) - return { - isNodeInLoop, } } -- Gitblit v1.8.0