refactor: refactor pipeline-related components and services to use template terminology

This commit is contained in:
twwu 2025-05-16 15:14:50 +08:00
parent 019ef74bf2
commit 35a7add4e9
19 changed files with 483 additions and 580 deletions

View File

@ -167,10 +167,6 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
<div className={cn(s.app, 'relative flex', 'overflow-hidden')}>
{appDetail && (
<AppSideBar
title={appDetail.name}
icon={appDetail.icon}
icon_background={appDetail.icon_background as string}
desc={appDetail.mode}
navigation={navigation}
/>
)}

View File

@ -173,11 +173,6 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
}}>
{!hideSideBar && (
<AppSideBar
title={datasetRes?.name || '--'}
icon={datasetRes?.icon_info?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
icon_background={datasetRes?.icon_info?.icon_background || '#F5F5F5'}
desc={datasetRes?.description || '--'}
isExternal={datasetRes?.provider === 'external'}
navigation={navigation}
extraInfo={
!isCurrentWorkspaceDatasetOperator

View File

@ -28,9 +28,10 @@ const DatasetInfo: FC<Props> = ({
icon_background: '#FFF4ED',
icon_url: '',
}
const isExternal = dataset.provider === 'external'
const isExternalProvider = dataset.provider === 'external'
const { formatIndexingTechniqueAndMethod } = useKnowledge()
const Icon = isExternal ? DOC_FORM_ICON_WITH_BG.external : DOC_FORM_ICON_WITH_BG[dataset.doc_form]
const chunkingModeIcon = dataset.doc_form ? DOC_FORM_ICON_WITH_BG[dataset.doc_form] : React.Fragment
const Icon = isExternalProvider ? DOC_FORM_ICON_WITH_BG.external : chunkingModeIcon
return (
<div className={cn('relative flex flex-col', expand ? '' : 'p-1')}>
@ -46,32 +47,34 @@ const DatasetInfo: FC<Props> = ({
background={iconInfo.icon_background}
imageUrl={iconInfo.icon_url}
/>
<div className='absolute -bottom-1 -right-1 z-10'>
<Icon className='size-4' />
</div>
</div>
<>
<div className='flex flex-col gap-y-1'>
<div
className='system-md-semibold truncate text-text-secondary'
title={dataset.name}
>
{dataset.name}
</div>
<div className='system-2xs-medium-uppercase text-text-tertiary'>
{isExternal && t('dataset.externalTag')}
{!isExternal && (
<div className='flex items-center gap-x-1'>
<Badge>{t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`)}</Badge>
<Badge>{formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}</Badge>
</div>
)}
</div>
{(dataset.doc_form || isExternalProvider) && (
<div className='absolute -bottom-1 -right-1 z-10'>
<Icon className='size-4' />
</div>
<p className='system-xs-regular line-clamp-3 text-text-tertiary first-letter:capitalize'>
{dataset.description}
</p>
</>
)}
</div>
<>
<div className='flex flex-col gap-y-1'>
<div
className='system-md-semibold truncate text-text-secondary'
title={dataset.name}
>
{dataset.name}
</div>
<div className='system-2xs-medium-uppercase text-text-tertiary'>
{isExternalProvider && t('dataset.externalTag')}
{!isExternalProvider && dataset.doc_form && dataset.indexing_technique && (
<div className='flex items-center gap-x-1'>
<Badge>{t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`)}</Badge>
<Badge>{formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}</Badge>
</div>
)}
</div>
</div>
<p className='system-xs-regular line-clamp-3 text-text-tertiary first-letter:capitalize'>
{dataset.description}
</p>
</>
</div>
</>
)}

View File

@ -3,7 +3,6 @@ import { useShallow } from 'zustand/react/shallow'
import { RiLayoutLeft2Line, RiLayoutRight2Line } from '@remixicon/react'
import NavLink from './navLink'
import type { NavIcon } from './navLink'
import AppBasic from './basic'
import AppInfo from './app-info'
import DatasetInfo from './dataset-info'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
@ -12,11 +11,6 @@ import cn from '@/utils/classnames'
export type IAppDetailNavProps = {
iconType?: 'app' | 'dataset' | 'notion'
title: string
desc: string
isExternal?: boolean
icon: string
icon_background: string
navigation: Array<{
name: string
href: string
@ -27,11 +21,6 @@ export type IAppDetailNavProps = {
}
const AppDetailNav = ({
title,
desc,
isExternal,
icon,
icon_background,
navigation,
extraInfo,
iconType = 'app',
@ -71,23 +60,12 @@ const AppDetailNav = ({
{iconType === 'app' && (
<AppInfo expand={expand} />
)}
{iconType === 'dataset' && (
{iconType !== 'app' && (
<DatasetInfo
expand={expand}
extraInfo={extraInfo && extraInfo(appSidebarExpand)}
/>
)}
{!['app', 'dataset'].includes(iconType) && (
<AppBasic
mode={appSidebarExpand}
iconType={iconType}
icon={icon}
icon_background={icon_background}
name={title}
type={desc}
isExternal={isExternal}
/>
)}
</div>
<div className='px-4'>
<div className={cn('mx-auto mt-1 h-[1px] bg-divider-subtle', !expand && 'w-6')} />

View File

@ -7,6 +7,7 @@ import type { Member } from '@/models/common'
import CreateForm from '../create-form'
import type { CreateFormData } from '@/models/pipeline'
import Modal from '@/app/components/base/modal'
import { useRouter } from 'next/navigation'
type CreateFromScratchModalProps = {
show: boolean
@ -17,6 +18,7 @@ const CreateFromScratchModal = ({
show,
onClose,
}: CreateFromScratchModalProps) => {
const { push } = useRouter()
const [memberList, setMemberList] = useState<Member[]>([])
const { data: members } = useMembers()
@ -52,11 +54,15 @@ const CreateFromScratchModal = ({
request.partial_member_list = selectedMemberList
}
await createEmptyDataset(request, {
onSettled: () => {
onSettled: (data) => {
if (data) {
const { id } = data
push(`/datasets/${id}/pipeline`)
}
onClose?.()
},
})
}, [createEmptyDataset, memberList, onClose])
}, [createEmptyDataset, memberList, onClose, push])
return (
<Modal

View File

@ -1,57 +1,9 @@
import { usePipelineTemplateList } from '@/service/use-pipeline'
import TemplateCard from './template-card'
import { ChunkingMode } from '@/models/datasets'
import type { PipelineTemplate } from '@/models/pipeline'
const BuiltInPipelineList = () => {
// TODO: remove mock data
const mockData: PipelineTemplate[] = [{
id: '1',
name: 'Pipeline 1',
description: 'This is a description of Pipeline 1. When use the general chunking mode, the chunks retrieved and recalled are the same. When use the general chunking mode, the chunks retrieved and recalled are the same.',
icon_info: {
icon: '🤖',
icon_background: '#F0FDF9',
icon_type: 'emoji',
},
doc_form: ChunkingMode.text,
position: 0,
}, {
id: '2',
name: 'Pipeline 2',
description: 'This is a description of Pipeline 2. When use the general chunking mode, the chunks retrieved and recalled are the same.',
icon_info: {
icon: '🏖️',
icon_background: '#FFF4ED',
icon_type: 'emoji',
},
doc_form: ChunkingMode.parentChild,
position: 1,
}, {
id: '3',
name: 'Pipeline 3',
description: 'This is a description of Pipeline 3',
icon_info: {
icon: '🚀',
icon_background: '#FEFBE8',
icon_type: 'emoji',
},
doc_form: ChunkingMode.qa,
position: 2,
}, {
id: '4',
name: 'Pipeline 4',
description: 'This is a description of Pipeline 4',
icon_info: {
icon: '🍯',
icon_background: '#F5F3FF',
icon_type: 'emoji',
},
doc_form: ChunkingMode.graph,
position: 3,
}]
const { data: pipelineList, isLoading } = usePipelineTemplateList({ type: 'built-in' })
const list = pipelineList?.pipelines || mockData
const list = pipelineList?.pipelines
if (isLoading || !list)
return null

View File

@ -1,57 +1,9 @@
import { ChunkingMode } from '@/models/datasets'
import TemplateCard from './template-card'
import { usePipelineTemplateList } from '@/service/use-pipeline'
import type { PipelineTemplate } from '@/models/pipeline'
const CustomizedList = () => {
const mockData: PipelineTemplate[] = [{
id: '1',
name: 'Pipeline 1',
description: 'This is a description of Pipeline 1. When use the general chunking mode, the chunks retrieved and recalled are the same. When use the general chunking mode, the chunks retrieved and recalled are the same.',
icon_info: {
icon: '🤖',
icon_background: '#F0FDF9',
icon_type: 'emoji',
},
doc_form: ChunkingMode.text,
position: 0,
}, {
id: '2',
name: 'Pipeline 2',
description: 'This is a description of Pipeline 2. When use the general chunking mode, the chunks retrieved and recalled are the same.',
icon_info: {
icon: '🏖️',
icon_background: '#FFF4ED',
icon_type: 'emoji',
},
doc_form: ChunkingMode.parentChild,
position: 1,
}, {
id: '3',
name: 'Pipeline 3',
description: 'This is a description of Pipeline 3',
icon_info: {
icon: '🚀',
icon_background: '#FEFBE8',
icon_type: 'emoji',
},
doc_form: ChunkingMode.qa,
position: 2,
}, {
id: '4',
name: 'Pipeline 4',
description: 'This is a description of Pipeline 4',
icon_info: {
icon: '🍯',
icon_background: '#F5F3FF',
icon_type: 'emoji',
},
doc_form: ChunkingMode.graph,
position: 3,
}]
const { data: pipelineList, isLoading } = usePipelineTemplateList({ type: 'customized' })
const list = pipelineList?.pipelines || mockData
const list = pipelineList?.pipelines
if (isLoading || !list)
return null

View File

@ -9,7 +9,7 @@ import Button from '@/app/components/base/button'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import type { PipelineTemplate } from '@/models/pipeline'
import { useUpdatePipelineInfo } from '@/service/use-pipeline'
import { useUpdateTemplateInfo } from '@/service/use-pipeline'
type EditPipelineInfoProps = {
onClose: () => void
@ -61,7 +61,7 @@ const EditPipelineInfo = ({
setDescription(value)
}, [])
const { mutateAsync: updatePipeline } = useUpdatePipelineInfo()
const { mutateAsync: updatePipeline } = useUpdateTemplateInfo()
const handleSave = useCallback(async () => {
if (!name) {
@ -72,7 +72,7 @@ const EditPipelineInfo = ({
return
}
const request = {
pipeline_id: pipeline.id,
template_id: pipeline.id,
name,
icon_info: {
icon_type: appIcon.type,

View File

@ -5,8 +5,8 @@ import EditPipelineInfo from './edit-pipeline-info'
import type { PipelineTemplate } from '@/models/pipeline'
import Confirm from '@/app/components/base/confirm'
import {
useDeletePipeline,
useExportPipelineDSL,
useDeleteTemplate,
useExportTemplateDSL,
usePipelineTemplateById,
} from '@/service/use-pipeline'
import { downloadFile } from '@/utils/format'
@ -63,8 +63,8 @@ const TemplateCard = ({
type: 'success',
message: t('app.newApp.appCreated'),
})
if (newDataset.pipeline_info?.id)
await handleCheckPluginDependencies(newDataset.pipeline_info.id, true)
if (newDataset.pipeline_id)
await handleCheckPluginDependencies(newDataset.pipeline_id, true)
push(`dataset/${newDataset.id}/pipeline`)
}
catch {
@ -91,14 +91,11 @@ const TemplateCard = ({
setShowDetailModal(false)
}, [])
const { mutateAsync: exportPipelineDSL, isPending: isExporting } = useExportPipelineDSL()
const { mutateAsync: exportPipelineDSL, isPending: isExporting } = useExportTemplateDSL()
const handleExportDSL = useCallback(async (includeSecret = false) => {
const handleExportDSL = useCallback(async () => {
if (isExporting) return
await exportPipelineDSL({
pipeline_id: pipeline.id,
include_secret: includeSecret,
}, {
await exportPipelineDSL(pipeline.id, {
onSuccess: (res) => {
const blob = new Blob([res.data], { type: 'application/yaml' })
downloadFile({
@ -127,7 +124,7 @@ const TemplateCard = ({
setShowConfirmDelete(false)
}, [])
const { mutateAsync: deletePipeline } = useDeletePipeline()
const { mutateAsync: deletePipeline } = useDeleteTemplate()
const onConfirmDelete = useCallback(async () => {
await deletePipeline(pipeline.id, {

View File

@ -1,18 +1,10 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useBoolean, useDebounceFn } from 'ahooks'
import { useBoolean } from 'ahooks'
import { ArrowDownIcon } from '@heroicons/react/24/outline'
import { pick, uniq } from 'lodash-es'
import {
RiArchive2Line,
RiDeleteBinLine,
RiEditLine,
RiEqualizer2Line,
RiLoopLeftLine,
RiMoreFill,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
import { RiEditLine } from '@remixicon/react'
import { useRouter } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import dayjs from 'dayjs'
@ -23,359 +15,25 @@ import s from './style.module.css'
import RenameModal from './rename-modal'
import BatchAction from './detail/completed/common/batch-action'
import cn from '@/utils/classnames'
import Switch from '@/app/components/base/switch'
import Divider from '@/app/components/base/divider'
import Popover from '@/app/components/base/popover'
import Confirm from '@/app/components/base/confirm'
import Tooltip from '@/app/components/base/tooltip'
import Toast, { ToastContext } from '@/app/components/base/toast'
import type { ColorMap, IndicatorProps } from '@/app/components/header/indicator'
import Indicator from '@/app/components/header/indicator'
import Toast from '@/app/components/base/toast'
import { asyncRunSafe } from '@/utils'
import { formatNumber } from '@/utils/format'
import NotionIcon from '@/app/components/base/notion-icon'
import ProgressBar from '@/app/components/base/progress-bar'
import { ChunkingMode, DataSourceType, DocumentActionType, type DocumentDisplayStatus, type SimpleDocumentDetail } from '@/models/datasets'
import { ChunkingMode, DataSourceType, DocumentActionType, type SimpleDocumentDetail } from '@/models/datasets'
import type { CommonResponse } from '@/models/common'
import useTimestamp from '@/hooks/use-timestamp'
import { useDatasetDetailContextWithSelector as useDatasetDetailContext } from '@/context/dataset-detail'
import type { Props as PaginationProps } from '@/app/components/base/pagination'
import Pagination from '@/app/components/base/pagination'
import Checkbox from '@/app/components/base/checkbox'
import { useDocumentArchive, useDocumentDelete, useDocumentDisable, useDocumentEnable, useDocumentUnArchive, useSyncDocument, useSyncWebsite } from '@/service/knowledge/use-document'
import { useDocumentArchive, useDocumentDelete, useDocumentDisable, useDocumentEnable } from '@/service/knowledge/use-document'
import { extensionToFileType } from '@/app/components/datasets/hit-testing/utils/extension-to-file-type'
import useBatchEditDocumentMetadata from '../metadata/hooks/use-batch-edit-document-metadata'
import EditMetadataBatchModal from '@/app/components/datasets/metadata/edit-metadata-batch/modal'
import { noop } from 'lodash-es'
export const useIndexStatus = () => {
const { t } = useTranslation()
return {
queuing: { color: 'orange', text: t('datasetDocuments.list.status.queuing') }, // waiting
indexing: { color: 'blue', text: t('datasetDocuments.list.status.indexing') }, // indexing splitting parsing cleaning
paused: { color: 'orange', text: t('datasetDocuments.list.status.paused') }, // paused
error: { color: 'red', text: t('datasetDocuments.list.status.error') }, // error
available: { color: 'green', text: t('datasetDocuments.list.status.available') }, // completedarchived = falseenabled = true
enabled: { color: 'green', text: t('datasetDocuments.list.status.enabled') }, // completedarchived = falseenabled = true
disabled: { color: 'gray', text: t('datasetDocuments.list.status.disabled') }, // completedarchived = falseenabled = false
archived: { color: 'gray', text: t('datasetDocuments.list.status.archived') }, // completedarchived = true
}
}
const STATUS_TEXT_COLOR_MAP: ColorMap = {
green: 'text-util-colors-green-green-600',
orange: 'text-util-colors-warning-warning-600',
red: 'text-util-colors-red-red-600',
blue: 'text-util-colors-blue-light-blue-light-600',
yellow: 'text-util-colors-warning-warning-600',
gray: 'text-text-tertiary',
}
// status item for list
export const StatusItem: FC<{
status: DocumentDisplayStatus
reverse?: boolean
scene?: 'list' | 'detail'
textCls?: string
errorMessage?: string
detail?: {
enabled: boolean
archived: boolean
id: string
}
datasetId?: string
onUpdate?: (operationName?: string) => void
}> = ({ status, reverse = false, scene = 'list', textCls = '', errorMessage, datasetId = '', detail, onUpdate }) => {
const DOC_INDEX_STATUS_MAP = useIndexStatus()
const localStatus = status.toLowerCase() as keyof typeof DOC_INDEX_STATUS_MAP
const { enabled = false, archived = false, id = '' } = detail || {}
const { notify } = useContext(ToastContext)
const { t } = useTranslation()
const { mutateAsync: enableDocument } = useDocumentEnable()
const { mutateAsync: disableDocument } = useDocumentDisable()
const { mutateAsync: deleteDocument } = useDocumentDelete()
const onOperate = async (operationName: OperationName) => {
let opApi = deleteDocument
switch (operationName) {
case 'enable':
opApi = enableDocument
break
case 'disable':
opApi = disableDocument
break
}
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId, documentId: id }) as Promise<CommonResponse>)
if (!e) {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
onUpdate?.()
// onUpdate?.(operationName)
}
else { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) }
}
const { run: handleSwitch } = useDebounceFn((operationName: OperationName) => {
if (operationName === 'enable' && enabled)
return
if (operationName === 'disable' && !enabled)
return
onOperate(operationName)
}, { wait: 500 })
const embedding = useMemo(() => {
return ['queuing', 'indexing', 'paused'].includes(localStatus)
}, [localStatus])
return <div className={
cn('flex items-center',
reverse ? 'flex-row-reverse' : '',
scene === 'detail' ? s.statusItemDetail : '')
}>
<Indicator color={DOC_INDEX_STATUS_MAP[localStatus]?.color as IndicatorProps['color']} className={reverse ? 'ml-2' : 'mr-2'} />
<span className={cn(`${STATUS_TEXT_COLOR_MAP[DOC_INDEX_STATUS_MAP[localStatus].color as keyof typeof STATUS_TEXT_COLOR_MAP]} text-sm`, textCls)}>
{DOC_INDEX_STATUS_MAP[localStatus]?.text}
</span>
{
errorMessage && (
<Tooltip
popupContent={
<div className='max-w-[260px] break-all'>{errorMessage}</div>
}
triggerClassName='ml-1 w-4 h-4'
/>
)
}
{
scene === 'detail' && (
<div className='ml-1.5 flex items-center justify-between'>
<Tooltip
popupContent={t('datasetDocuments.list.action.enableWarning')}
popupClassName='text-text-secondary system-xs-medium'
needsDelay
disabled={!archived}
>
<Switch
defaultValue={archived ? false : enabled}
onChange={v => !archived && handleSwitch(v ? 'enable' : 'disable')}
disabled={embedding || archived}
size='md'
/>
</Tooltip>
</div>
)
}
</div>
}
type OperationName = 'delete' | 'archive' | 'enable' | 'disable' | 'sync' | 'un_archive'
// operation action for list and detail
export const OperationAction: FC<{
embeddingAvailable: boolean
detail: {
name: string
enabled: boolean
archived: boolean
id: string
data_source_type: string
doc_form: string
}
datasetId: string
onUpdate: (operationName?: string) => void
scene?: 'list' | 'detail'
className?: string
}> = ({ embeddingAvailable, datasetId, detail, onUpdate, scene = 'list', className = '' }) => {
const { id, enabled = false, archived = false, data_source_type } = detail || {}
const [showModal, setShowModal] = useState(false)
const [deleting, setDeleting] = useState(false)
const { notify } = useContext(ToastContext)
const { t } = useTranslation()
const router = useRouter()
const { mutateAsync: archiveDocument } = useDocumentArchive()
const { mutateAsync: unArchiveDocument } = useDocumentUnArchive()
const { mutateAsync: enableDocument } = useDocumentEnable()
const { mutateAsync: disableDocument } = useDocumentDisable()
const { mutateAsync: deleteDocument } = useDocumentDelete()
const { mutateAsync: syncDocument } = useSyncDocument()
const { mutateAsync: syncWebsite } = useSyncWebsite()
const isListScene = scene === 'list'
const onOperate = async (operationName: OperationName) => {
let opApi
switch (operationName) {
case 'archive':
opApi = archiveDocument
break
case 'un_archive':
opApi = unArchiveDocument
break
case 'enable':
opApi = enableDocument
break
case 'disable':
opApi = disableDocument
break
case 'sync':
if (data_source_type === 'notion_import')
opApi = syncDocument
else
opApi = syncWebsite
break
default:
opApi = deleteDocument
setDeleting(true)
break
}
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId, documentId: id }) as Promise<CommonResponse>)
if (!e) {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
onUpdate(operationName)
}
else { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) }
if (operationName === 'delete')
setDeleting(false)
}
const { run: handleSwitch } = useDebounceFn((operationName: OperationName) => {
if (operationName === 'enable' && enabled)
return
if (operationName === 'disable' && !enabled)
return
onOperate(operationName)
}, { wait: 500 })
const [currDocument, setCurrDocument] = useState<{
id: string
name: string
} | null>(null)
const [isShowRenameModal, {
setTrue: setShowRenameModalTrue,
setFalse: setShowRenameModalFalse,
}] = useBoolean(false)
const handleShowRenameModal = useCallback((doc: {
id: string
name: string
}) => {
setCurrDocument(doc)
setShowRenameModalTrue()
}, [setShowRenameModalTrue])
const handleRenamed = useCallback(() => {
onUpdate()
}, [onUpdate])
return <div className='flex items-center' onClick={e => e.stopPropagation()}>
{isListScene && !embeddingAvailable && (
<Switch defaultValue={false} onChange={noop} disabled={true} size='md' />
)}
{isListScene && embeddingAvailable && (
<>
{archived
? <Tooltip
popupContent={t('datasetDocuments.list.action.enableWarning')}
popupClassName='!font-semibold'
needsDelay
>
<div>
<Switch defaultValue={false} onChange={noop} disabled={true} size='md' />
</div>
</Tooltip>
: <Switch defaultValue={enabled} onChange={v => handleSwitch(v ? 'enable' : 'disable')} size='md' />
}
<Divider className='!ml-4 !mr-2 !h-3' type='vertical' />
</>
)}
{embeddingAvailable && (
<>
<Tooltip
popupContent={t('datasetDocuments.list.action.settings')}
popupClassName='text-text-secondary system-xs-medium'
>
<button
className={cn('mr-2 cursor-pointer rounded-lg',
!isListScene
? 'border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px] hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover'
: 'p-0.5 hover:bg-state-base-hover')}
onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}>
<RiEqualizer2Line className='h-4 w-4 text-components-button-secondary-text' />
</button>
</Tooltip>
<Popover
htmlContent={
<div className='w-full py-1'>
{!archived && (
<>
<div className={s.actionItem} onClick={() => {
handleShowRenameModal({
id: detail.id,
name: detail.name,
})
}}>
<RiEditLine className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.table.rename')}</span>
</div>
{['notion_import', DataSourceType.WEB].includes(data_source_type) && (
<div className={s.actionItem} onClick={() => onOperate('sync')}>
<RiLoopLeftLine className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.sync')}</span>
</div>
)}
<Divider className='my-1' />
</>
)}
{!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}>
<RiArchive2Line className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span>
</div>}
{archived && (
<div className={s.actionItem} onClick={() => onOperate('un_archive')}>
<RiArchive2Line className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.unarchive')}</span>
</div>
)}
<div className={cn(s.actionItem, s.deleteActionItem, 'group')} onClick={() => setShowModal(true)}>
<RiDeleteBinLine className={'h-4 w-4 text-text-tertiary group-hover:text-text-destructive'} />
<span className={cn(s.actionName, 'group-hover:text-text-destructive')}>{t('datasetDocuments.list.action.delete')}</span>
</div>
</div>
}
trigger='click'
position='br'
btnElement={
<div className={cn(s.commonIcon)}>
<RiMoreFill className='h-4 w-4 text-components-button-secondary-text' />
</div>
}
btnClassName={open => cn(isListScene ? s.actionIconWrapperList : s.actionIconWrapperDetail, open ? '!hover:bg-state-base-hover !shadow-none' : '!bg-transparent')}
popupClassName='!w-full'
className={`!z-20 flex h-fit !w-[200px] justify-end ${className}`}
/>
</>
)}
{showModal
&& <Confirm
isShow={showModal}
isLoading={deleting}
isDisabled={deleting}
title={t('datasetDocuments.list.delete.title')}
content={t('datasetDocuments.list.delete.content')}
confirmText={t('common.operation.sure')}
onConfirm={() => onOperate('delete')}
onCancel={() => setShowModal(false)}
/>
}
{isShowRenameModal && currDocument && (
<RenameModal
datasetId={datasetId}
documentId={currDocument.id}
name={currDocument.name}
onClose={setShowRenameModalFalse}
onSaved={handleRenamed}
/>
)}
</div>
}
import StatusItem from './status-item'
import Operations from './operations'
export const renderTdValue = (value: string | number | null, isEmptyStyle = false) => {
return (
@ -575,7 +233,6 @@ const DocumentList: FC<IDocumentListProps> = ({
)
}}
/>
{/* {doc.position} */}
{index + 1}
</div>
</td>
@ -623,7 +280,7 @@ const DocumentList: FC<IDocumentListProps> = ({
}
</td>
<td>
<OperationAction
<Operations
embeddingAvailable={embeddingAvailable}
datasetId={datasetId}
detail={pick(doc, ['name', 'enabled', 'archived', 'id', 'data_source_type', 'doc_form'])}

View File

@ -0,0 +1,238 @@
import React, { useCallback, useState } from 'react'
import { useContext } from 'use-context-selector'
import { ToastContext } from '../../base/toast'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { useDocumentArchive, useDocumentDelete, useDocumentDisable, useDocumentEnable, useDocumentUnArchive, useSyncDocument, useSyncWebsite } from '@/service/knowledge/use-document'
import type { OperationName } from './types'
import { asyncRunSafe } from '@/utils'
import type { CommonResponse } from '@/models/common'
import { useBoolean, useDebounceFn } from 'ahooks'
import Switch from '../../base/switch'
import { noop } from 'lodash'
import Tooltip from '../../base/tooltip'
import Divider from '../../base/divider'
import cn from '@/utils/classnames'
import { RiArchive2Line, RiDeleteBinLine, RiEditLine, RiEqualizer2Line, RiLoopLeftLine, RiMoreFill } from '@remixicon/react'
import CustomPopover from '../../base/popover'
import s from './style.module.css'
import { DataSourceType } from '@/models/datasets'
import Confirm from '../../base/confirm'
import RenameModal from './rename-modal'
type OperationsProps = {
embeddingAvailable: boolean
detail: {
name: string
enabled: boolean
archived: boolean
id: string
data_source_type: string
doc_form: string
}
datasetId: string
onUpdate: (operationName?: string) => void
scene?: 'list' | 'detail'
className?: string
}
const Operations = ({
embeddingAvailable,
datasetId,
detail,
onUpdate,
scene = 'list',
className = '',
}: OperationsProps) => {
const { t } = useTranslation()
const router = useRouter()
const { id, enabled = false, archived = false, data_source_type } = detail || {}
const [showModal, setShowModal] = useState(false)
const [deleting, setDeleting] = useState(false)
const { notify } = useContext(ToastContext)
const { mutateAsync: archiveDocument } = useDocumentArchive()
const { mutateAsync: unArchiveDocument } = useDocumentUnArchive()
const { mutateAsync: enableDocument } = useDocumentEnable()
const { mutateAsync: disableDocument } = useDocumentDisable()
const { mutateAsync: deleteDocument } = useDocumentDelete()
const { mutateAsync: syncDocument } = useSyncDocument()
const { mutateAsync: syncWebsite } = useSyncWebsite()
const isListScene = scene === 'list'
const onOperate = async (operationName: OperationName) => {
let opApi
switch (operationName) {
case 'archive':
opApi = archiveDocument
break
case 'un_archive':
opApi = unArchiveDocument
break
case 'enable':
opApi = enableDocument
break
case 'disable':
opApi = disableDocument
break
case 'sync':
if (data_source_type === 'notion_import')
opApi = syncDocument
else
opApi = syncWebsite
break
default:
opApi = deleteDocument
setDeleting(true)
break
}
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId, documentId: id }) as Promise<CommonResponse>)
if (!e) {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
onUpdate(operationName)
}
else { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) }
if (operationName === 'delete')
setDeleting(false)
}
const { run: handleSwitch } = useDebounceFn((operationName: OperationName) => {
if (operationName === 'enable' && enabled)
return
if (operationName === 'disable' && !enabled)
return
onOperate(operationName)
}, { wait: 500 })
const [currDocument, setCurrDocument] = useState<{
id: string
name: string
} | null>(null)
const [isShowRenameModal, {
setTrue: setShowRenameModalTrue,
setFalse: setShowRenameModalFalse,
}] = useBoolean(false)
const handleShowRenameModal = useCallback((doc: {
id: string
name: string
}) => {
setCurrDocument(doc)
setShowRenameModalTrue()
}, [setShowRenameModalTrue])
const handleRenamed = useCallback(() => {
onUpdate()
}, [onUpdate])
return <div className='flex items-center' onClick={e => e.stopPropagation()}>
{isListScene && !embeddingAvailable && (
<Switch defaultValue={false} onChange={noop} disabled={true} size='md' />
)}
{isListScene && embeddingAvailable && (
<>
{archived
? <Tooltip
popupContent={t('datasetDocuments.list.action.enableWarning')}
popupClassName='!font-semibold'
needsDelay
>
<div>
<Switch defaultValue={false} onChange={noop} disabled={true} size='md' />
</div>
</Tooltip>
: <Switch defaultValue={enabled} onChange={v => handleSwitch(v ? 'enable' : 'disable')} size='md' />
}
<Divider className='!ml-4 !mr-2 !h-3' type='vertical' />
</>
)}
{embeddingAvailable && (
<>
<Tooltip
popupContent={t('datasetDocuments.list.action.settings')}
popupClassName='text-text-secondary system-xs-medium'
>
<button
className={cn('mr-2 cursor-pointer rounded-lg',
!isListScene
? 'border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px] hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover'
: 'p-0.5 hover:bg-state-base-hover')}
onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}>
<RiEqualizer2Line className='h-4 w-4 text-components-button-secondary-text' />
</button>
</Tooltip>
<CustomPopover
htmlContent={
<div className='w-full py-1'>
{!archived && (
<>
<div className={s.actionItem} onClick={() => {
handleShowRenameModal({
id: detail.id,
name: detail.name,
})
}}>
<RiEditLine className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.table.rename')}</span>
</div>
{['notion_import', DataSourceType.WEB].includes(data_source_type) && (
<div className={s.actionItem} onClick={() => onOperate('sync')}>
<RiLoopLeftLine className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.sync')}</span>
</div>
)}
<Divider className='my-1' />
</>
)}
{!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}>
<RiArchive2Line className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span>
</div>}
{archived && (
<div className={s.actionItem} onClick={() => onOperate('un_archive')}>
<RiArchive2Line className='h-4 w-4 text-text-tertiary' />
<span className={s.actionName}>{t('datasetDocuments.list.action.unarchive')}</span>
</div>
)}
<div className={cn(s.actionItem, s.deleteActionItem, 'group')} onClick={() => setShowModal(true)}>
<RiDeleteBinLine className={'h-4 w-4 text-text-tertiary group-hover:text-text-destructive'} />
<span className={cn(s.actionName, 'group-hover:text-text-destructive')}>{t('datasetDocuments.list.action.delete')}</span>
</div>
</div>
}
trigger='click'
position='br'
btnElement={
<div className={cn(s.commonIcon)}>
<RiMoreFill className='h-4 w-4 text-components-button-secondary-text' />
</div>
}
btnClassName={open => cn(isListScene ? s.actionIconWrapperList : s.actionIconWrapperDetail, open ? '!hover:bg-state-base-hover !shadow-none' : '!bg-transparent')}
popupClassName='!w-full'
className={`!z-20 flex h-fit !w-[200px] justify-end ${className}`}
/>
</>
)}
{showModal
&& <Confirm
isShow={showModal}
isLoading={deleting}
isDisabled={deleting}
title={t('datasetDocuments.list.delete.title')}
content={t('datasetDocuments.list.delete.content')}
confirmText={t('common.operation.sure')}
onConfirm={() => onOperate('delete')}
onCancel={() => setShowModal(false)}
/>
}
{isShowRenameModal && currDocument && (
<RenameModal
datasetId={datasetId}
documentId={currDocument.id}
name={currDocument.name}
onClose={setShowRenameModalFalse}
onSaved={handleRenamed}
/>
)}
</div>
}
export default React.memo(Operations)

View File

@ -0,0 +1,15 @@
import { useTranslation } from 'react-i18next'
export const useIndexStatus = () => {
const { t } = useTranslation()
return {
queuing: { color: 'orange', text: t('datasetDocuments.list.status.queuing') }, // waiting
indexing: { color: 'blue', text: t('datasetDocuments.list.status.indexing') }, // indexing splitting parsing cleaning
paused: { color: 'orange', text: t('datasetDocuments.list.status.paused') }, // paused
error: { color: 'red', text: t('datasetDocuments.list.status.error') }, // error
available: { color: 'green', text: t('datasetDocuments.list.status.available') }, // completedarchived = falseenabled = true
enabled: { color: 'green', text: t('datasetDocuments.list.status.enabled') }, // completedarchived = falseenabled = true
disabled: { color: 'gray', text: t('datasetDocuments.list.status.disabled') }, // completedarchived = falseenabled = false
archived: { color: 'gray', text: t('datasetDocuments.list.status.archived') }, // completedarchived = true
}
}

View File

@ -0,0 +1,133 @@
import React, { useMemo } from 'react'
import type { ColorMap, IndicatorProps } from '@/app/components/header/indicator'
import Indicator from '@/app/components/header/indicator'
import type { DocumentDisplayStatus } from '@/models/datasets'
import { useContext } from 'use-context-selector'
import { useIndexStatus } from './hooks'
import { ToastContext } from '@/app/components/base/toast'
import { useTranslation } from 'react-i18next'
import { useDocumentDelete, useDocumentDisable, useDocumentEnable } from '@/service/knowledge/use-document'
import type { CommonResponse } from '@/models/common'
import { asyncRunSafe } from '@/utils'
import { useDebounceFn } from 'ahooks'
import s from '../style.module.css'
import cn from '@/utils/classnames'
import Tooltip from '@/app/components/base/tooltip'
import Switch from '@/app/components/base/switch'
import type { OperationName } from '../types'
const STATUS_TEXT_COLOR_MAP: ColorMap = {
green: 'text-util-colors-green-green-600',
orange: 'text-util-colors-warning-warning-600',
red: 'text-util-colors-red-red-600',
blue: 'text-util-colors-blue-light-blue-light-600',
yellow: 'text-util-colors-warning-warning-600',
gray: 'text-text-tertiary',
}
type StatusItemProps = {
status: DocumentDisplayStatus
reverse?: boolean
scene?: 'list' | 'detail'
textCls?: string
errorMessage?: string
detail?: {
enabled: boolean
archived: boolean
id: string
}
datasetId?: string
onUpdate?: (operationName?: string) => void
}
const StatusItem = ({
status,
reverse = false,
scene = 'list',
textCls = '',
errorMessage,
datasetId = '',
detail,
onUpdate,
}: StatusItemProps) => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const DOC_INDEX_STATUS_MAP = useIndexStatus()
const localStatus = status.toLowerCase() as keyof typeof DOC_INDEX_STATUS_MAP
const { enabled = false, archived = false, id = '' } = detail || {}
const { mutateAsync: enableDocument } = useDocumentEnable()
const { mutateAsync: disableDocument } = useDocumentDisable()
const { mutateAsync: deleteDocument } = useDocumentDelete()
const onOperate = async (operationName: OperationName) => {
let opApi = deleteDocument
switch (operationName) {
case 'enable':
opApi = enableDocument
break
case 'disable':
opApi = disableDocument
break
}
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId, documentId: id }) as Promise<CommonResponse>)
if (!e) {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
onUpdate?.()
}
else { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) }
}
const { run: handleSwitch } = useDebounceFn((operationName: OperationName) => {
if (operationName === 'enable' && enabled)
return
if (operationName === 'disable' && !enabled)
return
onOperate(operationName)
}, { wait: 500 })
const embedding = useMemo(() => {
return ['queuing', 'indexing', 'paused'].includes(localStatus)
}, [localStatus])
return <div className={
cn('flex items-center',
reverse ? 'flex-row-reverse' : '',
scene === 'detail' ? s.statusItemDetail : '')
}>
<Indicator color={DOC_INDEX_STATUS_MAP[localStatus]?.color as IndicatorProps['color']} className={reverse ? 'ml-2' : 'mr-2'} />
<span className={cn(`${STATUS_TEXT_COLOR_MAP[DOC_INDEX_STATUS_MAP[localStatus].color as keyof typeof STATUS_TEXT_COLOR_MAP]} text-sm`, textCls)}>
{DOC_INDEX_STATUS_MAP[localStatus]?.text}
</span>
{
errorMessage && (
<Tooltip
popupContent={
<div className='max-w-[260px] break-all'>{errorMessage}</div>
}
triggerClassName='ml-1 w-4 h-4'
/>
)
}
{
scene === 'detail' && (
<div className='ml-1.5 flex items-center justify-between'>
<Tooltip
popupContent={t('datasetDocuments.list.action.enableWarning')}
popupClassName='text-text-secondary system-xs-medium'
needsDelay
disabled={!archived}
>
<Switch
defaultValue={archived ? false : enabled}
onChange={v => !archived && handleSwitch(v ? 'enable' : 'disable')}
disabled={embedding || archived}
size='md'
/>
</Tooltip>
</div>
)
}
</div>
}
export default React.memo(StatusItem)

View File

@ -0,0 +1 @@
export type OperationName = 'delete' | 'archive' | 'enable' | 'disable' | 'sync' | 'un_archive'

View File

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import type { DataSet } from '@/models/datasets'
import { useSelector as useAppContextWithSelector } from '@/context/app-context'
import { useKnowledge } from '@/hooks/use-knowledge'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import type { Tag } from '@/app/components/base/tag-management/constant'
import TagSelector from '@/app/components/base/tag-management/selector'
import cn from '@/utils/classnames'
@ -49,7 +49,8 @@ const DatasetCard = ({
const isExternalProvider = useMemo(() => {
return dataset.provider === EXTERNAL_PROVIDER
}, [dataset.provider])
const Icon = isExternalProvider ? DOC_FORM_ICON_WITH_BG.external : DOC_FORM_ICON_WITH_BG[dataset.doc_form]
const chunkingModeIcon = dataset.doc_form ? DOC_FORM_ICON_WITH_BG[dataset.doc_form] : React.Fragment
const Icon = isExternalProvider ? DOC_FORM_ICON_WITH_BG.external : chunkingModeIcon
const iconInfo = dataset.icon_info || {
icon: '📙',
icon_type: 'emoji',
@ -133,9 +134,11 @@ const DatasetCard = ({
background={iconInfo.icon_type === 'image' ? undefined : iconInfo.icon_background}
imageUrl={iconInfo.icon_type === 'image' ? iconInfo.icon_url : undefined}
/>
<div className='absolute -bottom-1 -right-1 z-[5]'>
<Icon className='size-4' />
</div>
{(dataset.doc_form || isExternalProvider) && (
<div className='absolute -bottom-1 -right-1 z-[5]'>
<Icon className='size-4' />
</div>
)}
</div>
<div className='flex grow flex-col gap-y-1 overflow-hidden py-px'>
<div
@ -145,13 +148,12 @@ const DatasetCard = ({
{dataset.name}
</div>
<div className='system-2xs-medium-uppercase flex items-center gap-x-3 text-text-tertiary'>
{!isExternalProvider ? (
{isExternalProvider && <span>{t('dataset.externalKnowledgeBase')}</span>}
{!isExternalProvider && dataset.doc_form && dataset.indexing_technique && (
<>
<span>{t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`)}</span>
<span>{formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}</span>
</>
) : (
<span>{t('dataset.externalKnowledgeBase')}</span>
)}
</div>
</div>

View File

@ -29,7 +29,7 @@ const Datasets = ({
} = useDatasetList({
initialPage: 1,
tag_ids: tags,
limit: 20,
limit: 30,
include_all: includeAll,
keyword: keywords,
})

View File

@ -6,9 +6,6 @@ import type { MetadataFilteringVariableType } from '@/app/components/workflow/no
import type { MetadataItemWithValue } from '@/app/components/datasets/metadata/types'
import { ExternalKnowledgeBase, General, Graph, ParentChild, Qa } from '@/app/components/base/icons/src/public/knowledge/dataset-card'
import { GeneralChunk, ParentChildChunk, QuestionAndAnswer } from '@/app/components/base/icons/src/vender/knowledge'
import type { Edge, EnvironmentVariable, Node } from '@/app/components/workflow/types'
import type { Viewport } from 'reactflow'
import type { RAGPipelineVariables } from './pipeline'
export enum DataSourceType {
FILE = 'upload_file',
@ -761,14 +758,5 @@ export type CreateDatasetResponse = {
created_at: number
updated_by: string
updated_at: number
pipeline_info: {
id: string
graph: {
nodes: Node[]
edges: Edge[]
viewport?: Viewport
}
environment_variables: EnvironmentVariable[]
rag_pipeline_variables: RAGPipelineVariables
}
pipeline_id: string
}

View File

@ -44,14 +44,14 @@ export type CreateFormData = {
selectedMemberIDs: string[]
}
export type UpdatePipelineInfoRequest = {
pipeline_id: string
export type UpdateTemplateInfoRequest = {
template_id: string
name: string
icon_info: IconInfo
description: string
}
export type UpdatePipelineInfoResponse = {
export type UpdateTemplateInfoResponse = {
pipeline_id: string
name: string
icon_info: IconInfo
@ -59,16 +59,11 @@ export type UpdatePipelineInfoResponse = {
position: number
}
export type DeletePipelineResponse = {
export type DeleteTemplateResponse = {
code: number
}
export type ExportPipelineDSLRequest = {
pipeline_id: string
include_secret?: boolean
}
export type ExportPipelineDSLResponse = {
export type ExportTemplateDSLResponse = {
data: string
}

View File

@ -2,9 +2,8 @@ import type { MutationOptions } from '@tanstack/react-query'
import { useMutation, useQuery } from '@tanstack/react-query'
import { del, get, patch, post } from './base'
import type {
DeletePipelineResponse,
ExportPipelineDSLRequest,
ExportPipelineDSLResponse,
DeleteTemplateResponse,
ExportTemplateDSLResponse,
ImportPipelineDSLConfirmResponse,
ImportPipelineDSLRequest,
ImportPipelineDSLResponse,
@ -13,8 +12,8 @@ import type {
PipelineTemplateByIdResponse,
PipelineTemplateListParams,
PipelineTemplateListResponse,
UpdatePipelineInfoRequest,
UpdatePipelineInfoResponse,
UpdateTemplateInfoRequest,
UpdateTemplateInfoResponse,
} from '@/models/pipeline'
const NAME_SPACE = 'pipeline'
@ -23,7 +22,7 @@ export const usePipelineTemplateList = (params: PipelineTemplateListParams) => {
return useQuery<PipelineTemplateListResponse>({
queryKey: [NAME_SPACE, 'template', 'list'],
queryFn: () => {
return get<PipelineTemplateListResponse>('/rag/pipeline/template', { params })
return get<PipelineTemplateListResponse>('/rag/pipeline/templates', { params })
},
})
}
@ -32,20 +31,20 @@ export const usePipelineTemplateById = (templateId: string, enabled: boolean) =>
return useQuery<PipelineTemplateByIdResponse>({
queryKey: [NAME_SPACE, 'template', templateId],
queryFn: () => {
return get<PipelineTemplateByIdResponse>(`/rag/pipeline/template/${templateId}`)
return get<PipelineTemplateByIdResponse>(`/rag/pipeline/templates/${templateId}`)
},
enabled,
})
}
export const useUpdatePipelineInfo = (
mutationOptions: MutationOptions<UpdatePipelineInfoResponse, Error, UpdatePipelineInfoRequest> = {},
export const useUpdateTemplateInfo = (
mutationOptions: MutationOptions<UpdateTemplateInfoResponse, Error, UpdateTemplateInfoRequest> = {},
) => {
return useMutation({
mutationKey: [NAME_SPACE, 'template', 'update'],
mutationFn: (request: UpdatePipelineInfoRequest) => {
const { pipeline_id, ...rest } = request
return patch<UpdatePipelineInfoResponse>(`/rag/pipeline/${pipeline_id}`, {
mutationFn: (request: UpdateTemplateInfoRequest) => {
const { template_id, ...rest } = request
return patch<UpdateTemplateInfoResponse>(`/rag/customized/templates/${template_id}`, {
body: rest,
})
},
@ -53,29 +52,25 @@ export const useUpdatePipelineInfo = (
})
}
export const useDeletePipeline = (
mutationOptions: MutationOptions<DeletePipelineResponse, Error, string> = {},
export const useDeleteTemplate = (
mutationOptions: MutationOptions<DeleteTemplateResponse, Error, string> = {},
) => {
return useMutation({
mutationKey: [NAME_SPACE, 'template', 'delete'],
mutationFn: (pipelineId: string) => {
return del<DeletePipelineResponse>(`/rag/pipeline/${pipelineId}`)
mutationFn: (templateId: string) => {
return del<DeleteTemplateResponse>(`/rag/customized/templates/${templateId}`)
},
...mutationOptions,
})
}
export const useExportPipelineDSL = (
mutationOptions: MutationOptions<ExportPipelineDSLResponse, Error, ExportPipelineDSLRequest> = {},
export const useExportTemplateDSL = (
mutationOptions: MutationOptions<ExportTemplateDSLResponse, Error, ExportTemplateDSLRequest> = {},
) => {
return useMutation({
mutationKey: [NAME_SPACE, 'dsl-export'],
mutationFn: (request: ExportPipelineDSLRequest) => {
return get<ExportPipelineDSLResponse>(`/rag/pipeline/${request.pipeline_id}/export`, {
params: {
include_secret: !!request.include_secret,
},
})
mutationFn: (templateId: string) => {
return get<ExportTemplateDSLResponse>(`/rag/customized/templates/${templateId}`)
},
...mutationOptions,
})