mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-13 10:59:03 +08:00
Co-authored-by: zhuxinliang <zhuxinliang@didiglobal.com>
This commit is contained in:
parent
9903f1e703
commit
0c6a8a130e
@ -0,0 +1,60 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useBoolean } from 'ahooks'
|
||||||
|
import ResultItemMeta from './result-item-meta'
|
||||||
|
import ResultItemFooter from './result-item-footer'
|
||||||
|
import type { ExternalKnowledgeBaseHitTesting } from '@/models/datasets'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import Modal from '@/app/components/base/modal'
|
||||||
|
import { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
|
||||||
|
|
||||||
|
const i18nPrefix = 'datasetHitTesting'
|
||||||
|
type Props = {
|
||||||
|
payload: ExternalKnowledgeBaseHitTesting
|
||||||
|
positionId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResultItemExternal: FC<Props> = ({ payload, positionId }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { content, title, score } = payload
|
||||||
|
const [
|
||||||
|
isShowDetailModal,
|
||||||
|
{ setTrue: showDetailModal, setFalse: hideDetailModal },
|
||||||
|
] = useBoolean(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('pt-3 bg-chat-bubble-bg rounded-xl hover:shadow-lg cursor-pointer')} onClick={showDetailModal}>
|
||||||
|
{/* Meta info */}
|
||||||
|
<ResultItemMeta className='px-3' labelPrefix={'Chunk'} positionId={positionId} wordCount={content.length} score={score} />
|
||||||
|
|
||||||
|
{/* Main */}
|
||||||
|
<div className='mt-1 px-3'>
|
||||||
|
<div className='line-clamp-2 body-md-regular break-all'>{content}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Foot */}
|
||||||
|
<ResultItemFooter docType={FileAppearanceTypeEnum.custom} docTitle={title} showDetailModal={showDetailModal} />
|
||||||
|
|
||||||
|
{isShowDetailModal && (
|
||||||
|
<Modal
|
||||||
|
title={t(`${i18nPrefix}.chunkDetail`)}
|
||||||
|
className={'!min-w-[800px]'}
|
||||||
|
closable
|
||||||
|
onClose={hideDetailModal}
|
||||||
|
isShow={isShowDetailModal}
|
||||||
|
>
|
||||||
|
<div className='mt-4 flex-1'>
|
||||||
|
<ResultItemMeta labelPrefix={'Chunk'} positionId={positionId} wordCount={content.length} score={score} />
|
||||||
|
<div className={cn('mt-2 body-md-regular text-text-secondary break-all', 'h-[min(539px,_80vh)] overflow-y-auto')}>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(ResultItemExternal)
|
@ -0,0 +1,42 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiArrowRightUpLine } from '@remixicon/react'
|
||||||
|
import FileIcon from '@/app/components/base/file-uploader/file-type-icon'
|
||||||
|
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
docType: FileAppearanceTypeEnum
|
||||||
|
docTitle: string
|
||||||
|
showDetailModal: () => void
|
||||||
|
}
|
||||||
|
const i18nPrefix = 'datasetHitTesting'
|
||||||
|
|
||||||
|
const ResultItemFooter: FC<Props> = ({
|
||||||
|
docType,
|
||||||
|
docTitle,
|
||||||
|
showDetailModal,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-3 flex justify-between items-center h-10 pl-3 pr-2 border-t border-divider-subtle">
|
||||||
|
<div className="grow flex items-center space-x-1">
|
||||||
|
<FileIcon type={docType} size="sm" />
|
||||||
|
<span className="grow w-0 truncate text-text-secondary text-[13px] font-normal">
|
||||||
|
{docTitle}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="flex items-center space-x-1 cursor-pointer text-text-tertiary"
|
||||||
|
onClick={showDetailModal}
|
||||||
|
>
|
||||||
|
<div className="text-xs uppercase">{t(`${i18nPrefix}.open`)}</div>
|
||||||
|
<RiArrowRightUpLine className="size-3.5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(ResultItemFooter)
|
@ -0,0 +1,45 @@
|
|||||||
|
'use client'
|
||||||
|
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 Dot from '../../documents/detail/completed/common/dot'
|
||||||
|
import Score from './score'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
labelPrefix: string
|
||||||
|
positionId: number
|
||||||
|
wordCount: number
|
||||||
|
score: number
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResultItemMeta: FC<Props> = ({
|
||||||
|
labelPrefix,
|
||||||
|
positionId,
|
||||||
|
wordCount,
|
||||||
|
score,
|
||||||
|
className,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('flex justify-between items-center', className)}>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<SegmentIndexTag
|
||||||
|
labelPrefix={labelPrefix}
|
||||||
|
positionId={positionId}
|
||||||
|
className={cn('w-fit group-hover:opacity-100')}
|
||||||
|
/>
|
||||||
|
<Dot />
|
||||||
|
<div className="system-xs-medium text-text-tertiary">
|
||||||
|
{wordCount} {t('datasetDocuments.segment.characters', { count: wordCount })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Score value={score} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(ResultItemMeta)
|
@ -2,33 +2,29 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { RiArrowDownSLine, RiArrowRightSLine, RiArrowRightUpLine } from '@remixicon/react'
|
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import { SegmentIndexTag } from '../../documents/detail/completed/common/segment-index-tag'
|
|
||||||
import Dot from '../../documents/detail/completed/common/dot'
|
|
||||||
import Score from './score'
|
|
||||||
import ChildChunkItem from './child-chunks-item'
|
import ChildChunkItem from './child-chunks-item'
|
||||||
import ChunkDetailModal from './chunk-detail-modal'
|
import ChunkDetailModal from './chunk-detail-modal'
|
||||||
|
import ResultItemMeta from './result-item-meta'
|
||||||
|
import ResultItemFooter from './result-item-footer'
|
||||||
import type { HitTesting } from '@/models/datasets'
|
import type { HitTesting } from '@/models/datasets'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import FileIcon from '@/app/components/base/file-uploader/file-type-icon'
|
|
||||||
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
|
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
|
||||||
import Tag from '@/app/components/datasets/documents/detail/completed/common/tag'
|
import Tag from '@/app/components/datasets/documents/detail/completed/common/tag'
|
||||||
import { extensionToFileType } from '@/app/components/datasets/hit-testing/utils/extension-to-file-type'
|
import { extensionToFileType } from '@/app/components/datasets/hit-testing/utils/extension-to-file-type'
|
||||||
|
|
||||||
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, content: externalContent, score, child_chunks } = payload
|
const { segment, score, child_chunks } = payload
|
||||||
const data = isExternal ? externalContent : segment
|
const data = segment
|
||||||
const { position, word_count, content, keywords, document } = data
|
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
|
||||||
@ -46,18 +42,7 @@ const ResultItem: FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<div className={cn('pt-3 bg-chat-bubble-bg rounded-xl hover:shadow-lg cursor-pointer')} onClick={showDetailModal}>
|
<div className={cn('pt-3 bg-chat-bubble-bg rounded-xl hover:shadow-lg cursor-pointer')} onClick={showDetailModal}>
|
||||||
{/* Meta info */}
|
{/* Meta info */}
|
||||||
<div className='flex justify-between items-center px-3'>
|
<ResultItemMeta className='px-3' labelPrefix={`${isParentChildRetrieval ? 'Parent-' : ''}Chunk`} positionId={position} wordCount={word_count} score={score} />
|
||||||
<div className='flex items-center space-x-2'>
|
|
||||||
<SegmentIndexTag
|
|
||||||
labelPrefix={`${isParentChildRetrieval ? 'Parent-' : ''}Chunk`}
|
|
||||||
positionId={position}
|
|
||||||
className={cn('w-fit group-hover:opacity-100')}
|
|
||||||
/>
|
|
||||||
<Dot />
|
|
||||||
<div className='system-xs-medium text-text-tertiary'>{word_count} {t('datasetDocuments.segment.characters', { count: word_count })}</div>
|
|
||||||
</div>
|
|
||||||
<Score value={score} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main */}
|
{/* Main */}
|
||||||
<div className='mt-1 px-3'>
|
<div className='mt-1 px-3'>
|
||||||
@ -88,19 +73,7 @@ const ResultItem: FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Foot */}
|
{/* Foot */}
|
||||||
<div className='mt-3 flex justify-between items-center h-10 pl-3 pr-2 border-t border-divider-subtle'>
|
<ResultItemFooter docType={fileType} docTitle={document.name} showDetailModal={showDetailModal} />
|
||||||
<div className='grow flex items-center space-x-1'>
|
|
||||||
<FileIcon type={fileType} size='sm' />
|
|
||||||
<span className='grow w-0 truncate text-text-secondary text-[13px] font-normal'>{document.name}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='flex items-center space-x-1 cursor-pointer text-text-tertiary'
|
|
||||||
onClick={showDetailModal}
|
|
||||||
>
|
|
||||||
<div className='text-xs uppercase'>{t(`${i18nPrefix}.open`)}</div>
|
|
||||||
<RiArrowRightUpLine className='size-3.5' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
isShowDetailModal && (
|
isShowDetailModal && (
|
||||||
|
@ -12,8 +12,9 @@ import Textarea from './textarea'
|
|||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
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 ResultItemExternal from './components/result-item-external'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import type { ExternalKnowledgeBaseHitTestingResponse, HitTestingResponse } from '@/models/datasets'
|
import type { ExternalKnowledgeBaseHitTesting, ExternalKnowledgeBaseHitTestingResponse, HitTesting, HitTestingResponse } from '@/models/datasets'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
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'
|
||||||
@ -41,7 +42,7 @@ const RecordsEmpty: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { formatTime } = useTimestamp()
|
const { formatTime } = useTimestamp()
|
||||||
|
|
||||||
@ -68,19 +69,25 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
|
const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
|
||||||
const [isShowModifyRetrievalModal, setIsShowModifyRetrievalModal] = useState(false)
|
const [isShowModifyRetrievalModal, setIsShowModifyRetrievalModal] = useState(false)
|
||||||
const [isShowRightPanel, { setTrue: showRightPanel, setFalse: hideRightPanel, set: setShowRightPanel }] = useBoolean(!isMobile)
|
const [isShowRightPanel, { setTrue: showRightPanel, setFalse: hideRightPanel, set: setShowRightPanel }] = useBoolean(!isMobile)
|
||||||
const renderHitResults = (results: any[]) => (
|
const renderHitResults = (results: HitTesting[] | ExternalKnowledgeBaseHitTesting[]) => (
|
||||||
<div className='h-full flex flex-col py-3 px-4 rounded-t-2xl bg-background-body'>
|
<div className='h-full flex flex-col py-3 px-4 rounded-t-2xl bg-background-body'>
|
||||||
<div className='shrink-0 pl-2 text-text-primary font-semibold leading-6 mb-2'>
|
<div className='shrink-0 pl-2 text-text-primary font-semibold leading-6 mb-2'>
|
||||||
{t('datasetHitTesting.hit.title', { num: results.length })}
|
{t('datasetHitTesting.hit.title', { num: results.length })}
|
||||||
</div>
|
</div>
|
||||||
<div className='grow overflow-y-auto space-y-2'>
|
<div className='grow overflow-y-auto space-y-2'>
|
||||||
{results.map((record, idx) => (
|
{results.map((record, idx) =>
|
||||||
<ResultItem
|
isExternal
|
||||||
key={idx}
|
? (
|
||||||
payload={record}
|
<ResultItemExternal
|
||||||
isExternal={isExternal}
|
key={idx}
|
||||||
/>
|
positionId={idx + 1}
|
||||||
))}
|
payload={record as ExternalKnowledgeBaseHitTesting}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<ResultItem key={idx} payload={record as HitTesting} />
|
||||||
|
),
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -130,7 +137,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 text-[13px] leading-4 text-text-secondary '}>
|
<table className={'w-full border-collapse border-0 text-[13px] leading-4 text-text-secondary '}>
|
||||||
<thead className="sticky top-0 h-7 leading-7 text-xs text-text-tertiary font-medium uppercase">
|
<thead className='sticky top-0 h-7 leading-7 text-xs text-text-tertiary font-medium uppercase'>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='pl-3 w-[128px] rounded-l-lg bg-background-section-burn'>{t('datasetHitTesting.table.header.source')}</td>
|
<td className='pl-3 w-[128px] rounded-l-lg bg-background-section-burn'>{t('datasetHitTesting.table.header.source')}</td>
|
||||||
<td className='bg-background-section-burn'>{t('datasetHitTesting.table.header.text')}</td>
|
<td className='bg-background-section-burn'>{t('datasetHitTesting.table.header.text')}</td>
|
||||||
@ -208,4 +215,4 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HitTesting
|
export default HitTestingPage
|
||||||
|
@ -84,6 +84,7 @@ const TextAreaWithButton = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const externalRetrievalTestingOnSubmit = async () => {
|
const externalRetrievalTestingOnSubmit = async () => {
|
||||||
|
setLoading(true)
|
||||||
const [e, res] = await asyncRunSafe<ExternalKnowledgeBaseHitTestingResponse>(
|
const [e, res] = await asyncRunSafe<ExternalKnowledgeBaseHitTestingResponse>(
|
||||||
externalKnowledgeBaseHitTesting({
|
externalKnowledgeBaseHitTesting({
|
||||||
datasetId,
|
datasetId,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user