diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx index a2d41dbfa7..1da62befd1 100644 --- a/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx +++ b/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx @@ -1,14 +1,60 @@ -import { memo } from 'react' +import { + memo, + useState, +} from 'react' import { useStore } from '../../workflow/store' import InputField from './input-field' import RagPipelinePanel from './panel' import RagPipelineHeader from './rag-pipeline-header' +import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { DSL_EXPORT_CHECK } from '@/app/components/workflow/constants' +import UpdateDSLModal from '@/app/components/workflow/update-dsl-modal' +import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal' +import { + useDSL, + usePanelInteractions, +} from '@/app/components/workflow/hooks' +import { useEventEmitterContextContext } from '@/context/event-emitter' const RagPipelineChildren = () => { + const { eventEmitter } = useEventEmitterContextContext() + const [secretEnvList, setSecretEnvList] = useState([]) const showInputFieldDialog = useStore(state => state.showInputFieldDialog) + const showImportDSLModal = useStore(s => s.showImportDSLModal) + const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal) + const { + handlePaneContextmenuCancel, + } = usePanelInteractions() + const { + exportCheck, + handleExportDSL, + } = useDSL() + + eventEmitter?.useSubscription((v: any) => { + if (v.type === DSL_EXPORT_CHECK) + setSecretEnvList(v.payload.data as EnvironmentVariable[]) + }) return ( <> + { + showImportDSLModal && ( + setShowImportDSLModal(false)} + onBackup={exportCheck!} + onImport={handlePaneContextmenuCancel} + /> + ) + } + { + secretEnvList.length > 0 && ( + setSecretEnvList([])} + /> + ) + } { diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-main.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-main.tsx index 53de99609e..cf9b9744e9 100644 --- a/web/app/components/rag-pipeline/components/rag-pipeline-main.tsx +++ b/web/app/components/rag-pipeline/components/rag-pipeline-main.tsx @@ -6,6 +6,7 @@ import type { WorkflowProps } from '@/app/components/workflow' import RagPipelineChildren from './rag-pipeline-children' import { useAvailableNodesMetaData, + useDSL, useGetRunAndTraceUrl, useNodesSyncDraft, usePipelineRefreshDraft, @@ -37,6 +38,10 @@ const RagPipelineMain = ({ } = usePipelineStartRun() const availableNodesMetaData = useAvailableNodesMetaData() const { getWorkflowRunAndTraceUrl } = useGetRunAndTraceUrl() + const { + exportCheck, + handleExportDSL, + } = useDSL() const hooksStore = useMemo(() => { return { @@ -52,6 +57,8 @@ const RagPipelineMain = ({ handleStartWorkflowRun, handleWorkflowStartRunInWorkflow, getWorkflowRunAndTraceUrl, + exportCheck, + handleExportDSL, } }, [ availableNodesMetaData, @@ -66,6 +73,8 @@ const RagPipelineMain = ({ handleStartWorkflowRun, handleWorkflowStartRunInWorkflow, getWorkflowRunAndTraceUrl, + exportCheck, + handleExportDSL, ]) return ( diff --git a/web/app/components/rag-pipeline/hooks/index.ts b/web/app/components/rag-pipeline/hooks/index.ts index 216a6d51cb..3754ddb02d 100644 --- a/web/app/components/rag-pipeline/hooks/index.ts +++ b/web/app/components/rag-pipeline/hooks/index.ts @@ -5,3 +5,4 @@ export * from './use-pipeline-run' export * from './use-pipeline-start-run' export * from './use-pipeline-init' export * from './use-get-run-and-trace-url' +export * from './use-DSL' diff --git a/web/app/components/rag-pipeline/hooks/use-DSL.ts b/web/app/components/rag-pipeline/hooks/use-DSL.ts new file mode 100644 index 0000000000..36ac6b9e0b --- /dev/null +++ b/web/app/components/rag-pipeline/hooks/use-DSL.ts @@ -0,0 +1,81 @@ +import { + useCallback, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import { + DSL_EXPORT_CHECK, +} from '@/app/components/workflow/constants' +import { useNodesSyncDraft } from './use-nodes-sync-draft' +import { useEventEmitterContextContext } from '@/context/event-emitter' +import { fetchWorkflowDraft } from '@/service/workflow' +import { useToastContext } from '@/app/components/base/toast' +import { useWorkflowStore } from '@/app/components/workflow/store' +import { useExportPipelineDSL } from '@/service/use-pipeline' + +export const useDSL = () => { + const { t } = useTranslation() + const { notify } = useToastContext() + const { eventEmitter } = useEventEmitterContextContext() + const [exporting, setExporting] = useState(false) + const { doSyncWorkflowDraft } = useNodesSyncDraft() + const workflowStore = useWorkflowStore() + const { mutateAsync: exportPipelineConfig } = useExportPipelineDSL() + + const handleExportDSL = useCallback(async (include = false) => { + const { pipelineId, knowledgeName } = workflowStore.getState() + if (!pipelineId) + return + + if (exporting) + return + + try { + setExporting(true) + await doSyncWorkflowDraft() + const { data } = await exportPipelineConfig({ + pipelineId, + include, + }) + const a = document.createElement('a') + const file = new Blob([data], { type: 'application/yaml' }) + a.href = URL.createObjectURL(file) + a.download = `${knowledgeName}.yml` + a.click() + } + catch { + notify({ type: 'error', message: t('app.exportFailed') }) + } + finally { + setExporting(false) + } + }, [notify, t, doSyncWorkflowDraft, exporting, exportPipelineConfig, workflowStore]) + + const exportCheck = useCallback(async () => { + const { pipelineId } = workflowStore.getState() + if (!pipelineId) + return + try { + const workflowDraft = await fetchWorkflowDraft(`/rag/pipelines/${pipelineId}/workflows/draft`) + const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret') + if (list.length === 0) { + handleExportDSL() + return + } + eventEmitter?.emit({ + type: DSL_EXPORT_CHECK, + payload: { + data: list, + }, + } as any) + } + catch { + notify({ type: 'error', message: t('app.exportFailed') }) + } + }, [eventEmitter, handleExportDSL, notify, t, workflowStore]) + + return { + exportCheck, + handleExportDSL, + } +} diff --git a/web/app/components/rag-pipeline/hooks/use-get-run-and-trace-url.tsx b/web/app/components/rag-pipeline/hooks/use-get-run-and-trace-url.ts similarity index 100% rename from web/app/components/rag-pipeline/hooks/use-get-run-and-trace-url.tsx rename to web/app/components/rag-pipeline/hooks/use-get-run-and-trace-url.ts diff --git a/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts b/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts index 0c8f1d51c9..d3e65c8ca6 100644 --- a/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts +++ b/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts @@ -24,10 +24,11 @@ export const usePipelineInit = () => { const [data, setData] = useState() const [isLoading, setIsLoading] = useState(true) const datasetId = useDatasetDetailContextWithSelector(s => s.dataset)?.pipeline_id + const knowledgeName = useDatasetDetailContextWithSelector(s => s.dataset)?.name useEffect(() => { - workflowStore.setState({ pipelineId: datasetId }) - }, [datasetId, workflowStore]) + workflowStore.setState({ pipelineId: datasetId, knowledgeName }) + }, [datasetId, workflowStore, knowledgeName]) usePipelineConfig() diff --git a/web/app/components/rag-pipeline/store/index.ts b/web/app/components/rag-pipeline/store/index.ts index cdda5b52f9..7799c29daa 100644 --- a/web/app/components/rag-pipeline/store/index.ts +++ b/web/app/components/rag-pipeline/store/index.ts @@ -8,6 +8,7 @@ import { transformDataSourceToTool } from '@/app/components/workflow/block-selec export type RagPipelineSliceShape = { pipelineId: string + knowledgeName: string showInputFieldDialog: boolean setShowInputFieldDialog: (showInputFieldPanel: boolean) => void nodesDefaultConfigs: Record @@ -21,6 +22,7 @@ export type RagPipelineSliceShape = { export type CreateRagPipelineSliceSlice = StateCreator export const createRagPipelineSliceSlice: StateCreator = set => ({ pipelineId: '', + knowledgeName: '', showInputFieldDialog: false, setShowInputFieldDialog: showInputFieldDialog => set(() => ({ showInputFieldDialog })), nodesDefaultConfigs: {}, diff --git a/web/app/components/workflow-app/components/workflow-children.tsx b/web/app/components/workflow-app/components/workflow-children.tsx index 6a6bbcd61a..baccb0b60a 100644 --- a/web/app/components/workflow-app/components/workflow-children.tsx +++ b/web/app/components/workflow-app/components/workflow-children.tsx @@ -46,7 +46,7 @@ const WorkflowChildren = () => { showImportDSLModal && ( setShowImportDSLModal(false)} - onBackup={exportCheck} + onBackup={exportCheck!} onImport={handlePaneContextmenuCancel} /> ) @@ -55,7 +55,7 @@ const WorkflowChildren = () => { secretEnvList.length > 0 && ( setSecretEnvList([])} /> ) diff --git a/web/app/components/workflow-app/components/workflow-main.tsx b/web/app/components/workflow-app/components/workflow-main.tsx index 50ae6ed35c..ead930ba5e 100644 --- a/web/app/components/workflow-app/components/workflow-main.tsx +++ b/web/app/components/workflow-app/components/workflow-main.tsx @@ -8,6 +8,7 @@ import type { WorkflowProps } from '@/app/components/workflow' import WorkflowChildren from './workflow-children' import { useAvailableNodesMetaData, + useDSL, useGetRunAndTraceUrl, useNodesSyncDraft, useWorkflowRefreshDraft, @@ -50,6 +51,10 @@ const WorkflowMain = ({ } = useWorkflowStartRun() const availableNodesMetaData = useAvailableNodesMetaData() const { getWorkflowRunAndTraceUrl } = useGetRunAndTraceUrl() + const { + exportCheck, + handleExportDSL, + } = useDSL() const hooksStore = useMemo(() => { return { @@ -66,6 +71,8 @@ const WorkflowMain = ({ handleWorkflowStartRunInWorkflow, availableNodesMetaData, getWorkflowRunAndTraceUrl, + exportCheck, + handleExportDSL, } }, [ syncWorkflowDraftWhenPageClose, @@ -81,6 +88,8 @@ const WorkflowMain = ({ handleWorkflowStartRunInWorkflow, availableNodesMetaData, getWorkflowRunAndTraceUrl, + exportCheck, + handleExportDSL, ]) return ( diff --git a/web/app/components/workflow-app/hooks/index.ts b/web/app/components/workflow-app/hooks/index.ts index 3b4a587d8f..b17943afc5 100644 --- a/web/app/components/workflow-app/hooks/index.ts +++ b/web/app/components/workflow-app/hooks/index.ts @@ -7,3 +7,4 @@ export * from './use-is-chat-mode' export * from './use-available-nodes-meta-data' export * from './use-workflow-refresh-draft' export * from './use-get-run-and-trace-url' +export * from './use-DSL' diff --git a/web/app/components/workflow-app/hooks/use-DSL.ts b/web/app/components/workflow-app/hooks/use-DSL.ts new file mode 100644 index 0000000000..372eb2c850 --- /dev/null +++ b/web/app/components/workflow-app/hooks/use-DSL.ts @@ -0,0 +1,79 @@ +import { + useCallback, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import { + DSL_EXPORT_CHECK, +} from '@/app/components/workflow/constants' +import { useNodesSyncDraft } from './use-nodes-sync-draft' +import { useEventEmitterContextContext } from '@/context/event-emitter' +import { fetchWorkflowDraft } from '@/service/workflow' +import { exportAppConfig } from '@/service/apps' +import { useToastContext } from '@/app/components/base/toast' +import { useStore as useAppStore } from '@/app/components/app/store' + +export const useDSL = () => { + const { t } = useTranslation() + const { notify } = useToastContext() + const { eventEmitter } = useEventEmitterContextContext() + const [exporting, setExporting] = useState(false) + const { doSyncWorkflowDraft } = useNodesSyncDraft() + + const appDetail = useAppStore(s => s.appDetail) + + const handleExportDSL = useCallback(async (include = false) => { + if (!appDetail) + return + + if (exporting) + return + + try { + setExporting(true) + await doSyncWorkflowDraft() + const { data } = await exportAppConfig({ + appID: appDetail.id, + include, + }) + const a = document.createElement('a') + const file = new Blob([data], { type: 'application/yaml' }) + a.href = URL.createObjectURL(file) + a.download = `${appDetail.name}.yml` + a.click() + } + catch { + notify({ type: 'error', message: t('app.exportFailed') }) + } + finally { + setExporting(false) + } + }, [appDetail, notify, t, doSyncWorkflowDraft, exporting]) + + const exportCheck = useCallback(async () => { + if (!appDetail) + return + try { + const workflowDraft = await fetchWorkflowDraft(`/apps/${appDetail?.id}/workflows/draft`) + const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret') + if (list.length === 0) { + handleExportDSL() + return + } + eventEmitter?.emit({ + type: DSL_EXPORT_CHECK, + payload: { + data: list, + }, + } as any) + } + catch { + notify({ type: 'error', message: t('app.exportFailed') }) + } + }, [appDetail, eventEmitter, handleExportDSL, notify, t]) + + return { + exportCheck, + handleExportDSL, + } +} diff --git a/web/app/components/workflow-app/hooks/use-get-run-and-trace-url.tsx b/web/app/components/workflow-app/hooks/use-get-run-and-trace-url.ts similarity index 100% rename from web/app/components/workflow-app/hooks/use-get-run-and-trace-url.tsx rename to web/app/components/workflow-app/hooks/use-get-run-and-trace-url.ts diff --git a/web/app/components/workflow/block-selector/utils.ts b/web/app/components/workflow/block-selector/utils.ts index b1ad7b1256..0a4bed25ff 100644 --- a/web/app/components/workflow/block-selector/utils.ts +++ b/web/app/components/workflow/block-selector/utils.ts @@ -13,6 +13,7 @@ export const transformDataSourceToTool = (dataSourceItem: DataSourceItem) => { type: dataSourceItem.declaration.provider_type, team_credentials: {}, allow_delete: true, + is_team_authorization: dataSourceItem.is_authorized, is_authorized: dataSourceItem.is_authorized, labels: dataSourceItem.declaration.identity.tags || [], plugin_id: dataSourceItem.plugin_id, diff --git a/web/app/components/workflow/hooks-store/store.ts b/web/app/components/workflow/hooks-store/store.ts index cea8f3a248..967682c7dd 100644 --- a/web/app/components/workflow/hooks-store/store.ts +++ b/web/app/components/workflow/hooks-store/store.ts @@ -37,6 +37,8 @@ export type CommonHooksFnMap = { handleWorkflowStartRunInChatflow: () => void availableNodesMetaData?: AvailableNodesMetaData getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string; traceUrl: string } + exportCheck?: () => Promise + handleExportDSL?: (include?: boolean) => Promise } export type Shape = { @@ -62,6 +64,8 @@ export const createHooksStore = ({ runUrl: '', traceUrl: '', }), + exportCheck = async () => noop(), + handleExportDSL = async () => noop(), }: Partial) => { return createStore(set => ({ refreshAll: props => set(state => ({ ...state, ...props })), @@ -78,6 +82,8 @@ export const createHooksStore = ({ handleWorkflowStartRunInChatflow, availableNodesMetaData, getWorkflowRunAndTraceUrl, + exportCheck, + handleExportDSL, })) } diff --git a/web/app/components/workflow/hooks/index.ts b/web/app/components/workflow/hooks/index.ts index 317ca23d4a..8a33643d69 100644 --- a/web/app/components/workflow/hooks/index.ts +++ b/web/app/components/workflow/hooks/index.ts @@ -19,3 +19,4 @@ export * from './use-nodes-meta-data' export * from './use-available-blocks' export * from './use-workflow-refresh-draft' export * from './use-tool-icon' +export * from './use-DSL' diff --git a/web/app/components/workflow/hooks/use-DSL.ts b/web/app/components/workflow/hooks/use-DSL.ts new file mode 100644 index 0000000000..2470ca7b46 --- /dev/null +++ b/web/app/components/workflow/hooks/use-DSL.ts @@ -0,0 +1,11 @@ +import { useHooksStore } from '@/app/components/workflow/hooks-store' + +export const useDSL = () => { + const exportCheck = useHooksStore(s => s.exportCheck) + const handleExportDSL = useHooksStore(s => s.handleExportDSL) + + return { + exportCheck, + handleExportDSL, + } +} diff --git a/web/app/components/workflow/hooks/use-workflow-interactions.ts b/web/app/components/workflow/hooks/use-workflow-interactions.ts index 636d3b94f9..c9e04fe801 100644 --- a/web/app/components/workflow/hooks/use-workflow-interactions.ts +++ b/web/app/components/workflow/hooks/use-workflow-interactions.ts @@ -1,13 +1,11 @@ import { useCallback, - useState, } from 'react' -import { useTranslation } from 'react-i18next' import { useReactFlow, useStoreApi } from 'reactflow' import produce from 'immer' import { useStore, useWorkflowStore } from '../store' import { - CUSTOM_NODE, DSL_EXPORT_CHECK, + CUSTOM_NODE, NODE_LAYOUT_HORIZONTAL_PADDING, NODE_LAYOUT_VERTICAL_PADDING, WORKFLOW_DATA_UPDATE, @@ -30,10 +28,6 @@ import { useNodesInteractionsWithoutSync } from './use-nodes-interactions-withou import { useNodesSyncDraft } from './use-nodes-sync-draft' import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history' import { useEventEmitterContextContext } from '@/context/event-emitter' -import { fetchWorkflowDraft } from '@/service/workflow' -import { exportAppConfig } from '@/service/apps' -import { useToastContext } from '@/app/components/base/toast' -import { useStore as useAppStore } from '@/app/components/app/store' export const useWorkflowInteractions = () => { const workflowStore = useWorkflowStore() @@ -336,68 +330,3 @@ export const useWorkflowUpdate = () => { handleUpdateWorkflowCanvas, } } - -export const useDSL = () => { - const { t } = useTranslation() - const { notify } = useToastContext() - const { eventEmitter } = useEventEmitterContextContext() - const [exporting, setExporting] = useState(false) - const { doSyncWorkflowDraft } = useNodesSyncDraft() - - const appDetail = useAppStore(s => s.appDetail) - - const handleExportDSL = useCallback(async (include = false) => { - if (!appDetail) - return - - if (exporting) - return - - try { - setExporting(true) - await doSyncWorkflowDraft() - const { data } = await exportAppConfig({ - appID: appDetail.id, - include, - }) - const a = document.createElement('a') - const file = new Blob([data], { type: 'application/yaml' }) - a.href = URL.createObjectURL(file) - a.download = `${appDetail.name}.yml` - a.click() - } - catch { - notify({ type: 'error', message: t('app.exportFailed') }) - } - finally { - setExporting(false) - } - }, [appDetail, notify, t, doSyncWorkflowDraft, exporting]) - - const exportCheck = useCallback(async () => { - if (!appDetail) - return - try { - const workflowDraft = await fetchWorkflowDraft(`/apps/${appDetail?.id}/workflows/draft`) - const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret') - if (list.length === 0) { - handleExportDSL() - return - } - eventEmitter?.emit({ - type: DSL_EXPORT_CHECK, - payload: { - data: list, - }, - } as any) - } - catch { - notify({ type: 'error', message: t('app.exportFailed') }) - } - }, [appDetail, eventEmitter, handleExportDSL, notify, t]) - - return { - exportCheck, - handleExportDSL, - } -} diff --git a/web/app/components/workflow/panel-contextmenu.tsx b/web/app/components/workflow/panel-contextmenu.tsx index 0a09452c67..2ee69df671 100644 --- a/web/app/components/workflow/panel-contextmenu.tsx +++ b/web/app/components/workflow/panel-contextmenu.tsx @@ -112,7 +112,7 @@ const PanelContextmenu = () => {
exportCheck()} + onClick={() => exportCheck?.()} > {t('app.export')}
diff --git a/web/service/use-pipeline.ts b/web/service/use-pipeline.ts index 9b03f9ea33..b338615ef6 100644 --- a/web/service/use-pipeline.ts +++ b/web/service/use-pipeline.ts @@ -298,3 +298,15 @@ export const usePublishedPipelinePreProcessingParams = (params: PipelinePreProce enabled, }) } + +export const useExportPipelineDSL = () => { + return useMutation({ + mutationKey: [NAME_SPACE, 'export-pipeline-dsl'], + mutationFn: ({ + pipelineId, + include = false, + }: { pipelineId: string; include?: boolean }) => { + return get(`/rag/pipelines/${pipelineId}/export?include_secret=${include}`) + }, + }) +}