feat: implement chunk structure card and related hooks for dataset creation; update translations and refactor pipeline template fetching

This commit is contained in:
twwu
2025-06-11 16:38:42 +08:00
parent 45c76c1d68
commit f995436eec
15 changed files with 190 additions and 45 deletions

View File

@ -8,19 +8,21 @@ const Header = () => {
const { t } = useTranslation()
return (
<Link
className='system-md-semibold relative flex px-16 pb-2 pt-5 text-text-primary'
href={'/datasets'}
replace
>
<div className='system-md-semibold relative flex px-16 pb-2 pt-5 text-text-primary'>
<span>{t('datasetPipeline.creation.title')}</span>
<Button
variant='secondary-accent'
className='absolute bottom-0 left-5 size-9 rounded-full p-0'
<Link
className='absolute bottom-0 left-5'
href={'/datasets'}
replace
>
<RiArrowLeftLine className='size-5 ' />
</Button>
</Link>
<Button
variant='secondary-accent'
className='size-9 rounded-full p-0'
>
<RiArrowLeftLine className='size-5 ' />
</Button>
</Link>
</div>
)
}

View File

@ -0,0 +1,66 @@
import React from 'react'
import cn from '@/utils/classnames'
import type { Option } from './types'
import { EffectColor } from './types'
const HEADER_EFFECT_MAP: Record<EffectColor, string> = {
[EffectColor.indigo]: 'bg-util-colors-indigo-indigo-600 opacity-80',
[EffectColor.blueLight]: 'bg-util-colors-blue-light-blue-light-500 opacity-80',
[EffectColor.green]: 'bg-util-colors-teal-teal-600 opacity-80',
[EffectColor.none]: '',
}
const IconBackgroundColorMap: Record<EffectColor, string> = {
[EffectColor.indigo]: 'bg-components-icon-bg-indigo-solid',
[EffectColor.blueLight]: 'bg-components-icon-bg-blue-light-solid',
[EffectColor.green]: 'bg-components-icon-bg-teal-solid',
[EffectColor.none]: '',
}
type ChunkStructureCardProps = {
className?: string
} & Option
const ChunkStructureCard = ({
className,
icon,
title,
description,
effectColor,
}: ChunkStructureCardProps) => {
return (
<div className={cn(
'relative flex overflow-hidden rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-bg p-2 shadow-xs shadow-shadow-shadow-3',
className,
)}>
<div className={cn(
'absolute -left-1 -top-1 size-14 rounded-full blur-[80px]',
`${HEADER_EFFECT_MAP[effectColor]}`,
)} />
<div className='p-1'>
<div className={cn(
'flex size-6 shrink-0 items-center justify-center rounded-lg border-[0.5px] border-divider-subtle text-text-primary-on-surface shadow-md shadow-shadow-shadow-5',
`${IconBackgroundColorMap[effectColor]}`,
)}>
{icon}
</div>
</div>
<div className='flex grow flex-col gap-y-0.5 py-px'>
<div className='flex items-center gap-x-1'>
<span className='system-sm-medium text-text-secondary'>
{title}
</span>
</div>
{
description && (
<div className='system-xs-regular text-text-tertiary'>
{description}
</div>
)
}
</div>
</div>
)
}
export default React.memo(ChunkStructureCard) as typeof ChunkStructureCard

View File

@ -0,0 +1,36 @@
import { GeneralChunk, ParentChildChunk, QuestionAndAnswer } from '@/app/components/base/icons/src/vender/knowledge'
import { useTranslation } from 'react-i18next'
import { EffectColor, type Option } from './types'
import { ChunkingMode } from '@/models/datasets'
export const useChunkStructureConfig = () => {
const { t } = useTranslation()
const GeneralOption: Option = {
icon: <GeneralChunk className='size-4' />,
title: 'General',
description: t('datasetCreation.stepTwo.generalTip'),
effectColor: EffectColor.indigo,
}
const ParentChildOption: Option = {
icon: <ParentChildChunk className='size-4' />,
title: 'Parent-Child',
description: t('datasetCreation.stepTwo.parentChildTip'),
effectColor: EffectColor.blueLight,
}
const QuestionAnswerOption: Option = {
icon: <QuestionAndAnswer className='size-4' />,
title: 'Q&A',
description: t('datasetCreation.stepTwo.qaTip'),
effectColor: EffectColor.green,
}
const chunkStructureConfig: Record<ChunkingMode, Option> = {
[ChunkingMode.text]: GeneralOption,
[ChunkingMode.parentChild]: ParentChildOption,
[ChunkingMode.qa]: QuestionAnswerOption,
}
return chunkStructureConfig
}

View File

@ -1,4 +1,4 @@
import React from 'react'
import React, { useMemo } from 'react'
import AppIcon from '@/app/components/base/app-icon'
import { usePipelineTemplateById } from '@/service/use-pipeline'
import type { AppIconType } from '@/types/app'
@ -7,6 +7,8 @@ import Button from '@/app/components/base/button'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import Loading from '@/app/components/base/loading'
import { useChunkStructureConfig } from './hooks'
import ChunkStructureCard from './chunk-structure-card'
import WorkflowPreview from '@/app/components/workflow/workflow-preview'
type DetailsProps = {
@ -23,16 +25,22 @@ const Details = ({
onClose,
}: DetailsProps) => {
const { t } = useTranslation()
const { data: pipelineTemplateInfo } = usePipelineTemplateById(id, type, true)
const appIcon = React.useMemo(() => {
const { data: pipelineTemplateInfo } = usePipelineTemplateById({
template_id: id,
type,
}, true)
const appIcon = useMemo(() => {
if (!pipelineTemplateInfo)
return { type: 'emoji', icon: '📙', background: '#FFF4ED' }
const iconInfo = pipelineTemplateInfo.icon
const iconInfo = pipelineTemplateInfo.icon_info
return iconInfo.icon_type === 'image'
? { type: 'image', url: iconInfo.icon_url || '', fileId: iconInfo.icon || '' }
: { type: 'icon', icon: iconInfo.icon || '', background: iconInfo.icon_background || '' }
}, [pipelineTemplateInfo])
const chunkStructureConfig = useChunkStructureConfig()
if (!pipelineTemplateInfo) {
return (
<Loading type='app' />
@ -42,9 +50,7 @@ const Details = ({
return (
<div className='flex h-full'>
<div className='flex grow items-center justify-center p-3 pr-0'>
<WorkflowPreview
{...pipelineTemplateInfo.export_data.workflow.graph}
/>
<WorkflowPreview {...pipelineTemplateInfo.graph} />
</div>
<div className='relative flex w-[360px] shrink-0 flex-col'>
<button
@ -68,7 +74,9 @@ const Details = ({
{pipelineTemplateInfo.name}
</div>
<div className='system-2xs-medium-uppercase text-text-tertiary'>
{`By ${pipelineTemplateInfo.author}`}
{t('datasetPipeline.details.createdBy', {
author: pipelineTemplateInfo.created_by,
})}
</div>
</div>
</div>
@ -86,14 +94,16 @@ const Details = ({
</Button>
</div>
<div className='flex flex-col gap-y-1 px-4 py-2'>
<div className='flex items-center gap-x-0.5'>
<div className='flex h-6 items-center gap-x-0.5'>
<span className='system-sm-semibold-uppercase text-text-secondary'>
{t('datasetPipeline.details.structure')}
</span>
<Tooltip
popupClassName='max-w-[240px]'
popupContent={t('datasetPipeline.details.structureTooltip')}
/>
</div>
<ChunkStructureCard {...chunkStructureConfig[pipelineTemplateInfo.chunk_structure]} />
</div>
</div>
</div>

View File

@ -0,0 +1,15 @@
import type { ReactNode } from 'react'
export enum EffectColor {
indigo = 'indigo',
blueLight = 'blue-light',
green = 'green',
none = 'none',
}
export type Option = {
icon: ReactNode
title: string
description?: string
effectColor: EffectColor
}

View File

@ -40,7 +40,10 @@ const TemplateCard = ({
const [showDetailModal, setShowDetailModal] = useState(false)
const [showCreateModal, setShowCreateModal] = useState(false)
const { refetch: getPipelineTemplateInfo } = usePipelineTemplateById(pipeline.id, type, false)
const { refetch: getPipelineTemplateInfo } = usePipelineTemplateById({
template_id: pipeline.id,
type,
}, false)
const { mutateAsync: createEmptyDataset } = useCreatePipelineDataset()
const { handleCheckPluginDependencies } = usePluginDependencies()

View File

@ -5,14 +5,17 @@ import {
} from '@/app/components/base/icons/src/vender/knowledge'
import { EffectColor, type Option } from './types'
import { ChunkingMode } from '@/models/datasets'
import { useTranslation } from 'react-i18next'
export const useChunkStructure = () => {
const { t } = useTranslation()
const GeneralOption: Option = {
id: ChunkingMode.text,
icon: <GeneralChunk className='size-[18px]' />,
iconActiveColor: 'text-util-colors-indigo-indigo-600',
title: 'General',
description: 'General text chunking mode, the chunks retrieved and recalled are the same.',
description: t('datasetCreation.stepTwo.generalTip'),
effectColor: EffectColor.indigo,
showEffectColor: true,
}
@ -21,7 +24,7 @@ export const useChunkStructure = () => {
icon: <ParentChildChunk className='size-[18px]' />,
iconActiveColor: 'text-util-colors-blue-light-blue-light-500',
title: 'Parent-Child',
description: 'When using the parent-child mode, the child-chunk is used for retrieval and the parent-chunk is used for recall as context.',
description: t('datasetCreation.stepTwo.parentChildTip'),
effectColor: EffectColor.blueLight,
showEffectColor: true,
}
@ -29,7 +32,7 @@ export const useChunkStructure = () => {
id: ChunkingMode.qa,
icon: <QuestionAndAnswer className='size-[18px]' />,
title: 'Q&A',
description: 'When using structured Q&A data, you can create documents that pair questions with answers. These documents are indexed based on the question portion, allowing the system to retrieve relevant answers based on query similarity',
description: t('datasetCreation.stepTwo.qaTip'),
}
const options = [

View File

@ -41,7 +41,7 @@ export const useChunkStructure = () => {
id: ChunkStructureEnum.question_answer,
icon: <QuestionAndAnswer className='h-[18px] w-[18px] text-text-tertiary' />,
title: 'Question-Answer',
description: 'Question-answer text chunking mode, the chunks retrieved and recalled are different.',
description: t('datasetCreation.stepTwo.qaTip'),
}
const optionMap: Record<ChunkStructureEnum, Option> = {