mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 12:37:20 +08:00
Merge branch 'feat/parent-child-retrieval' of https://github.com/langgenius/dify into feat/parent-child-retrieval
This commit is contained in:
commit
14f14420e7
@ -17,15 +17,18 @@ import Tag from '@/app/components/datasets/documents/detail/completed/common/tag
|
|||||||
|
|
||||||
const i18nPrefix = 'datasetHitTesting'
|
const i18nPrefix = 'datasetHitTesting'
|
||||||
type Props = {
|
type Props = {
|
||||||
|
isExternal: boolean
|
||||||
payload: HitTesting
|
payload: HitTesting
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResultItem: FC<Props> = ({
|
const ResultItem: FC<Props> = ({
|
||||||
|
isExternal,
|
||||||
payload,
|
payload,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { segment, score, child_chunks } = payload
|
const { segment, content: externalContent, score, child_chunks } = payload
|
||||||
const { position, word_count, content, keywords, document } = segment
|
const data = isExternal ? externalContent : segment
|
||||||
|
const { position, word_count, content, keywords, document } = data
|
||||||
const isParentChildRetrieval = !!(child_chunks && child_chunks.length > 0)
|
const isParentChildRetrieval = !!(child_chunks && child_chunks.length > 0)
|
||||||
const extension = document.name.split('.').slice(-1)[0] as FileAppearanceTypeEnum
|
const extension = document.name.split('.').slice(-1)[0] as FileAppearanceTypeEnum
|
||||||
const [isFold, {
|
const [isFold, {
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -9,13 +9,11 @@ import { useContext } from 'use-context-selector'
|
|||||||
import SegmentCard from '../documents/detail/completed/SegmentCard'
|
import SegmentCard from '../documents/detail/completed/SegmentCard'
|
||||||
import Textarea from './textarea'
|
import Textarea from './textarea'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
import HitDetail from './hit-detail'
|
|
||||||
import ModifyRetrievalModal from './modify-retrieval-modal'
|
import ModifyRetrievalModal from './modify-retrieval-modal'
|
||||||
import ResultItem from './components/result-item'
|
import ResultItem from './components/result-item'
|
||||||
import cn from '@/utils/classnames'
|
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 Loading from '@/app/components/base/loading'
|
||||||
import Modal from '@/app/components/base/modal'
|
|
||||||
import Drawer from '@/app/components/base/drawer'
|
import Drawer from '@/app/components/base/drawer'
|
||||||
import Pagination from '@/app/components/base/pagination'
|
import Pagination from '@/app/components/base/pagination'
|
||||||
import FloatRightContainer from '@/app/components/base/float-right-container'
|
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 isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>() // 初始化记录为空数组
|
const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>() // 初始化记录为空数组
|
||||||
// console.log(hitResult?.records)
|
|
||||||
const [externalHitResult, setExternalHitResult] = useState<ExternalKnowledgeBaseHitTestingResponse | undefined>()
|
const [externalHitResult, setExternalHitResult] = useState<ExternalKnowledgeBaseHitTestingResponse | undefined>()
|
||||||
const [submitLoading, setSubmitLoading] = useState(false)
|
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 [text, setText] = useState('')
|
||||||
|
|
||||||
const [currPage, setCurrPage] = React.useState<number>(0)
|
const [currPage, setCurrPage] = React.useState<number>(0)
|
||||||
@ -82,6 +77,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
<ResultItem
|
<ResultItem
|
||||||
key={idx}
|
key={idx}
|
||||||
payload={record}
|
payload={record}
|
||||||
|
isExternal={isExternal}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -103,10 +99,10 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={s.container}>
|
<div className={s.container}>
|
||||||
<div className={s.leftDiv}>
|
<div className='px-6 py-3 flex flex-col'>
|
||||||
<div className={s.titleWrapper}>
|
<div className='flex flex-col justify-center mb-4'>
|
||||||
<h1 className={s.title}>{t('datasetHitTesting.title')}</h1>
|
<h1 className='text-base font-semibold text-text-primary'>{t('datasetHitTesting.title')}</h1>
|
||||||
<p className={s.desc}>{t('datasetHitTesting.desc')}</p>
|
<p className='mt-0.5 text-[13px] leading-4 font-normal text-text-tertiary'>{t('datasetHitTesting.desc')}</p>
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
datasetId={datasetId}
|
datasetId={datasetId}
|
||||||
@ -123,7 +119,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
retrievalConfig={retrievalConfig}
|
retrievalConfig={retrievalConfig}
|
||||||
isEconomy={currentDataset?.indexing_technique === 'economy'}
|
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)
|
{(!recordsRes && !error)
|
||||||
? (
|
? (
|
||||||
<div className='flex-1'><Loading type='app' /></div>
|
<div className='flex-1'><Loading type='app' /></div>
|
||||||
@ -132,7 +128,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
? (
|
? (
|
||||||
<>
|
<>
|
||||||
<div className='grow overflow-y-auto'>
|
<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">
|
<thead className="sticky top-0 h-8 bg-white leading-8 border-b border-gray-200 text-gray-500 font-bold">
|
||||||
<tr>
|
<tr>
|
||||||
<td className='w-28'>{t('datasetHitTesting.table.header.source')}</td>
|
<td className='w-28'>{t('datasetHitTesting.table.header.source')}</td>
|
||||||
@ -172,7 +168,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<FloatRightContainer panelClassname='!justify-start !overflow-y-auto' showClose isMobile={isMobile} isOpen={isShowRightPanel} onClose={hideRightPanel} footer={null}>
|
<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)} */}
|
{/* {renderHitResults(generalResultData)} */}
|
||||||
{submitLoading
|
{submitLoading
|
||||||
? <SegmentCard
|
? <SegmentCard
|
||||||
@ -194,29 +190,6 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</FloatRightContainer>
|
</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'>
|
<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
|
<ModifyRetrievalModal
|
||||||
indexMethod={currentDataset?.indexing_technique || ''}
|
indexMethod={currentDataset?.indexing_technique || ''}
|
||||||
|
|||||||
@ -1,42 +1,30 @@
|
|||||||
.container {
|
.container {
|
||||||
@apply flex h-full w-full relative overflow-y-auto;
|
@apply flex h-full w-full relative overflow-y-auto;
|
||||||
}
|
}
|
||||||
.container > div {
|
|
||||||
|
.container>div {
|
||||||
@apply flex-1 h-full;
|
@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 {
|
.table td {
|
||||||
@apply whitespace-nowrap overflow-hidden text-ellipsis;
|
@apply whitespace-nowrap overflow-hidden text-ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commonIcon {
|
.commonIcon {
|
||||||
@apply w-3.5 h-3.5 inline-block align-middle;
|
@apply w-3.5 h-3.5 inline-block align-middle;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app_icon {
|
.app_icon {
|
||||||
background-image: url(./assets/grid.svg);
|
background-image: url(./assets/grid.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hit_testing_icon {
|
.hit_testing_icon {
|
||||||
background-image: url(../documents/assets/target.svg);
|
background-image: url(../documents/assets/target.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.plugin_icon {
|
.plugin_icon {
|
||||||
background-image: url(./assets/plugin.svg);
|
background-image: url(./assets/plugin.svg);
|
||||||
}
|
}
|
||||||
@ -51,11 +39,13 @@
|
|||||||
grid-gap: 16px;
|
grid-gap: 16px;
|
||||||
grid-auto-rows: 216px;
|
grid-auto-rows: 216px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clockWrapper {
|
.clockWrapper {
|
||||||
border: 0.5px solid #eaecf5;
|
border: 0.5px solid #eaecf5;
|
||||||
@apply rounded-lg w-11 h-11 flex justify-center items-center;
|
@apply rounded-lg w-11 h-11 flex justify-center items-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clockIcon {
|
.clockIcon {
|
||||||
mask-image: url(./assets/clock.svg);
|
mask-image: url(./assets/clock.svg);
|
||||||
@apply bg-gray-500;
|
@apply bg-gray-500;
|
||||||
}
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
const translation = {
|
const translation = {
|
||||||
title: 'Retrieval Testing',
|
title: 'Retrieval Test',
|
||||||
settingTitle: 'Retrieval Setting',
|
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',
|
dateTimeFormat: 'MM/DD/YYYY hh:mm A',
|
||||||
recents: 'Recents',
|
records: 'Records',
|
||||||
table: {
|
table: {
|
||||||
header: {
|
header: {
|
||||||
source: 'Source',
|
source: 'Source',
|
||||||
@ -16,7 +16,7 @@ const translation = {
|
|||||||
placeholder: 'Please enter a text, a short declarative sentence is recommended.',
|
placeholder: 'Please enter a text, a short declarative sentence is recommended.',
|
||||||
countWarning: 'Up to 200 characters.',
|
countWarning: 'Up to 200 characters.',
|
||||||
indexWarning: 'High quality Knowledge only.',
|
indexWarning: 'High quality Knowledge only.',
|
||||||
testing: 'Testing',
|
testing: 'Test',
|
||||||
},
|
},
|
||||||
hit: {
|
hit: {
|
||||||
title: '{{num}} Retrieved Chunks',
|
title: '{{num}} Retrieved Chunks',
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
const translation = {
|
const translation = {
|
||||||
title: '召回测试',
|
title: '召回测试',
|
||||||
settingTitle: '召回设置',
|
settingTitle: '召回设置',
|
||||||
desc: '基于给定的查询文本测试知识库的召回效果',
|
desc: '根据给定的查询文本测试知识的召回效果。',
|
||||||
dateTimeFormat: 'YYYY-MM-DD HH:mm',
|
dateTimeFormat: 'YYYY-MM-DD HH:mm',
|
||||||
recents: '最近查询',
|
records: '记录',
|
||||||
table: {
|
table: {
|
||||||
header: {
|
header: {
|
||||||
source: '数据源',
|
source: '数据源',
|
||||||
|
|||||||
@ -487,6 +487,7 @@ export type HitTestingChildChunk = {
|
|||||||
}
|
}
|
||||||
export type HitTesting = {
|
export type HitTesting = {
|
||||||
segment: Segment
|
segment: Segment
|
||||||
|
content: Segment
|
||||||
score: number
|
score: number
|
||||||
tsne_position: TsnePosition
|
tsne_position: TsnePosition
|
||||||
child_chunks?: HitTestingChildChunk[] | null
|
child_chunks?: HitTestingChildChunk[] | null
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user