| @@ -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<EnvironmentVariable[]>([]) | |||
| 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 && ( | |||
| <UpdateDSLModal | |||
| onCancel={() => setShowImportDSLModal(false)} | |||
| onBackup={exportCheck!} | |||
| onImport={handlePaneContextmenuCancel} | |||
| /> | |||
| ) | |||
| } | |||
| { | |||
| secretEnvList.length > 0 && ( | |||
| <DSLExportConfirmModal | |||
| envList={secretEnvList} | |||
| onConfirm={handleExportDSL!} | |||
| onClose={() => setSecretEnvList([])} | |||
| /> | |||
| ) | |||
| } | |||
| <RagPipelineHeader /> | |||
| <RagPipelinePanel /> | |||
| { | |||
| @@ -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 ( | |||
| @@ -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' | |||
| @@ -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, | |||
| } | |||
| } | |||
| @@ -24,10 +24,11 @@ export const usePipelineInit = () => { | |||
| const [data, setData] = useState<FetchWorkflowDraftResponse>() | |||
| 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() | |||
| @@ -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<string, any> | |||
| @@ -21,6 +22,7 @@ export type RagPipelineSliceShape = { | |||
| export type CreateRagPipelineSliceSlice = StateCreator<RagPipelineSliceShape> | |||
| export const createRagPipelineSliceSlice: StateCreator<RagPipelineSliceShape> = set => ({ | |||
| pipelineId: '', | |||
| knowledgeName: '', | |||
| showInputFieldDialog: false, | |||
| setShowInputFieldDialog: showInputFieldDialog => set(() => ({ showInputFieldDialog })), | |||
| nodesDefaultConfigs: {}, | |||
| @@ -46,7 +46,7 @@ const WorkflowChildren = () => { | |||
| showImportDSLModal && ( | |||
| <UpdateDSLModal | |||
| onCancel={() => setShowImportDSLModal(false)} | |||
| onBackup={exportCheck} | |||
| onBackup={exportCheck!} | |||
| onImport={handlePaneContextmenuCancel} | |||
| /> | |||
| ) | |||
| @@ -55,7 +55,7 @@ const WorkflowChildren = () => { | |||
| secretEnvList.length > 0 && ( | |||
| <DSLExportConfirmModal | |||
| envList={secretEnvList} | |||
| onConfirm={handleExportDSL} | |||
| onConfirm={handleExportDSL!} | |||
| onClose={() => setSecretEnvList([])} | |||
| /> | |||
| ) | |||
| @@ -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 ( | |||
| @@ -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' | |||
| @@ -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, | |||
| } | |||
| } | |||
| @@ -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, | |||
| @@ -37,6 +37,8 @@ export type CommonHooksFnMap = { | |||
| handleWorkflowStartRunInChatflow: () => void | |||
| availableNodesMetaData?: AvailableNodesMetaData | |||
| getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string; traceUrl: string } | |||
| exportCheck?: () => Promise<void> | |||
| handleExportDSL?: (include?: boolean) => Promise<void> | |||
| } | |||
| export type Shape = { | |||
| @@ -62,6 +64,8 @@ export const createHooksStore = ({ | |||
| runUrl: '', | |||
| traceUrl: '', | |||
| }), | |||
| exportCheck = async () => noop(), | |||
| handleExportDSL = async () => noop(), | |||
| }: Partial<Shape>) => { | |||
| return createStore<Shape>(set => ({ | |||
| refreshAll: props => set(state => ({ ...state, ...props })), | |||
| @@ -78,6 +82,8 @@ export const createHooksStore = ({ | |||
| handleWorkflowStartRunInChatflow, | |||
| availableNodesMetaData, | |||
| getWorkflowRunAndTraceUrl, | |||
| exportCheck, | |||
| handleExportDSL, | |||
| })) | |||
| } | |||
| @@ -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' | |||
| @@ -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, | |||
| } | |||
| } | |||
| @@ -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, | |||
| } | |||
| } | |||
| @@ -112,7 +112,7 @@ const PanelContextmenu = () => { | |||
| <div className='p-1'> | |||
| <div | |||
| className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' | |||
| onClick={() => exportCheck()} | |||
| onClick={() => exportCheck?.()} | |||
| > | |||
| {t('app.export')} | |||
| </div> | |||
| @@ -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<ExportTemplateDSLResponse>(`/rag/pipelines/${pipelineId}/export?include_secret=${include}`) | |||
| }, | |||
| }) | |||
| } | |||