feat: refactor docForm type to use ChuckingMode and improve UI for question/answer sections

This commit is contained in:
twwu 2024-12-16 18:15:54 +08:00
parent 14f14420e7
commit 4850a99ce5
7 changed files with 66 additions and 37 deletions

View File

@ -9,7 +9,7 @@ import ChunkContent from './common/chunk-content'
import Dot from './common/dot'
import { SegmentIndexTag } from './common/segment-index-tag'
import { useSegmentListContext } from './index'
import type { ChildChunkDetail } from '@/models/datasets'
import type { ChildChunkDetail, ChuckingMode } from '@/models/datasets'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { formatNumber } from '@/utils/format'
import classNames from '@/utils/classnames'
@ -21,7 +21,7 @@ type IChildSegmentDetailProps = {
childChunkInfo?: Partial<ChildChunkDetail> & { id: string }
onUpdate: (segmentId: string, childChunkId: string, content: string) => void
onCancel: () => void
docForm: string
docForm: ChuckingMode
}
/**

View File

@ -1,5 +1,6 @@
import React, { type FC } from 'react'
import { useTranslation } from 'react-i18next'
import { ChuckingMode } from '@/models/datasets'
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common'
type IChunkContentProps = {
@ -8,7 +9,7 @@ type IChunkContentProps = {
onQuestionChange: (question: string) => void
onAnswerChange?: (answer: string) => void
isEditMode?: boolean
docForm: string
docForm: ChuckingMode
}
const ChunkContent: FC<IChunkContentProps> = ({
@ -21,22 +22,22 @@ const ChunkContent: FC<IChunkContentProps> = ({
}) => {
const { t } = useTranslation()
if (docForm === 'qa_model') {
if (docForm === ChuckingMode.qa) {
return (
<>
<div className='mb-1 text-xs font-medium text-gray-500'>QUESTION</div>
<div className='text-text-tertiary text-xs font-medium'>QUESTION</div>
<AutoHeightTextarea
outerClassName='mb-4'
className='leading-6 text-md text-gray-800'
outerClassName='mb-6 mt-1'
className='text-text-secondary text-sm tracking-[-0.07px] caret-[#295EFF]'
value={question}
placeholder={t('datasetDocuments.segment.questionPlaceholder') || ''}
onChange={e => onQuestionChange(e.target.value)}
disabled={!isEditMode}
/>
<div className='mb-1 text-xs font-medium text-gray-500'>ANSWER</div>
<div className='text-text-tertiary text-xs font-medium'>ANSWER</div>
<AutoHeightTextarea
outerClassName='mb-4'
className='leading-6 text-md text-gray-800'
outerClassName='mb-6 mt-1'
className='text-text-secondary text-sm tracking-[-0.07px] caret-[#295EFF]'
value={answer}
placeholder={t('datasetDocuments.segment.answerPlaceholder') || ''}
onChange={e => onAnswerChange?.(e.target.value)}

View File

@ -25,7 +25,7 @@ import { ToastContext } from '@/app/components/base/toast'
import type { Item } from '@/app/components/base/select'
import { SimpleSelect } from '@/app/components/base/select'
import { updateSegment } from '@/service/datasets'
import type { ChildChunkDetail, SegmentDetailModel, SegmentUpdater } from '@/models/datasets'
import { type ChildChunkDetail, ChuckingMode, type SegmentDetailModel, type SegmentUpdater } from '@/models/datasets'
import NewSegment from '@/app/components/datasets/documents/detail/new-segment'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import Checkbox from '@/app/components/base/checkbox'
@ -252,7 +252,7 @@ const Completed: FC<ICompletedProps> = ({
needRegenerate = false,
) => {
const params: SegmentUpdater = { content: '' }
if (docForm === 'qa_model') {
if (docForm === ChuckingMode.qa) {
if (!question.trim())
return notify({ type: 'error', message: t('datasetDocuments.segment.questionEmpty') })
if (!answer.trim())

View File

@ -14,7 +14,7 @@ import Dot from './common/dot'
import { useSegmentListContext } from './index'
import { useStore as useAppStore } from '@/app/components/app/store'
import { ToastContext } from '@/app/components/base/toast'
import type { ChildChunkDetail, SegmentUpdater } from '@/models/datasets'
import { type ChildChunkDetail, ChuckingMode, type SegmentUpdater } from '@/models/datasets'
import classNames from '@/utils/classnames'
import { formatNumber } from '@/utils/format'
import Divider from '@/app/components/base/divider'
@ -144,7 +144,7 @@ const NewChildSegmentModal: FC<NewChildSegmentModalProps> = ({
<div className={classNames('flex grow overflow-hidden', fullScreen ? 'w-full flex-row justify-center px-6 pt-6 gap-x-8' : 'flex-col gap-y-1 py-3 px-4')}>
<div className={classNames('break-all overflow-y-auto whitespace-pre-line', fullScreen ? 'w-1/2' : 'grow')}>
<ChunkContent
docForm=''
docForm={ChuckingMode.parentChild}
question={content}
onQuestionChange={content => setContent(content)}
isEditMode={true}

View File

@ -98,13 +98,22 @@ const SegmentCard: FC<ISegmentCardProps> = ({
if (answer) {
return (
<>
<div className='flex'>
<div className='flex gap-x-1'>
<div className='w-4 mr-2 text-[13px] font-medium leading-[20px] text-text-tertiary'>Q</div>
<div className='text-text-secondary body-md-regular'>{content}</div>
<div
className={cn('text-text-secondary body-md-regular',
isCollapsed ? 'line-clamp-2' : 'line-clamp-20',
)}>
{content}
</div>
</div>
<div className='flex'>
<div className='flex gap-x-1'>
<div className='w-4 mr-2 text-[13px] font-medium leading-[20px] text-text-tertiary'>A</div>
<div className='text-text-secondary body-md-regular'>{answer}</div>
<div className={cn('text-text-secondary body-md-regular',
isCollapsed ? 'line-clamp-2' : 'line-clamp-20',
)}>
{answer}
</div>
</div>
</>
)

View File

@ -12,7 +12,7 @@ import RegenerationModal from './common/regeneration-modal'
import { SegmentIndexTag } from './common/segment-index-tag'
import Dot from './common/dot'
import { useSegmentListContext } from './index'
import type { SegmentDetailModel } from '@/models/datasets'
import { ChuckingMode, type SegmentDetailModel } from '@/models/datasets'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { formatNumber } from '@/utils/format'
import classNames from '@/utils/classnames'
@ -23,7 +23,7 @@ type ISegmentDetailProps = {
onUpdate: (segmentId: string, q: string, a: string, k: string[], needRegenerate?: boolean) => void
onCancel: () => void
isEditMode?: boolean
docForm: string
docForm: ChuckingMode
}
/**
@ -85,12 +85,17 @@ const SegmentDetail: FC<ISegmentDetailProps> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEditMode])
const isQAModel = useMemo(() => {
return docForm === ChuckingMode.qa
}, [docForm])
const wordCountText = useMemo(() => {
const total = formatNumber(isEditMode ? question.length : segInfo!.word_count as number)
const count = isEditMode ? question.length : segInfo!.word_count as number
const contentLength = isQAModel ? (question.length + answer.length) : question.length
const total = formatNumber(isEditMode ? contentLength : segInfo!.word_count as number)
const count = isEditMode ? contentLength : segInfo!.word_count as number
return `${total} ${t('datasetDocuments.segment.characters', { count })}`
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEditMode, question.length, segInfo?.word_count])
}, [isEditMode, question.length, answer.length, segInfo?.word_count, isQAModel])
const labelPrefix = useMemo(() => {
return isParentChildMode ? t('datasetDocuments.segment.parentChunk') : t('datasetDocuments.segment.chunk')

View File

@ -15,7 +15,7 @@ import Dot from './completed/common/dot'
import { useDocumentContext } from './index'
import { useStore as useAppStore } from '@/app/components/app/store'
import { ToastContext } from '@/app/components/base/toast'
import type { SegmentUpdater } from '@/models/datasets'
import { ChuckingMode, type SegmentUpdater } from '@/models/datasets'
import { addSegment } from '@/service/datasets'
import classNames from '@/utils/classnames'
import { formatNumber } from '@/utils/format'
@ -23,7 +23,7 @@ import Divider from '@/app/components/base/divider'
type NewSegmentModalProps = {
onCancel: () => void
docForm: string
docForm: ChuckingMode
onSave: () => void
viewNewlyAddedChunk: () => void
}
@ -59,6 +59,10 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
</button>
</>
const isQAModel = useMemo(() => {
return docForm === ChuckingMode.qa
}, [docForm])
const handleCancel = (actionType: 'esc' | 'add' = 'esc') => {
if (actionType === 'esc' || !addAnother)
onCancel()
@ -69,18 +73,30 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
const handleSave = async () => {
const params: SegmentUpdater = { content: '' }
if (docForm === 'qa_model') {
if (!question.trim())
return notify({ type: 'error', message: t('datasetDocuments.segment.questionEmpty') })
if (!answer.trim())
return notify({ type: 'error', message: t('datasetDocuments.segment.answerEmpty') })
if (isQAModel) {
if (!question.trim()) {
return notify({
type: 'error',
message: t('datasetDocuments.segment.questionEmpty'),
})
}
if (!answer.trim()) {
return notify({
type: 'error',
message: t('datasetDocuments.segment.answerEmpty'),
})
}
params.content = question
params.answer = answer
}
else {
if (!question.trim())
return notify({ type: 'error', message: t('datasetDocuments.segment.contentEmpty') })
if (!question.trim()) {
return notify({
type: 'error',
message: t('datasetDocuments.segment.contentEmpty'),
})
}
params.content = question
}
@ -109,19 +125,17 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
}
const wordCountText = useMemo(() => {
const count = question.length
const count = isQAModel ? (question.length + answer.length) : question.length
return `${formatNumber(count)} ${t('datasetDocuments.segment.characters', { count })}`
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [question.length])
}, [question.length, answer.length, isQAModel])
return (
<div className={'flex flex-col h-full'}>
<div className={classNames('flex items-center justify-between', fullScreen ? 'py-3 pr-4 pl-6 border border-divider-subtle' : 'pt-3 pr-3 pl-4')}>
<div className='flex flex-col'>
<div className='text-text-primary system-xl-semibold'>{
docForm === 'qa_model'
? t('datasetDocuments.segment.newQaSegment')
: t('datasetDocuments.segment.addChunk')
t('datasetDocuments.segment.addChunk')
}</div>
<div className='flex items-center gap-x-2'>
<SegmentIndexTag label={'New Chunk'} />