浏览代码

feat: Add conversion functionality to Knowledge Pipeline with UI and translations

tags/2.0.0-beta.1
twwu 3 个月前
父节点
当前提交
a2e0bc26c6

+ 27
- 13
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx 查看文件

@@ -1,6 +1,6 @@
'use client'
import type { FC } from 'react'
import React, { useEffect, useMemo } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import { usePathname } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import type { RemixiconComponentType } from '@remixicon/react'
@@ -23,6 +23,8 @@ import { PipelineFill, PipelineLine } from '@/app/components/base/icons/src/vend
import { useDatasetDetail, useDatasetRelatedApps } from '@/service/knowledge/use-dataset'
import useDocumentTitle from '@/hooks/use-document-title'
import ExtraInfo from '@/app/components/datasets/extra-info'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import cn from '@/utils/classnames'

export type IAppDetailLayoutProps = {
children: React.ReactNode
@@ -34,9 +36,18 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
children,
params: { datasetId },
} = props
const { t } = useTranslation()
const pathname = usePathname()
const hideSideBar = pathname.endsWith('documents/create') || pathname.endsWith('documents/create-from-pipeline')
const { t } = useTranslation()
const isPipelineCanvas = pathname.endsWith('/pipeline')
const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
const { eventEmitter } = useEventEmitterContextContext()

eventEmitter?.useSubscription((v: any) => {
if (v?.type === 'workflow-canvas-maximize')
setHideHeader(v.payload)
})
const { isCurrentWorkspaceDatasetOperator } = useAppContext()

const media = useBreakpoints()
@@ -75,15 +86,13 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
]

if (datasetRes?.provider !== 'external') {
if (datasetRes?.runtime_mode === 'rag_pipeline') {
baseNavigation.unshift({
name: t('common.datasetMenus.pipeline'),
href: `/datasets/${datasetId}/pipeline`,
icon: PipelineLine as RemixiconComponentType,
selectedIcon: PipelineFill as RemixiconComponentType,
disabled: false,
})
}
baseNavigation.unshift({
name: t('common.datasetMenus.pipeline'),
href: `/datasets/${datasetId}/pipeline`,
icon: PipelineLine as RemixiconComponentType,
selectedIcon: PipelineFill as RemixiconComponentType,
disabled: false,
})
baseNavigation.unshift({
name: t('common.datasetMenus.documents'),
href: `/datasets/${datasetId}/documents`,
@@ -94,7 +103,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
}

return baseNavigation
}, [t, datasetId, isButtonDisabledWithPipeline, datasetRes?.provider, datasetRes?.runtime_mode])
}, [t, datasetId, isButtonDisabledWithPipeline, datasetRes?.provider])

useDocumentTitle(datasetRes?.name || t('common.menus.datasets'))

@@ -110,7 +119,12 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return <Loading type='app' />

return (
<div className='flex grow overflow-hidden'>
<div
className={cn(
'flex grow overflow-hidden',
hideHeader && isPipelineCanvas ? '' : 'rounded-t-2xl border-t border-effects-highlight',
)}
>
<DatasetDetailContext.Provider value={{
indexingTechnique: datasetRes?.indexing_technique,
dataset: datasetRes,

+ 71
- 0
web/app/components/rag-pipeline/conversion.tsx 查看文件

@@ -0,0 +1,71 @@
import React, { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '../base/button'
import PipelineScreenShot from './screenshot'
import Confirm from '../base/confirm'

const Conversion = () => {
const { t } = useTranslation()
const [showConfirmModal, setShowConfirmModal] = useState(false)

const handleConvert = useCallback(() => {
setShowConfirmModal(false)
// todo: Add conversion logic here
}, [])

const handleShowConfirmModal = useCallback(() => {
setShowConfirmModal(true)
}, [])

const handleCancelConversion = useCallback(() => {
setShowConfirmModal(false)
}, [])

return (
<div className='flex h-full w-full items-center justify-center bg-background-body p-6 pb-16'>
<div className='flex rounded-2xl border-[0.5px] border-components-card-border bg-components-card-bg shadow-sm shadow-shadow-shadow-4'>
<div className='flex max-w-[480px] flex-col justify-between p-10'>
<div className='flex flex-col gap-y-2.5'>
<div className='title-4xl-semi-bold text-text-primary'>
{t('datasetPipeline.conversion.title')}
</div>
<div className='body-md-medium'>
<span className='text-text-secondary'>{t('datasetPipeline.conversion.descriptionChunk1')}</span>
<span className='text-text-tertiary'>{t('datasetPipeline.conversion.descriptionChunk2')}</span>
</div>
</div>
<div className='flex items-center gap-x-4'>
<Button
variant='primary'
className='w-32'
onClick={handleShowConfirmModal}
>
{t('datasetPipeline.operations.convert')}
</Button>
<span className='system-xs-regular text-text-warning'>
{t('datasetPipeline.conversion.warning')}
</span>
</div>
</div>
<div className='pb-8 pl-[25px] pr-0 pt-6'>
<div className='rounded-l-xl border border-effects-highlight bg-background-default p-1 shadow-md shadow-shadow-shadow-5 backdrop-blur-[5px]'>
<div className='overflow-hidden rounded-l-lg'>
<PipelineScreenShot />
</div>
</div>
</div>
</div>
{showConfirmModal && (
<Confirm
title={t('datasetPipeline.conversion.confirm.title')}
content={t('datasetPipeline.conversion.confirm.content')}
isShow={showConfirmModal}
onConfirm={handleConvert}
onCancel={handleCancelConversion}
/>
)}
</div>
)
}

export default React.memo(Conversion)

+ 7
- 0
web/app/components/rag-pipeline/index.tsx 查看文件

@@ -12,6 +12,8 @@ import Loading from '@/app/components/base/loading'
import { createRagPipelineSliceSlice } from './store'
import RagPipelineMain from './components/rag-pipeline-main'
import { usePipelineInit } from './hooks'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import Conversion from './conversion'

const RagPipeline = () => {
const {
@@ -53,6 +55,11 @@ const RagPipeline = () => {
}

const RagPipelineWrapper = () => {
const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id)

if (!pipelineId)
return <Conversion />

return (
<WorkflowContextProvider
injectWorkflowStoreSliceFn={createRagPipelineSliceSlice as InjectWorkflowStoreSliceFn}

+ 22
- 0
web/app/components/rag-pipeline/screenshot.tsx 查看文件

@@ -0,0 +1,22 @@
import React from 'react'
import useTheme from '@/hooks/use-theme'
import { basePath } from '@/utils/var'
import Image from 'next/image'

const PipelineScreenShot = () => {
const { theme } = useTheme()

return (
<picture>
<source media="(resolution: 1x)" srcSet={`${basePath}/screenshots/${theme}/Pipeline.png`} />
<source media="(resolution: 2x)" srcSet={`${basePath}/screenshots/${theme}/Pipeline@2x.png`} />
<source media="(resolution: 3x)" srcSet={`${basePath}/screenshots/${theme}/Pipeline@3x.png`} />
<Image
src={`${basePath}/screenshots/${theme}/Pipeline.png`}
alt='Pipeline Screenshot'
width={692} height={456} />
</picture>
)
}

export default React.memo(PipelineScreenShot)

+ 11
- 0
web/i18n/en-US/dataset-pipeline.ts 查看文件

@@ -30,6 +30,7 @@ const translation = {
saveAndProcess: 'Save & Process',
preview: 'Preview',
exportPipeline: 'Export Pipeline',
convert: 'Convert',
},
knowledgeNameAndIcon: 'Knowledge name & icon',
knowledgeNameAndIconPlaceholder: 'Please enter the name of the Knowledge Base',
@@ -126,6 +127,16 @@ const translation = {
emptySearchResult: 'No items were found',
resetKeywords: 'Reset keywords',
},
conversion: {
title: 'Convert to Knowledge Pipeline',
descriptionChunk1: 'You can now convert your existing knowledge base to use the Knowledge Pipeline for document processing',
descriptionChunk2: ' — a more open and flexible approach with access to plugins from our marketplace. This will apply the new processing method to all future documents.',
warning: 'This action cannot be undone',
confirm: {
title: 'Confirmation',
content: 'This action is permanent. You won\'t be able to revert to the previous method.Please confirm to convert.',
},
},
}

export default translation

+ 11
- 0
web/i18n/zh-Hans/dataset-pipeline.ts 查看文件

@@ -30,6 +30,7 @@ const translation = {
saveAndProcess: '保存并处理',
preview: '预览',
exportPipeline: '导出 pipeline',
convert: '转换',
},
knowledgeNameAndIcon: '知识库名称和图标',
knowledgeNameAndIconPlaceholder: '请输入知识库名称',
@@ -126,6 +127,16 @@ const translation = {
emptySearchResult: '未找到任何项目',
resetKeywords: '重置关键词',
},
conversion: {
title: '转换为知识库 pipeline',
descriptionChunk1: '您现在可以将现有知识库转换为使用知识库 pipeline 来处理文档',
descriptionChunk2: ' —— 这是一种更开放、更灵活的方式,可以访问我们市场中的插件。新的处理方式将应用到后续添加的所有文档。',
warning: '此操作无法撤销',
confirm: {
title: '确认',
content: '此操作是永久性的。您将无法恢复到之前的方式。请确认转换。',
},
},
}

export default translation

二进制
web/public/screenshots/dark/Pipeline.png 查看文件


二进制
web/public/screenshots/dark/Pipeline@2x.png 查看文件


二进制
web/public/screenshots/dark/Pipeline@3x.png 查看文件


二进制
web/public/screenshots/light/Pipeline.png 查看文件


二进制
web/public/screenshots/light/Pipeline@2x.png 查看文件


二进制
web/public/screenshots/light/Pipeline@3x.png 查看文件


正在加载...
取消
保存