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

pull/21398/head
twwu 11 months ago
parent 019ef74bf2
commit 35a7add4e9

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

@ -173,11 +173,6 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
}}> }}>
{!hideSideBar && ( {!hideSideBar && (
<AppSideBar <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} navigation={navigation}
extraInfo={ extraInfo={
!isCurrentWorkspaceDatasetOperator !isCurrentWorkspaceDatasetOperator

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

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

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

@ -1,57 +1,9 @@
import { usePipelineTemplateList } from '@/service/use-pipeline' import { usePipelineTemplateList } from '@/service/use-pipeline'
import TemplateCard from './template-card' import TemplateCard from './template-card'
import { ChunkingMode } from '@/models/datasets'
import type { PipelineTemplate } from '@/models/pipeline'
const BuiltInPipelineList = () => { 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 { data: pipelineList, isLoading } = usePipelineTemplateList({ type: 'built-in' })
const list = pipelineList?.pipelines || mockData const list = pipelineList?.pipelines
if (isLoading || !list) if (isLoading || !list)
return null return null

@ -1,57 +1,9 @@
import { ChunkingMode } from '@/models/datasets'
import TemplateCard from './template-card' import TemplateCard from './template-card'
import { usePipelineTemplateList } from '@/service/use-pipeline' import { usePipelineTemplateList } from '@/service/use-pipeline'
import type { PipelineTemplate } from '@/models/pipeline'
const CustomizedList = () => { 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 { data: pipelineList, isLoading } = usePipelineTemplateList({ type: 'customized' })
const list = pipelineList?.pipelines || mockData const list = pipelineList?.pipelines
if (isLoading || !list) if (isLoading || !list)
return null return null

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

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

@ -1,18 +1,10 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React, { useCallback, useEffect, useMemo, useState } 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 { ArrowDownIcon } from '@heroicons/react/24/outline'
import { pick, uniq } from 'lodash-es' import { pick, uniq } from 'lodash-es'
import { import { RiEditLine } from '@remixicon/react'
RiArchive2Line,
RiDeleteBinLine,
RiEditLine,
RiEqualizer2Line,
RiLoopLeftLine,
RiMoreFill,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import dayjs from 'dayjs' import dayjs from 'dayjs'
@ -23,359 +15,25 @@ import s from './style.module.css'
import RenameModal from './rename-modal' import RenameModal from './rename-modal'
import BatchAction from './detail/completed/common/batch-action' import BatchAction from './detail/completed/common/batch-action'
import cn from '@/utils/classnames' 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 Tooltip from '@/app/components/base/tooltip'
import Toast, { ToastContext } from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import type { ColorMap, IndicatorProps } from '@/app/components/header/indicator'
import Indicator from '@/app/components/header/indicator'
import { asyncRunSafe } from '@/utils' import { asyncRunSafe } from '@/utils'
import { formatNumber } from '@/utils/format' import { formatNumber } from '@/utils/format'
import NotionIcon from '@/app/components/base/notion-icon' import NotionIcon from '@/app/components/base/notion-icon'
import ProgressBar from '@/app/components/base/progress-bar' 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 type { CommonResponse } from '@/models/common'
import useTimestamp from '@/hooks/use-timestamp' import useTimestamp from '@/hooks/use-timestamp'
import { useDatasetDetailContextWithSelector as useDatasetDetailContext } from '@/context/dataset-detail' import { useDatasetDetailContextWithSelector as useDatasetDetailContext } from '@/context/dataset-detail'
import type { Props as PaginationProps } from '@/app/components/base/pagination' import type { Props as PaginationProps } from '@/app/components/base/pagination'
import Pagination from '@/app/components/base/pagination' import Pagination from '@/app/components/base/pagination'
import Checkbox from '@/app/components/base/checkbox' 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 { extensionToFileType } from '@/app/components/datasets/hit-testing/utils/extension-to-file-type'
import useBatchEditDocumentMetadata from '../metadata/hooks/use-batch-edit-document-metadata' import useBatchEditDocumentMetadata from '../metadata/hooks/use-batch-edit-document-metadata'
import EditMetadataBatchModal from '@/app/components/datasets/metadata/edit-metadata-batch/modal' import EditMetadataBatchModal from '@/app/components/datasets/metadata/edit-metadata-batch/modal'
import { noop } from 'lodash-es' import StatusItem from './status-item'
import Operations from './operations'
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>
}
export const renderTdValue = (value: string | number | null, isEmptyStyle = false) => { export const renderTdValue = (value: string | number | null, isEmptyStyle = false) => {
return ( return (
@ -575,7 +233,6 @@ const DocumentList: FC<IDocumentListProps> = ({
) )
}} }}
/> />
{/* {doc.position} */}
{index + 1} {index + 1}
</div> </div>
</td> </td>
@ -623,7 +280,7 @@ const DocumentList: FC<IDocumentListProps> = ({
} }
</td> </td>
<td> <td>
<OperationAction <Operations
embeddingAvailable={embeddingAvailable} embeddingAvailable={embeddingAvailable}
datasetId={datasetId} datasetId={datasetId}
detail={pick(doc, ['name', 'enabled', 'archived', 'id', 'data_source_type', 'doc_form'])} detail={pick(doc, ['name', 'enabled', 'archived', 'id', 'data_source_type', 'doc_form'])}

@ -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)

@ -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
}
}

@ -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)

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

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

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

@ -6,9 +6,6 @@ import type { MetadataFilteringVariableType } from '@/app/components/workflow/no
import type { MetadataItemWithValue } from '@/app/components/datasets/metadata/types' 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 { 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 { 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 { export enum DataSourceType {
FILE = 'upload_file', FILE = 'upload_file',
@ -761,14 +758,5 @@ export type CreateDatasetResponse = {
created_at: number created_at: number
updated_by: string updated_by: string
updated_at: number updated_at: number
pipeline_info: { pipeline_id: string
id: string
graph: {
nodes: Node[]
edges: Edge[]
viewport?: Viewport
}
environment_variables: EnvironmentVariable[]
rag_pipeline_variables: RAGPipelineVariables
}
} }

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

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

Loading…
Cancel
Save