diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/index.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/index.tsx
index 214836b39f..d6203195b3 100644
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/index.tsx
+++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/index.tsx
@@ -83,6 +83,7 @@ const CreateFromDSLModal = ({
const isCreatingRef = useRef(false)
+ // todo: replace with pipeline import DSL and check plugin dependencies
const onCreate = async () => {
if (currentTab === CreateFromDSLModalTab.FROM_FILE && !currentFile)
return
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/details.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/details.tsx
new file mode 100644
index 0000000000..2cf6c5cfce
--- /dev/null
+++ b/web/app/components/datasets/create-from-pipeline/list/template-card/details.tsx
@@ -0,0 +1,99 @@
+import React from 'react'
+import AppIcon from '@/app/components/base/app-icon'
+import { usePipelineTemplateById } from '@/service/use-pipeline'
+import type { AppIconType } from '@/types/app'
+import { RiAddLine, RiCloseLine } from '@remixicon/react'
+import Button from '@/app/components/base/button'
+import { useTranslation } from 'react-i18next'
+import Tooltip from '@/app/components/base/tooltip'
+
+type DetailsProps = {
+ id: string
+ handleUseTemplate: () => void
+ onClose: () => void
+}
+
+const Details = ({
+ id,
+ handleUseTemplate,
+ onClose,
+}: DetailsProps) => {
+ const { t } = useTranslation()
+ const { data: pipelineTemplateInfo } = usePipelineTemplateById(id, true)
+ const appIcon = React.useMemo(() => {
+ if (!pipelineTemplateInfo)
+ return { type: 'emoji', icon: '📙', background: '#FFF4ED' }
+ const iconInfo = pipelineTemplateInfo.icon_info
+ return iconInfo.icon_type === 'image'
+ ? { type: 'image', url: iconInfo.icon_url || '', fileId: iconInfo.icon || '' }
+ : { type: 'icon', icon: iconInfo.icon || '', background: iconInfo.icon_background || '' }
+ }, [pipelineTemplateInfo])
+
+ if (!pipelineTemplateInfo) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+ Pipeline Preview
+
+
+
+
+
+
+
+ {pipelineTemplateInfo.name}
+
+
+ {`By ${pipelineTemplateInfo.author}`}
+
+
+
+
+ {pipelineTemplateInfo.description}
+
+
+
+
+
+
+
+ {t('datasetPipeline.details.structure')}
+
+
+
+
+
+
+ )
+}
+
+export default React.memo(Details)
diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx
index 0152bb49c0..200143f703 100644
--- a/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx
+++ b/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx
@@ -11,9 +11,13 @@ import EditPipelineInfo from './edit-pipeline-info'
import type { PipelineTemple } from '@/models/pipeline'
import { DOC_FORM_ICON, DOC_FORM_TEXT } from '@/models/datasets'
import Confirm from '@/app/components/base/confirm'
-import { useDeletePipeline, useExportPipelineDSL } from '@/service/use-pipeline'
+import { useDeletePipeline, useExportPipelineDSL, useImportPipelineDSL, usePipelineTemplateById } from '@/service/use-pipeline'
import { downloadFile } from '@/utils/format'
import Toast from '@/app/components/base/toast'
+import { DSLImportMode } from '@/models/app'
+import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
+import { useRouter } from 'next/navigation'
+import Details from './details'
type TemplateCardProps = {
pipeline: PipelineTemple
@@ -25,8 +29,52 @@ const TemplateCard = ({
showMoreOperations = true,
}: TemplateCardProps) => {
const { t } = useTranslation()
+ const { push } = useRouter()
const [showEditModal, setShowEditModal] = useState(false)
const [showDeleteConfirm, setShowConfirmDelete] = useState(false)
+ const [showDetailModal, setShowDetailModal] = useState(false)
+
+ const { refetch: getPipelineTemplateInfo } = usePipelineTemplateById(pipeline.id, false)
+ const { mutateAsync: importPipelineDSL } = useImportPipelineDSL()
+ const { handleCheckPluginDependencies } = usePluginDependencies()
+
+ const handleUseTemplate = useCallback(async () => {
+ try {
+ const { data: pipelineTemplateInfo } = await getPipelineTemplateInfo()
+ if (!pipelineTemplateInfo) {
+ Toast.notify({
+ type: 'error',
+ message: t('datasetPipeline.creation.errorTip'),
+ })
+ return
+ }
+ const request = {
+ mode: DSLImportMode.YAML_CONTENT,
+ name: pipeline.name,
+ yaml_content: pipelineTemplateInfo.export_data,
+ icon_info: pipeline.icon_info,
+ description: pipeline.description,
+ }
+ const newPipeline = await importPipelineDSL(request)
+ Toast.notify({
+ type: 'success',
+ message: t('app.newApp.appCreated'),
+ })
+ if (newPipeline.dataset_id)
+ await handleCheckPluginDependencies(newPipeline.dataset_id) // todo: replace with pipeline dependency check
+ push(`dataset/${newPipeline.dataset_id}/pipeline`)
+ }
+ catch {
+ Toast.notify({
+ type: 'error',
+ message: t('datasetPipeline.creation.errorTip'),
+ })
+ }
+ }, [getPipelineTemplateInfo, importPipelineDSL, pipeline, t, push, handleCheckPluginDependencies])
+
+ const handleShowTemplateDetails = useCallback(() => {
+ setShowDetailModal(true)
+ }, [])
const openEditModal = useCallback(() => {
setShowEditModal(true)
@@ -36,10 +84,15 @@ const TemplateCard = ({
setShowEditModal(false)
}, [])
- const { mutateAsync: getDSLFileContent } = useExportPipelineDSL()
+ const closeDetailsModal = useCallback(() => {
+ setShowDetailModal(false)
+ }, [])
+
+ const { mutateAsync: exportPipelineDSL, isPending: isExporting } = useExportPipelineDSL()
const handleExportDSL = useCallback(async () => {
- await getDSLFileContent(pipeline.id, {
+ if (isExporting) return
+ await exportPipelineDSL(pipeline.id, {
onSuccess: (res) => {
const blob = new Blob([res.data], { type: 'application/yaml' })
downloadFile({
@@ -58,7 +111,7 @@ const TemplateCard = ({
})
},
})
- }, [t, pipeline.id, pipeline.name, getDSLFileContent])
+ }, [t, isExporting, pipeline.id, pipeline.name, exportPipelineDSL])
const handleDelete = useCallback(() => {
setShowConfirmDelete(true)
@@ -117,9 +170,7 @@ const TemplateCard = ({
)
}
diff --git a/web/app/components/datasets/list/dataset-card/operations.tsx b/web/app/components/datasets/list/dataset-card/operations.tsx
index c4fc78ba8f..3d24ebbac5 100644
--- a/web/app/components/datasets/list/dataset-card/operations.tsx
+++ b/web/app/components/datasets/list/dataset-card/operations.tsx
@@ -1,7 +1,7 @@
import Divider from '@/app/components/base/divider'
import React from 'react'
import { useTranslation } from 'react-i18next'
-import { RiDeleteBinLine, RiEditLine, RiFileCopyLine } from '@remixicon/react'
+import { RiDeleteBinLine, RiEditLine } from '@remixicon/react'
type OperationsProps = {
showDelete: boolean
@@ -40,7 +40,7 @@ const Operations = ({
{t('common.operation.edit')}
- { console.log('duplicate') }}
>
@@ -48,9 +48,9 @@ const Operations = ({
{t('common.operation.duplicate')}
-
+ */}
-
+ {/*
+ */}
{showDelete && (
<>
diff --git a/web/i18n/en-US/dataset-pipeline.ts b/web/i18n/en-US/dataset-pipeline.ts
index 97700875b6..251608c31d 100644
--- a/web/i18n/en-US/dataset-pipeline.ts
+++ b/web/i18n/en-US/dataset-pipeline.ts
@@ -10,6 +10,8 @@ const translation = {
description: 'Import from a DSL file',
},
createKnowledge: 'Create Knowledge',
+ errorTip: 'Failed to create a Knowledge Pipeline',
+ successTip: 'Successfully created a Knowledge Pipeline',
},
tabs: {
builtInPipeline: 'Built-in pipeline',
@@ -20,6 +22,7 @@ const translation = {
details: 'Details',
editInfo: 'Edit info',
exportDSL: 'Export DSL',
+ useTemplate: 'Use this Knowledge Pipeline',
},
knowledgeNameAndIcon: 'Knowledge name & icon',
knowledgeNameAndIconPlaceholder: 'Please enter the name of the Knowledge Base',
@@ -36,6 +39,9 @@ const translation = {
successTip: 'Export pipeline DSL successfully',
errorTip: 'Failed to export pipeline DSL',
},
+ details: {
+ structure: 'Structure',
+ },
}
export default translation
diff --git a/web/i18n/zh-Hans/dataset-pipeline.ts b/web/i18n/zh-Hans/dataset-pipeline.ts
index ede0aeb053..4570db18c1 100644
--- a/web/i18n/zh-Hans/dataset-pipeline.ts
+++ b/web/i18n/zh-Hans/dataset-pipeline.ts
@@ -10,6 +10,8 @@ const translation = {
description: '从 DSL 文件导入',
},
createKnowledge: '创建知识库',
+ errorTip: '创建知识库流水线失败',
+ successTip: '成功创建知识库流水线',
},
tabs: {
builtInPipeline: '内置流水线',
@@ -20,6 +22,7 @@ const translation = {
details: '详情',
editInfo: '编辑信息',
exportDSL: '导出 DSL',
+ useTemplate: '使用此知识库流水线',
},
knowledgeNameAndIcon: '知识库名称和图标',
knowledgeNameAndIconPlaceholder: '请输入知识库名称',
@@ -36,6 +39,9 @@ const translation = {
successTip: '成功导出流水线 DSL',
errorTip: '导出流水线 DSL 失败',
},
+ details: {
+ structure: '文档结构',
+ },
}
export default translation
diff --git a/web/models/app.ts b/web/models/app.ts
index d83045570a..2d2322df6d 100644
--- a/web/models/app.ts
+++ b/web/models/app.ts
@@ -1,5 +1,5 @@
import type { LangFuseConfig, LangSmithConfig, OpikConfig, TracingProvider, WeaveConfig } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type'
-import type { App, AppSSO, AppTemplate, SiteConfig } from '@/types/app'
+import type { App, AppMode, AppSSO, AppTemplate, SiteConfig } from '@/types/app'
import type { Dependency } from '@/app/components/plugins/types'
export enum DSLImportMode {
diff --git a/web/models/pipeline.ts b/web/models/pipeline.ts
index 101a4e4ab8..0128da841e 100644
--- a/web/models/pipeline.ts
+++ b/web/models/pipeline.ts
@@ -1,4 +1,6 @@
+import type { DSLImportMode, DSLImportStatus } from './app'
import type { ChunkingMode, IconInfo } from './datasets'
+import type { Dependency } from '@/app/components/plugins/types'
export type PipelineTemplateListParams = {
type: 'built-in' | 'customized'
@@ -21,6 +23,8 @@ export type PipelineTemplateByIdResponse = {
name: string
icon_info: IconInfo
description: string
+ author: string // todo: TBD
+ structure: string // todo: TBD
export_data: string
}
@@ -46,3 +50,22 @@ export type DeletePipelineResponse = {
export type ExportPipelineDSLResponse = {
data: string
}
+
+export type ImportPipelineDSLRequest = {
+ mode: DSLImportMode
+ name: string
+ yaml_content: string
+ icon_info: IconInfo
+ description: string
+}
+
+export type ImportPipelineDSLResponse = {
+ id: string
+ status: DSLImportStatus
+ app_mode: 'pipeline'
+ dataset_id?: string
+ current_dsl_version?: string
+ imported_dsl_version?: string
+ error: string
+ leaked_dependencies: Dependency[]
+}
diff --git a/web/service/use-pipeline.ts b/web/service/use-pipeline.ts
index 8e6e2981c4..13ada282ad 100644
--- a/web/service/use-pipeline.ts
+++ b/web/service/use-pipeline.ts
@@ -1,9 +1,11 @@
import type { MutationOptions } from '@tanstack/react-query'
import { useMutation, useQuery } from '@tanstack/react-query'
-import { del, get, patch } from './base'
+import { del, get, patch, post } from './base'
import type {
DeletePipelineResponse,
ExportPipelineDSLResponse,
+ ImportPipelineDSLRequest,
+ ImportPipelineDSLResponse,
PipelineTemplateByIdResponse,
PipelineTemplateListParams,
PipelineTemplateListResponse,
@@ -22,12 +24,13 @@ export const usePipelineTemplateList = (params: PipelineTemplateListParams) => {
})
}
-export const usePipelineTemplateById = (templateId: string) => {
+export const usePipelineTemplateById = (templateId: string, enabled: boolean) => {
return useQuery({
queryKey: [NAME_SPACE, 'template', templateId],
queryFn: () => {
return get(`/rag/pipeline/template/${templateId}`)
},
+ enabled,
})
}
@@ -69,3 +72,16 @@ export const useExportPipelineDSL = (
...mutationOptions,
})
}
+
+// TODO: replace with real API
+export const useImportPipelineDSL = (
+ mutationOptions: MutationOptions = {},
+) => {
+ return useMutation({
+ mutationKey: [NAME_SPACE, 'template', 'import'],
+ mutationFn: (request: ImportPipelineDSLRequest) => {
+ return post('/rag/pipeline/import', { body: request })
+ },
+ ...mutationOptions,
+ })
+}