Merge branch 'feat/parent-child-retrieval' of https://github.com/langgenius/dify into feat/parent-child-retrieval

This commit is contained in:
twwu 2024-12-16 17:10:38 +08:00
commit 14f14420e7
7 changed files with 31 additions and 132 deletions

View File

@ -17,15 +17,18 @@ import Tag from '@/app/components/datasets/documents/detail/completed/common/tag
const i18nPrefix = 'datasetHitTesting'
type Props = {
isExternal: boolean
payload: HitTesting
}
const ResultItem: FC<Props> = ({
isExternal,
payload,
}) => {
const { t } = useTranslation()
const { segment, score, child_chunks } = payload
const { position, word_count, content, keywords, document } = segment
const { segment, content: externalContent, score, child_chunks } = payload
const data = isExternal ? externalContent : segment
const { position, word_count, content, keywords, document } = data
const isParentChildRetrieval = !!(child_chunks && child_chunks.length > 0)
const extension = document.name.split('.').slice(-1)[0] as FileAppearanceTypeEnum
const [isFold, {

View File

@ -1,68 +0,0 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { SegmentIndexTag } from '../documents/detail/completed/common/segment-index-tag'
import s from '../documents/detail/completed/style.module.css'
import cn from '@/utils/classnames'
import type { SegmentDetailModel } from '@/models/datasets'
import Divider from '@/app/components/base/divider'
type IHitDetailProps = {
segInfo?: Partial<SegmentDetailModel> & { id: string }
}
const HitDetail: FC<IHitDetailProps> = ({ segInfo }) => {
const { t } = useTranslation()
const renderContent = () => {
if (segInfo?.answer) {
return (
<>
<div className='mt-2 mb-1 text-xs font-medium text-gray-500'>QUESTION</div>
<div className='mb-4 text-md text-gray-800'>{segInfo.content}</div>
<div className='mb-1 text-xs font-medium text-gray-500'>ANSWER</div>
<div className='text-md text-gray-800'>{segInfo.answer}</div>
</>
)
}
return <div className='mb-4 text-md text-gray-800 h-full'>{segInfo?.content}</div>
}
return (
segInfo?.id === 'external'
? <div className='w-full overflow-x-auto px-2'>
<div className={s.segModalContent}>{renderContent()}</div>
</div>
: <div className='overflow-x-auto'>
<div className="flex items-center">
<SegmentIndexTag
positionId={segInfo?.position || ''}
className="w-fit mr-6"
/>
<div className={cn(s.commonIcon, s.typeSquareIcon)} />
<span className={cn('mr-6', s.numberInfo)}>
{segInfo?.word_count} {t('datasetDocuments.segment.characters', { count: segInfo?.word_count || 0 })}
</span>
<div className={cn(s.commonIcon, s.targetIcon)} />
<span className={s.numberInfo}>
{segInfo?.hit_count} {t('datasetDocuments.segment.hitCount')}
</span>
</div>
<Divider />
<div className={s.segModalContent}>{renderContent()}</div>
<div className={s.keywordTitle}>
{t('datasetDocuments.segment.keywords')}
</div>
<div className={s.keywordWrapper}>
{!segInfo?.keywords?.length
? '-'
: segInfo?.keywords?.map((word, index) => {
return <div key={index} className={s.keyword}>{word}</div>
})}
</div>
</div>
)
}
export default HitDetail

View File

@ -9,13 +9,11 @@ import { useContext } from 'use-context-selector'
import SegmentCard from '../documents/detail/completed/SegmentCard'
import Textarea from './textarea'
import s from './style.module.css'
import HitDetail from './hit-detail'
import ModifyRetrievalModal from './modify-retrieval-modal'
import ResultItem from './components/result-item'
import cn from '@/utils/classnames'
import type { ExternalKnowledgeBaseHitTestingResponse, ExternalKnowledgeBaseHitTesting as ExternalKnowledgeBaseHitTestingType, HitTestingResponse, HitTesting as HitTestingType } from '@/models/datasets'
import type { ExternalKnowledgeBaseHitTestingResponse, HitTestingResponse } from '@/models/datasets'
import Loading from '@/app/components/base/loading'
import Modal from '@/app/components/base/modal'
import Drawer from '@/app/components/base/drawer'
import Pagination from '@/app/components/base/pagination'
import FloatRightContainer from '@/app/components/base/float-right-container'
@ -50,11 +48,8 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
const isMobile = media === MediaType.mobile
const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>() // 初始化记录为空数组
// console.log(hitResult?.records)
const [externalHitResult, setExternalHitResult] = useState<ExternalKnowledgeBaseHitTestingResponse | undefined>()
const [submitLoading, setSubmitLoading] = useState(false)
const [currParagraph, setCurrParagraph] = useState<{ paraInfo?: HitTestingType; showModal: boolean }>({ showModal: false })
const [externalCurrParagraph, setExternalCurrParagraph] = useState<{ paraInfo?: ExternalKnowledgeBaseHitTestingType; showModal: boolean }>({ showModal: false })
const [text, setText] = useState('')
const [currPage, setCurrPage] = React.useState<number>(0)
@ -82,6 +77,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
<ResultItem
key={idx}
payload={record}
isExternal={isExternal}
/>
))}
</div>
@ -103,10 +99,10 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
return (
<div className={s.container}>
<div className={s.leftDiv}>
<div className={s.titleWrapper}>
<h1 className={s.title}>{t('datasetHitTesting.title')}</h1>
<p className={s.desc}>{t('datasetHitTesting.desc')}</p>
<div className='px-6 py-3 flex flex-col'>
<div className='flex flex-col justify-center mb-4'>
<h1 className='text-base font-semibold text-text-primary'>{t('datasetHitTesting.title')}</h1>
<p className='mt-0.5 text-[13px] leading-4 font-normal text-text-tertiary'>{t('datasetHitTesting.desc')}</p>
</div>
<Textarea
datasetId={datasetId}
@ -123,7 +119,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
retrievalConfig={retrievalConfig}
isEconomy={currentDataset?.indexing_technique === 'economy'}
/>
<div className={cn(s.title, 'mt-8 mb-2')}>{t('datasetHitTesting.recents')}</div>
<div className='text-xl font-medium text-gray-900 mt-8 mb-2'>{t('datasetHitTesting.records')}</div>
{(!recordsRes && !error)
? (
<div className='flex-1'><Loading type='app' /></div>
@ -132,7 +128,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
? (
<>
<div className='grow overflow-y-auto'>
<table className={`w-full border-collapse border-0 mt-3 ${s.table}`}>
<table className={'w-full border-collapse border-0 mt-3 text-[13px] text-gray-500'}>
<thead className="sticky top-0 h-8 bg-white leading-8 border-b border-gray-200 text-gray-500 font-bold">
<tr>
<td className='w-28'>{t('datasetHitTesting.table.header.source')}</td>
@ -172,7 +168,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
)}
</div>
<FloatRightContainer panelClassname='!justify-start !overflow-y-auto' showClose isMobile={isMobile} isOpen={isShowRightPanel} onClose={hideRightPanel} footer={null}>
<div className={cn(s.rightDiv, 'pt-3')}>
<div className='flex flex-col pt-3'>
{/* {renderHitResults(generalResultData)} */}
{submitLoading
? <SegmentCard
@ -194,29 +190,6 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
}
</div>
</FloatRightContainer>
<Modal
className={isExternal ? 'py-10 px-8' : 'w-full'}
closable
onClose={() => {
setCurrParagraph({ showModal: false })
setExternalCurrParagraph({ showModal: false })
}}
isShow={currParagraph.showModal || externalCurrParagraph.showModal}
>
{currParagraph.showModal && (
<HitDetail
segInfo={currParagraph.paraInfo?.segment}
/>
)}
{externalCurrParagraph.showModal && (
<HitDetail
segInfo={{
id: 'external',
content: externalCurrParagraph.paraInfo?.content,
}}
/>
)}
</Modal>
<Drawer isOpen={isShowModifyRetrievalModal} onClose={() => setIsShowModifyRetrievalModal(false)} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
<ModifyRetrievalModal
indexMethod={currentDataset?.indexing_technique || ''}

View File

@ -1,42 +1,30 @@
.container {
@apply flex h-full w-full relative overflow-y-auto;
}
.container > div {
.container>div {
@apply flex-1 h-full;
}
.leftDiv {
@apply px-6 py-3 flex flex-col;
}
.rightDiv {
@apply flex flex-col;
}
.titleWrapper {
@apply flex flex-col justify-center gap-1 mb-5;
}
.title {
@apply text-xl font-medium text-gray-900;
}
.desc {
@apply text-sm font-normal text-gray-500;
}
.table {
@apply text-[13px] text-gray-500;
}
.table td {
@apply whitespace-nowrap overflow-hidden text-ellipsis;
}
.commonIcon {
@apply w-3.5 h-3.5 inline-block align-middle;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
}
.app_icon {
background-image: url(./assets/grid.svg);
}
.hit_testing_icon {
background-image: url(../documents/assets/target.svg);
}
.plugin_icon {
background-image: url(./assets/plugin.svg);
}
@ -51,11 +39,13 @@
grid-gap: 16px;
grid-auto-rows: 216px;
}
.clockWrapper {
border: 0.5px solid #eaecf5;
@apply rounded-lg w-11 h-11 flex justify-center items-center;
}
.clockIcon {
mask-image: url(./assets/clock.svg);
@apply bg-gray-500;
}
}

View File

@ -1,9 +1,9 @@
const translation = {
title: 'Retrieval Testing',
title: 'Retrieval Test',
settingTitle: 'Retrieval Setting',
desc: 'Test the hitting effect of the Knowledge based on the given query text',
desc: 'Test the hitting effect of the Knowledge based on the given query text.',
dateTimeFormat: 'MM/DD/YYYY hh:mm A',
recents: 'Recents',
records: 'Records',
table: {
header: {
source: 'Source',
@ -16,7 +16,7 @@ const translation = {
placeholder: 'Please enter a text, a short declarative sentence is recommended.',
countWarning: 'Up to 200 characters.',
indexWarning: 'High quality Knowledge only.',
testing: 'Testing',
testing: 'Test',
},
hit: {
title: '{{num}} Retrieved Chunks',

View File

@ -1,9 +1,9 @@
const translation = {
title: '召回测试',
settingTitle: '召回设置',
desc: '基于给定的查询文本测试知识库的召回效果',
desc: '根据给定的查询文本测试知识的召回效果。',
dateTimeFormat: 'YYYY-MM-DD HH:mm',
recents: '最近查询',
records: '记录',
table: {
header: {
source: '数据源',

View File

@ -487,6 +487,7 @@ export type HitTestingChildChunk = {
}
export type HitTesting = {
segment: Segment
content: Segment
score: number
tsne_position: TsnePosition
child_chunks?: HitTestingChildChunk[] | null