mirror of https://github.com/langgenius/dify.git
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'
|
||||
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, {
|
||||
|
|
|
|||
|
|
@ -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 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 || ''}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
const translation = {
|
||||
title: '召回测试',
|
||||
settingTitle: '召回设置',
|
||||
desc: '基于给定的查询文本测试知识库的召回效果',
|
||||
desc: '根据给定的查询文本测试知识的召回效果。',
|
||||
dateTimeFormat: 'YYYY-MM-DD HH:mm',
|
||||
recents: '最近查询',
|
||||
records: '记录',
|
||||
table: {
|
||||
header: {
|
||||
source: '数据源',
|
||||
|
|
|
|||
|
|
@ -487,6 +487,7 @@ export type HitTestingChildChunk = {
|
|||
}
|
||||
export type HitTesting = {
|
||||
segment: Segment
|
||||
content: Segment
|
||||
score: number
|
||||
tsne_position: TsnePosition
|
||||
child_chunks?: HitTestingChildChunk[] | null
|
||||
|
|
|
|||
Loading…
Reference in New Issue