chore(web): new lint setup (#30020)

Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
Stephen Zhou
2025-12-23 16:58:55 +08:00
committed by GitHub
parent 9701a2994b
commit f2842da397
3356 changed files with 85046 additions and 81278 deletions

View File

@ -1,9 +1,9 @@
'use client'
import type { FC } from 'react'
import type { HitTestingChildChunk } from '@/models/datasets'
import React from 'react'
import { SliceContent } from '../../formatted-text/flavours/shared'
import Score from './score'
import type { HitTestingChildChunk } from '@/models/datasets'
type Props = {
payload: HitTestingChildChunk
@ -19,11 +19,14 @@ const ChildChunks: FC<Props> = ({
<div
className={!isShowAll ? 'line-clamp-2 break-all' : ''}
>
<div className='relative top-[-2px] inline-flex items-center'>
<div className='system-2xs-semibold-uppercase flex h-[20.5px] items-center bg-state-accent-solid px-1 text-text-primary-on-surface'>C-{position}</div>
<div className="relative top-[-2px] inline-flex items-center">
<div className="system-2xs-semibold-uppercase flex h-[20.5px] items-center bg-state-accent-solid px-1 text-text-primary-on-surface">
C-
{position}
</div>
<Score value={score} besideChunkName />
</div>
<SliceContent className='bg-state-accent-hover py-0.5 text-sm font-normal text-text-secondary group-hover:bg-state-accent-hover'>{content}</SliceContent>
<SliceContent className="bg-state-accent-hover py-0.5 text-sm font-normal text-text-secondary group-hover:bg-state-accent-hover">{content}</SliceContent>
</div>
)
}

View File

@ -1,19 +1,19 @@
'use client'
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
import type { HitTesting } from '@/models/datasets'
import React, { useMemo } 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 ChildChunksItem from './child-chunks-item'
import Modal from '@/app/components/base/modal'
import type { HitTesting } from '@/models/datasets'
import FileIcon from '@/app/components/base/file-uploader/file-type-icon'
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
import { cn } from '@/utils/classnames'
import Tag from '@/app/components/datasets/documents/detail/completed/common/tag'
import { Markdown } from '@/app/components/base/markdown'
import Modal from '@/app/components/base/modal'
import Tag from '@/app/components/datasets/documents/detail/completed/common/tag'
import { cn } from '@/utils/classnames'
import ImageList from '../../common/image-list'
import Dot from '../../documents/detail/completed/common/dot'
import { SegmentIndexTag } from '../../documents/detail/completed/common/segment-index-tag'
import ChildChunksItem from './child-chunks-item'
import Mask from './mask'
import Score from './score'
const i18nPrefix = 'datasetHitTesting'
@ -35,7 +35,8 @@ const ChunkDetailModal = ({
const labelPrefix = isParentChildRetrieval ? t('datasetDocuments.segment.parentChunk') : t('datasetDocuments.segment.chunk')
const images = useMemo(() => {
if (!files) return []
if (!files)
return []
return files.map(file => ({
name: file.name,
mimeType: file.mime_type,
@ -56,26 +57,26 @@ const ChunkDetailModal = ({
onClose={onHide}
className={cn(isParentChildRetrieval ? '!min-w-[1200px]' : '!min-w-[800px]')}
>
<div className='mt-4 flex'>
<div className="mt-4 flex">
<div className={cn('flex-1', isParentChildRetrieval && 'pr-6')}>
{/* Meta info */}
<div className='flex items-center justify-between'>
<div className='flex grow items-center space-x-2'>
<div className="flex items-center justify-between">
<div className="flex grow items-center space-x-2">
<SegmentIndexTag
labelPrefix={labelPrefix}
positionId={position}
className={cn('w-fit group-hover:opacity-100')}
/>
<Dot />
<div className='flex grow items-center space-x-1'>
<FileIcon type={extension} size='sm' />
<span className='w-0 grow truncate text-[13px] font-normal text-text-secondary'>{document.name}</span>
<div className="flex grow items-center space-x-1">
<FileIcon type={extension} size="sm" />
<span className="w-0 grow truncate text-[13px] font-normal text-text-secondary">{document.name}</span>
</div>
</div>
<Score value={score} />
</div>
{/* Content */}
<div className='relative'>
<div className="relative">
{!answer && (
<Markdown
className={cn('!mt-2 !text-text-secondary', heighClassName)}
@ -84,15 +85,15 @@ const ChunkDetailModal = ({
/>
)}
{answer && (
<div className='break-all'>
<div className='flex gap-x-1'>
<div className='w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary'>Q</div>
<div className="break-all">
<div className="flex gap-x-1">
<div className="w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary">Q</div>
<div className={cn('body-md-regular line-clamp-20 text-text-secondary')}>
{content}
</div>
</div>
<div className='flex gap-x-1'>
<div className='w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary'>A</div>
<div className="flex gap-x-1">
<div className="w-4 shrink-0 text-[13px] font-medium leading-[20px] text-text-tertiary">A</div>
<div className={cn('body-md-regular line-clamp-20 text-text-secondary')}>
{answer}
</div>
@ -100,17 +101,17 @@ const ChunkDetailModal = ({
</div>
)}
{/* Mask */}
<Mask className='absolute inset-x-0 bottom-0' />
<Mask className="absolute inset-x-0 bottom-0" />
</div>
{(showImages || showKeywords) && (
<div className='flex flex-col gap-y-3 pt-3'>
<div className="flex flex-col gap-y-3 pt-3">
{showImages && (
<ImageList images={images} size='md' className='py-1' />
<ImageList images={images} size="md" className="py-1" />
)}
{showKeywords && (
<div className='flex flex-col gap-y-1'>
<div className='text-xs font-medium uppercase text-text-tertiary'>{t(`${i18nPrefix}.keyword`)}</div>
<div className='flex flex-wrap gap-x-2'>
<div className="flex flex-col gap-y-1">
<div className="text-xs font-medium uppercase text-text-tertiary">{t(`${i18nPrefix}.keyword`)}</div>
<div className="flex flex-wrap gap-x-2">
{keywords.map(keyword => (
<Tag key={keyword} text={keyword} />
))}
@ -122,8 +123,8 @@ const ChunkDetailModal = ({
</div>
{isParentChildRetrieval && (
<div className='flex-1 pb-6 pl-6'>
<div className='system-xs-semibold-uppercase text-text-secondary'>{t(`${i18nPrefix}.hitChunks`, { num: child_chunks.length })}</div>
<div className="flex-1 pb-6 pl-6">
<div className="system-xs-semibold-uppercase text-text-secondary">{t(`${i18nPrefix}.hitChunks`, { num: child_chunks.length })}</div>
<div className={cn('mt-1 space-y-2', heighClassName)}>
{child_chunks.map(item => (
<ChildChunksItem key={item.id} payload={item} isShowAll />

View File

@ -4,12 +4,14 @@ import { useTranslation } from 'react-i18next'
const EmptyRecords = () => {
const { t } = useTranslation()
return <div className='rounded-2xl bg-workflow-process-bg p-5'>
<div className='flex h-10 w-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg p-1 shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'>
<RiHistoryLine className='h-5 w-5 text-text-tertiary' />
return (
<div className="rounded-2xl bg-workflow-process-bg p-5">
<div className="flex h-10 w-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg p-1 shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]">
<RiHistoryLine className="h-5 w-5 text-text-tertiary" />
</div>
<div className="my-2 text-[13px] font-medium leading-4 text-text-tertiary">{t('datasetHitTesting.noRecentTip')}</div>
</div>
<div className='my-2 text-[13px] font-medium leading-4 text-text-tertiary'>{t('datasetHitTesting.noRecentTip')}</div>
</div>
)
}
export default React.memo(EmptyRecords)

View File

@ -12,7 +12,8 @@ export const Mask = ({
<div className={cn(
'h-12 bg-gradient-to-b from-components-panel-bg-transparent to-components-panel-bg',
className,
)} />
)}
/>
)
}

View File

@ -1,15 +1,6 @@
import type { UseMutateAsyncFunction } from '@tanstack/react-query'
import type { ChangeEvent } from 'react'
import React, { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiEqualizer2Line,
RiPlayCircleLine,
} from '@remixicon/react'
import Image from 'next/image'
import Button from '@/app/components/base/button'
import { getIcon } from '@/app/components/datasets/common/retrieval-method-info'
import ModifyExternalRetrievalModal from '@/app/components/datasets/hit-testing/modify-external-retrieval-modal'
import { cn } from '@/utils/classnames'
import type { FileEntity } from '@/app/components/datasets/common/image-uploader/types'
import type {
Attachment,
ExternalKnowledgeBaseHitTestingRequest,
@ -18,13 +9,23 @@ import type {
HitTestingResponse,
Query,
} from '@/models/datasets'
import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
import type { UseMutateAsyncFunction } from '@tanstack/react-query'
import ImageUploaderInRetrievalTesting from '@/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing'
import Textarea from './textarea'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import type { FileEntity } from '@/app/components/datasets/common/image-uploader/types'
import type { RetrievalConfig } from '@/types/app'
import {
RiEqualizer2Line,
RiPlayCircleLine,
} from '@remixicon/react'
import Image from 'next/image'
import React, { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { v4 as uuid4 } from 'uuid'
import Button from '@/app/components/base/button'
import ImageUploaderInRetrievalTesting from '@/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing'
import { getIcon } from '@/app/components/datasets/common/retrieval-method-info'
import ModifyExternalRetrievalModal from '@/app/components/datasets/hit-testing/modify-external-retrieval-modal'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { RETRIEVE_METHOD } from '@/types/app'
import { cn } from '@/utils/classnames'
import Textarea from './textarea'
type QueryInputProps = {
onUpdateList: () => void
@ -176,7 +177,7 @@ const QueryInput = ({
}, [text, externalRetrievalSettings, externalKnowledgeBaseHitTestingMutation, onUpdateList, setExternalHitResult])
const retrievalMethod = isEconomy ? RETRIEVE_METHOD.keywordSearch : retrievalConfig.search_method
const icon = <Image className='size-3.5 text-util-colors-purple-purple-600' src={getIcon(retrievalMethod)} alt='' />
const icon = <Image className="size-3.5 text-util-colors-purple-purple-600" src={getIcon(retrievalMethod)} alt="" />
const TextAreaComp = useMemo(() => {
return (
<Textarea
@ -189,12 +190,12 @@ const QueryInput = ({
return (
<Button
onClick={isExternal ? externalRetrievalTestingOnSubmit : onSubmit}
variant='primary'
variant="primary"
loading={loading}
disabled={(text.length === 0 && images.length === 0) || text.length > 200 || (images.length > 0 && !isAllUploaded)}
className='w-[88px]'
className="w-[88px]"
>
<RiPlayCircleLine className='mr-1 size-4' />
<RiPlayCircleLine className="mr-1 size-4" />
{t('datasetHitTesting.input.testing')}
</Button>
)
@ -202,32 +203,34 @@ const QueryInput = ({
return (
<div className={cn('relative flex h-80 shrink-0 flex-col overflow-hidden rounded-xl bg-gradient-to-r from-components-input-border-active-prompt-1 to-components-input-border-active-prompt-2 p-0.5 shadow-xs')}>
<div className='flex h-full flex-col overflow-hidden rounded-[10px] bg-background-section-burn'>
<div className='relative flex shrink-0 items-center justify-between p-1.5 pb-1 pl-3'>
<span className='system-sm-semibold-uppercase text-text-secondary'>
<div className="flex h-full flex-col overflow-hidden rounded-[10px] bg-background-section-burn">
<div className="relative flex shrink-0 items-center justify-between p-1.5 pb-1 pl-3">
<span className="system-sm-semibold-uppercase text-text-secondary">
{t('datasetHitTesting.input.title')}
</span>
{isExternal ? (
<Button
variant='secondary'
size='small'
onClick={() => setIsSettingsOpen(!isSettingsOpen)}
>
<RiEqualizer2Line className='h-3.5 w-3.5 text-components-button-secondary-text' />
<div className='flex items-center justify-center gap-1 px-[3px]'>
<span className='system-xs-medium text-components-button-secondary-text'>{t('datasetHitTesting.settingTitle')}</span>
</div>
</Button>
) : (
<div
onClick={onClickRetrievalMethod}
className='flex h-7 cursor-pointer items-center space-x-0.5 rounded-lg border-[0.5px] border-components-button-secondary-bg bg-components-button-secondary-bg px-1.5 shadow-xs backdrop-blur-[5px] hover:bg-components-button-secondary-bg-hover'
>
{icon}
<div className='text-xs font-medium uppercase text-text-secondary'>{t(`dataset.retrieval.${retrievalMethod}.title`)}</div>
<RiEqualizer2Line className='size-4 text-components-menu-item-text'></RiEqualizer2Line>
</div>
)}
{isExternal
? (
<Button
variant="secondary"
size="small"
onClick={() => setIsSettingsOpen(!isSettingsOpen)}
>
<RiEqualizer2Line className="h-3.5 w-3.5 text-components-button-secondary-text" />
<div className="flex items-center justify-center gap-1 px-[3px]">
<span className="system-xs-medium text-components-button-secondary-text">{t('datasetHitTesting.settingTitle')}</span>
</div>
</Button>
)
: (
<div
onClick={onClickRetrievalMethod}
className="flex h-7 cursor-pointer items-center space-x-0.5 rounded-lg border-[0.5px] border-components-button-secondary-bg bg-components-button-secondary-bg px-1.5 shadow-xs backdrop-blur-[5px] hover:bg-components-button-secondary-bg-hover"
>
{icon}
<div className="text-xs font-medium uppercase text-text-secondary">{t(`dataset.retrieval.${retrievalMethod}.title`)}</div>
<RiEqualizer2Line className="size-4 text-components-menu-item-text"></RiEqualizer2Line>
</div>
)}
{
isSettingsOpen && (
<ModifyExternalRetrievalModal
@ -246,8 +249,8 @@ const QueryInput = ({
onChange={handleImageChange}
value={images}
showUploader={isMultimodal}
className='grow'
actionAreaClassName='px-4 py-2 shrink-0 bg-background-default'
className="grow"
actionAreaClassName="px-4 py-2 shrink-0 bg-background-default"
/>
</div>
</div>

View File

@ -1,9 +1,9 @@
import type { ChangeEvent } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { cn } from '@/utils/classnames'
import { Corner } from '@/app/components/base/icons/src/vender/solid/shapes'
import Tooltip from '@/app/components/base/tooltip'
import { cn } from '@/utils/classnames'
type TextareaProps = {
text: string
@ -20,39 +20,41 @@ const Textarea = ({
<div className={cn(
'relative flex-1 overflow-hidden rounded-t-[10px] border-t-[0.5px] border-components-panel-border-subtle bg-background-default px-4 pb-0 pt-3',
text.length > 200 && 'border-state-destructive-active',
)}>
)}
>
<textarea
className='system-md-regular h-full w-full resize-none border-none bg-transparent text-text-secondary caret-[#295EFF] placeholder:text-components-input-text-placeholder focus-visible:outline-none'
className="system-md-regular h-full w-full resize-none border-none bg-transparent text-text-secondary caret-[#295EFF] placeholder:text-components-input-text-placeholder focus-visible:outline-none"
value={text}
onChange={handleTextChange}
placeholder={t('datasetHitTesting.input.placeholder') as string}
/>
<div className='absolute right-0 top-0 flex items-center'>
<div className="absolute right-0 top-0 flex items-center">
<Corner className={cn(
'text-background-section-burn',
text.length > 200 && 'text-util-colors-red-red-100',
)} />
)}
/>
{text.length > 200
? (
<Tooltip
popupContent={t('datasetHitTesting.input.countWarning')}
>
<Tooltip
popupContent={t('datasetHitTesting.input.countWarning')}
>
<div
className={cn('system-2xs-medium-uppercase bg-util-colors-red-red-100 py-1 pr-2 text-util-colors-red-red-600')}
>
{`${text.length}/200`}
</div>
</Tooltip>
)
: (
<div
className={cn('system-2xs-medium-uppercase bg-util-colors-red-red-100 py-1 pr-2 text-util-colors-red-red-600')}
className={cn(
'system-2xs-medium-uppercase bg-background-section-burn py-1 pr-2 text-text-tertiary',
)}
>
{`${text.length}/200`}
</div>
</Tooltip>
)
: (
<div
className={cn(
'system-2xs-medium-uppercase bg-background-section-burn py-1 pr-2 text-text-tertiary',
)}
>
{`${text.length}/200`}
</div>
)}
)}
</div>
</div>
)

View File

@ -1,10 +1,10 @@
import React, { useCallback, useMemo, useState } from 'react'
import useTimestamp from '@/hooks/use-timestamp'
import type { Attachment, HitTestingRecord, Query } from '@/models/datasets'
import { RiApps2Line, RiArrowDownLine, RiFocus2Line } from '@remixicon/react'
import React, { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ImageList from '../../common/image-list'
import useTimestamp from '@/hooks/use-timestamp'
import { cn } from '@/utils/classnames'
import ImageList from '../../common/image-list'
type RecordsProps = {
records: HitTestingRecord[]
@ -45,15 +45,15 @@ const Records = ({
}
return (
<div className='grow overflow-y-auto'>
<table className={'w-full border-collapse border-0 text-[13px] leading-4 text-text-secondary '}>
<thead className='sticky top-0 h-7 text-xs font-medium uppercase leading-7 text-text-tertiary backdrop-blur-[5px]'>
<div className="grow overflow-y-auto">
<table className="w-full border-collapse border-0 text-[13px] leading-4 text-text-secondary ">
<thead className="sticky top-0 h-7 text-xs font-medium uppercase leading-7 text-text-tertiary backdrop-blur-[5px]">
<tr>
<td className='rounded-l-lg bg-background-section-burn pl-3'>{t('datasetHitTesting.table.header.queryContent')}</td>
<td className='w-[128px] bg-background-section-burn pl-3'>{t('datasetHitTesting.table.header.source')}</td>
<td className='w-48 rounded-r-lg bg-background-section-burn pl-3'>
<td className="rounded-l-lg bg-background-section-burn pl-3">{t('datasetHitTesting.table.header.queryContent')}</td>
<td className="w-[128px] bg-background-section-burn pl-3">{t('datasetHitTesting.table.header.source')}</td>
<td className="w-48 rounded-r-lg bg-background-section-burn pl-3">
<div
className='flex cursor-pointer items-center'
className="flex cursor-pointer items-center"
onClick={handleSortTime}
>
{t('datasetHitTesting.table.header.time')}
@ -76,33 +76,33 @@ const Records = ({
return (
<tr
key={id}
className='group cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover'
className="group cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover"
onClick={() => onClickRecord(record)}
>
<td className='max-w-xs p-3 pr-2'>
<div className='flex flex-col gap-y-1'>
<td className="max-w-xs p-3 pr-2">
<div className="flex flex-col gap-y-1">
{content && (
<div className='line-clamp-2'>
<div className="line-clamp-2">
{content}
</div>
)}
{images.length > 0 && (
<ImageList
images={images}
size='md'
className='py-1'
size="md"
className="py-1"
limit={5}
/>
)}
</div>
</td>
<td className='w-[128px] p-3 pr-2'>
<div className='flex items-center'>
<SourceIcon className='mr-1 size-4 text-text-tertiary' />
<span className='capitalize'>{source.replace('_', ' ').replace('hit testing', 'retrieval test')}</span>
<td className="w-[128px] p-3 pr-2">
<div className="flex items-center">
<SourceIcon className="mr-1 size-4 text-text-tertiary" />
<span className="capitalize">{source.replace('_', ' ').replace('hit testing', 'retrieval test')}</span>
</div>
</td>
<td className='w-48 p-3 pr-2'>
<td className="w-48 p-3 pr-2">
{formatTime(created_at, t('datasetHitTesting.dateTimeFormat') as string)}
</td>
</tr>

View File

@ -1,14 +1,14 @@
'use client'
import type { FC } from 'react'
import type { ExternalKnowledgeBaseHitTesting } from '@/models/datasets'
import { useBoolean } from 'ahooks'
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'
import Modal from '@/app/components/base/modal'
import { cn } from '@/utils/classnames'
import ResultItemFooter from './result-item-footer'
import ResultItemMeta from './result-item-meta'
const i18nPrefix = 'datasetHitTesting'
type Props = {
@ -27,11 +27,11 @@ const ResultItemExternal: FC<Props> = ({ payload, positionId }) => {
return (
<div className={cn('cursor-pointer rounded-xl bg-chat-bubble-bg pt-3 hover:shadow-lg')} onClick={showDetailModal}>
{/* Meta info */}
<ResultItemMeta className='px-3' labelPrefix={'Chunk'} positionId={positionId} wordCount={content.length} score={score} />
<ResultItemMeta className="px-3" labelPrefix="Chunk" positionId={positionId} wordCount={content.length} score={score} />
{/* Main */}
<div className='mt-1 px-3'>
<div className='body-md-regular line-clamp-2 break-all text-text-primary'>{content}</div>
<div className="mt-1 px-3">
<div className="body-md-regular line-clamp-2 break-all text-text-primary">{content}</div>
</div>
{/* Foot */}
@ -40,13 +40,13 @@ const ResultItemExternal: FC<Props> = ({ payload, positionId }) => {
{isShowDetailModal && (
<Modal
title={t(`${i18nPrefix}.chunkDetail`)}
className={'!min-w-[800px]'}
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="mt-4 flex-1">
<ResultItemMeta labelPrefix="Chunk" positionId={positionId} wordCount={content.length} score={score} />
<div className={cn('body-md-regular mt-2 break-all text-text-secondary', 'h-[min(539px,_80vh)] overflow-y-auto')}>
{content}
</div>

View File

@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
import { RiArrowRightUpLine } from '@remixicon/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

View File

@ -2,10 +2,10 @@
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'
import Dot from '../../documents/detail/completed/common/dot'
import { SegmentIndexTag } from '../../documents/detail/completed/common/segment-index-tag'
import Score from './score'
type Props = {
labelPrefix: string
@ -34,7 +34,9 @@ const ResultItemMeta: FC<Props> = ({
/>
<Dot />
<div className="system-xs-medium text-text-tertiary">
{wordCount} {t('datasetDocuments.segment.characters', { count: wordCount })}
{wordCount}
{' '}
{t('datasetDocuments.segment.characters', { count: wordCount })}
</div>
</div>
<Score value={score} />

View File

@ -1,19 +1,19 @@
'use client'
import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
import type { HitTesting } from '@/models/datasets'
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import ChildChunkItem from './child-chunks-item'
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 { cn } from '@/utils/classnames'
import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Markdown } from '@/app/components/base/markdown'
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 { Markdown } from '@/app/components/base/markdown'
import { cn } from '@/utils/classnames'
import ImageList from '../../common/image-list'
import ChildChunkItem from './child-chunks-item'
import ChunkDetailModal from './chunk-detail-modal'
import ResultItemFooter from './result-item-footer'
import ResultItemMeta from './result-item-meta'
const i18nPrefix = 'datasetHitTesting'
type ResultItemProps = {
@ -41,7 +41,8 @@ const ResultItem = ({
}] = useBoolean(false)
const images = useMemo(() => {
if (!files) return []
if (!files)
return []
return files.map(file => ({
name: file.name,
mimeType: file.mime_type,
@ -54,20 +55,20 @@ const ResultItem = ({
return (
<div className={cn('cursor-pointer rounded-xl bg-chat-bubble-bg pt-3 hover:shadow-lg')} onClick={showDetailModal}>
{/* Meta info */}
<ResultItemMeta className='px-3' labelPrefix={`${isParentChildRetrieval ? 'Parent-' : ''}Chunk`} positionId={position} wordCount={word_count} score={score} />
<ResultItemMeta className="px-3" labelPrefix={`${isParentChildRetrieval ? 'Parent-' : ''}Chunk`} positionId={position} wordCount={word_count} score={score} />
{/* Main */}
<div className='mt-1 px-3'>
{<Markdown
className='line-clamp-2'
<div className="mt-1 px-3">
<Markdown
className="line-clamp-2"
content={sign_content || content}
customDisallowedElements={['input']}
/>}
/>
{images.length > 0 && (
<ImageList images={images} size='md' className='py-1' />
<ImageList images={images} size="md" className="py-1" />
)}
{isParentChildRetrieval && (
<div className='mt-1'>
<div className="mt-1">
<div
className={cn('inline-flex h-6 cursor-pointer select-none items-center space-x-0.5 rounded-lg text-text-secondary', isFold && 'bg-workflow-process-bg pl-1')}
onClick={(e) => {
@ -76,12 +77,12 @@ const ResultItem = ({
}}
>
<Icon className={cn('h-4 w-4', isFold && 'opacity-50')} />
<div className='text-xs font-semibold uppercase'>{t(`${i18nPrefix}.hitChunks`, { num: child_chunks.length })}</div>
<div className="text-xs font-semibold uppercase">{t(`${i18nPrefix}.hitChunks`, { num: child_chunks.length })}</div>
</div>
{!isFold && (
<div className='space-y-2'>
<div className="space-y-2">
{child_chunks.map(item => (
<div key={item.id} className='ml-[7px] border-l-[2px] border-text-accent-secondary pl-[7px]'>
<div key={item.id} className="ml-[7px] border-l-[2px] border-text-accent-secondary pl-[7px]">
<ChildChunkItem payload={item} isShowAll={false} />
</div>
))}
@ -90,9 +91,9 @@ const ResultItem = ({
</div>
)}
{!isParentChildRetrieval && keywords && keywords.length > 0 && (
<div className='mt-2 flex flex-wrap'>
<div className="mt-2 flex flex-wrap">
{keywords.map(keyword => (
<Tag key={keyword} text={keyword} className='mr-2' />
<Tag key={keyword} text={keyword} className="mr-2" />
))}
</div>
)}
@ -108,7 +109,7 @@ const ResultItem = ({
/>
)
}
</div >
</div>
)
}
export default React.memo(ResultItem)

View File

@ -15,12 +15,11 @@ const Score: FC<Props> = ({
if (!value || isNaN(value))
return null
return (
<div className={cn('relative items-center overflow-hidden border border-components-progress-bar-border px-[5px]',
besideChunkName ? 'h-[20.5px] border-l-0' : 'h-[20px] rounded-md')}>
<div className={cn('relative items-center overflow-hidden border border-components-progress-bar-border px-[5px]', besideChunkName ? 'h-[20.5px] border-l-0' : 'h-[20px] rounded-md')}>
<div className={cn('absolute left-0 top-0 h-full border-r-[1.5px] border-components-progress-brand-progress bg-util-colors-blue-brand-blue-brand-100', value === 1 && 'border-r-0')} style={{ width: `${value * 100}%` }} />
<div className={cn('relative flex h-full items-center space-x-0.5 text-util-colors-blue-brand-blue-brand-700')}>
<div className='system-2xs-medium-uppercase'>score</div>
<div className='system-xs-semibold'>{value?.toFixed(2)}</div>
<div className="system-2xs-medium-uppercase">score</div>
<div className="system-xs-semibold">{value?.toFixed(2)}</div>
</div>
</div>
)

View File

@ -1,15 +1,5 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import { useContext } from 'use-context-selector'
import QueryInput from './components/query-input'
import s from './style.module.css'
import ModifyRetrievalModal from './modify-retrieval-modal'
import ResultItem from './components/result-item'
import ResultItemExternal from './components/result-item-external'
import { cn } from '@/utils/classnames'
import type {
ExternalKnowledgeBaseHitTesting,
ExternalKnowledgeBaseHitTestingResponse,
@ -18,22 +8,32 @@ import type {
HitTestingResponse,
Query,
} from '@/models/datasets'
import Loading from '@/app/components/base/loading'
import Drawer from '@/app/components/base/drawer'
import Pagination from '@/app/components/base/pagination'
import FloatRightContainer from '@/app/components/base/float-right-container'
import DatasetDetailContext from '@/context/dataset-detail'
import type { RetrievalConfig } from '@/types/app'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useBoolean } from 'ahooks'
import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import Drawer from '@/app/components/base/drawer'
import FloatRightContainer from '@/app/components/base/float-right-container'
import Loading from '@/app/components/base/loading'
import Pagination from '@/app/components/base/pagination'
import docStyle from '@/app/components/datasets/documents/detail/completed/style.module.css'
import { CardSkelton } from '../documents/detail/completed/skeleton/general-list-skeleton'
import EmptyRecords from './components/empty-records'
import Records from './components/records'
import DatasetDetailContext from '@/context/dataset-detail'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useDatasetTestingRecords } from '@/service/knowledge/use-dataset'
import {
useExternalKnowledgeBaseHitTesting,
useHitTesting,
} from '@/service/knowledge/use-hit-testing'
import { useDatasetTestingRecords } from '@/service/knowledge/use-dataset'
import { cn } from '@/utils/classnames'
import { CardSkelton } from '../documents/detail/completed/skeleton/general-list-skeleton'
import EmptyRecords from './components/empty-records'
import QueryInput from './components/query-input'
import Records from './components/records'
import ResultItem from './components/result-item'
import ResultItemExternal from './components/result-item-external'
import ModifyRetrievalModal from './modify-retrieval-modal'
import s from './style.module.css'
const limit = 10
@ -73,32 +73,32 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
const isRetrievalLoading = isHitTestingPending || isExternalKnowledgeBaseHitTestingPending
const renderHitResults = (results: HitTesting[] | ExternalKnowledgeBaseHitTesting[]) => (
<div className='flex h-full flex-col rounded-tl-2xl bg-background-body px-4 py-3'>
<div className='mb-2 shrink-0 pl-2 font-semibold leading-6 text-text-primary'>
<div className="flex h-full flex-col rounded-tl-2xl bg-background-body px-4 py-3">
<div className="mb-2 shrink-0 pl-2 font-semibold leading-6 text-text-primary">
{t('datasetHitTesting.hit.title', { num: results.length })}
</div>
<div className='grow space-y-2 overflow-y-auto'>
<div className="grow space-y-2 overflow-y-auto">
{results.map((record, idx) =>
isExternal
? (
<ResultItemExternal
key={idx}
positionId={idx + 1}
payload={record as ExternalKnowledgeBaseHitTesting}
/>
)
<ResultItemExternal
key={idx}
positionId={idx + 1}
payload={record as ExternalKnowledgeBaseHitTesting}
/>
)
: (
<ResultItem key={idx} payload={record as HitTesting} />
),
<ResultItem key={idx} payload={record as HitTesting} />
),
)}
</div>
</div>
)
const renderEmptyState = () => (
<div className='flex h-full flex-col items-center justify-center rounded-tl-2xl bg-background-body px-4 py-3'>
<div className="flex h-full flex-col items-center justify-center rounded-tl-2xl bg-background-body px-4 py-3">
<div className={cn(docStyle.commonIcon, docStyle.targetIcon, '!h-14 !w-14 !bg-text-quaternary')} />
<div className='mt-3 text-[13px] text-text-quaternary'>
<div className="mt-3 text-[13px] text-text-quaternary">
{t('datasetHitTesting.hit.emptyTip')}
</div>
</div>
@ -115,10 +115,10 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
return (
<div className={s.container}>
<div className='flex flex-col px-6 py-3'>
<div className='mb-4 flex flex-col justify-center'>
<h1 className='text-base font-semibold text-text-primary'>{t('datasetHitTesting.title')}</h1>
<p className='mt-0.5 text-[13px] font-normal leading-4 text-text-tertiary'>{t('datasetHitTesting.desc')}</p>
<div className="flex flex-col px-6 py-3">
<div className="mb-4 flex flex-col justify-center">
<h1 className="text-base font-semibold text-text-primary">{t('datasetHitTesting.title')}</h1>
<p className="mt-0.5 text-[13px] font-normal leading-4 text-text-tertiary">{t('datasetHitTesting.desc')}</p>
</div>
<QueryInput
key={queryInputKey}
@ -136,13 +136,13 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
hitTestingMutation={hitTestingMutation}
externalKnowledgeBaseHitTestingMutation={externalKnowledgeBaseHitTestingMutation}
/>
<div className='mb-3 mt-6 text-base font-semibold text-text-primary'>{t('datasetHitTesting.records')}</div>
<div className="mb-3 mt-6 text-base font-semibold text-text-primary">{t('datasetHitTesting.records')}</div>
{isRecordsLoading && (
<div className='flex-1'><Loading type='app' /></div>
<div className="flex-1"><Loading type="app" /></div>
)}
{!isRecordsLoading && recordsRes?.data && recordsRes.data.length > 0 && (
<>
<Records records={recordsRes?.data} onClickRecord={handleClickRecord}/>
<Records records={recordsRes?.data} onClickRecord={handleClickRecord} />
{(total && total > limit)
? <Pagination current={currPage} onChange={setCurrPage} total={total} limit={limit} />
: null}
@ -153,30 +153,31 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
)}
</div>
<FloatRightContainer
panelClassName='!justify-start !overflow-y-auto'
panelClassName="!justify-start !overflow-y-auto"
showClose
isMobile={isMobile}
isOpen={isShowRightPanel}
onClose={hideRightPanel}
footer={null}
>
<div className='flex flex-col pt-3'>
<div className="flex flex-col pt-3">
{isRetrievalLoading
? <div className='flex h-full flex-col rounded-tl-2xl bg-background-body px-4 py-3'>
<CardSkelton />
</div>
? (
<div className="flex h-full flex-col rounded-tl-2xl bg-background-body px-4 py-3">
<CardSkelton />
</div>
)
: (
(() => {
if (!hitResult?.records.length && !externalHitResult?.records.length)
return renderEmptyState()
(() => {
if (!hitResult?.records.length && !externalHitResult?.records.length)
return renderEmptyState()
if (hitResult?.records.length)
return renderHitResults(hitResult.records)
if (hitResult?.records.length)
return renderHitResults(hitResult.records)
return renderHitResults(externalHitResult?.records || [])
})()
)
}
return renderHitResults(externalHitResult?.records || [])
})()
)}
</div>
</FloatRightContainer>
<Drawer
@ -185,7 +186,7 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
onClose={() => setIsShowModifyRetrievalModal(false)}
footer={null}
mask={isMobile}
panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'
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,15 +1,15 @@
import { useState } from 'react'
import {
RiCloseLine,
} from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import RetrievalSettings from '../external-knowledge-base/create/RetrievalSettings'
import Button from '@/app/components/base/button'
import ActionButton from '@/app/components/base/action-button'
import Button from '@/app/components/base/button'
import RetrievalSettings from '../external-knowledge-base/create/RetrievalSettings'
type ModifyExternalRetrievalModalProps = {
onClose: () => void
onSave: (data: { top_k: number; score_threshold: number; score_threshold_enabled: boolean }) => void
onSave: (data: { top_k: number, score_threshold: number, score_threshold_enabled: boolean }) => void
initialTopK: number
initialScoreThreshold: number
initialScoreThresholdEnabled: boolean
@ -27,7 +27,7 @@ const ModifyExternalRetrievalModal: React.FC<ModifyExternalRetrievalModalProps>
const [scoreThreshold, setScoreThreshold] = useState(initialScoreThreshold)
const [scoreThresholdEnabled, setScoreThresholdEnabled] = useState(initialScoreThresholdEnabled)
const handleSettingsChange = (data: { top_k?: number; score_threshold?: number; score_threshold_enabled?: boolean }) => {
const handleSettingsChange = (data: { top_k?: number, score_threshold?: number, score_threshold_enabled?: boolean }) => {
if (data.top_k !== undefined)
setTopK(data.top_k)
if (data.score_threshold !== undefined)
@ -42,16 +42,16 @@ const ModifyExternalRetrievalModal: React.FC<ModifyExternalRetrievalModalProps>
}
return (
<div className='shadows-shadow-2xl absolute right-[14px] top-[36px] z-10 flex w-[320px] flex-col items-start rounded-2xl
border-[0.5px] border-components-panel-border bg-components-panel-bg'
<div className="shadows-shadow-2xl absolute right-[14px] top-[36px] z-10 flex w-[320px] flex-col items-start rounded-2xl
border-[0.5px] border-components-panel-border bg-components-panel-bg"
>
<div className='flex items-center justify-between self-stretch p-4 pb-2'>
<div className='system-xl-semibold grow text-text-primary'>{t('datasetHitTesting.settingTitle')}</div>
<ActionButton className='ml-auto' onClick={onClose}>
<RiCloseLine className='h-4 w-4 shrink-0' />
<div className="flex items-center justify-between self-stretch p-4 pb-2">
<div className="system-xl-semibold grow text-text-primary">{t('datasetHitTesting.settingTitle')}</div>
<ActionButton className="ml-auto" onClick={onClose}>
<RiCloseLine className="h-4 w-4 shrink-0" />
</ActionButton>
</div>
<div className='flex flex-col items-start justify-center gap-4 self-stretch p-4 pt-2'>
<div className="flex flex-col items-start justify-center gap-4 self-stretch p-4 pt-2">
<RetrievalSettings
topK={topK}
scoreThreshold={scoreThreshold}
@ -60,9 +60,9 @@ const ModifyExternalRetrievalModal: React.FC<ModifyExternalRetrievalModalProps>
isInHitTesting={true}
/>
</div>
<div className='flex w-full items-end justify-end gap-1 p-4 pt-2'>
<Button className='min-w-[72px] shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button variant='primary' className='min-w-[72px] shrink-0' onClick={handleSave}>{t('common.operation.save')}</Button>
<div className="flex w-full items-end justify-end gap-1 p-4 pt-2">
<Button className="min-w-[72px] shrink-0" onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button variant="primary" className="min-w-[72px] shrink-0" onClick={handleSave}>{t('common.operation.save')}</Button>
</div>
</div>
)

View File

@ -1,20 +1,20 @@
'use client'
import type { FC } from 'react'
import type { IndexingType } from '../create/step-two'
import type { RetrievalConfig } from '@/types/app'
import { RiCloseLine } from '@remixicon/react'
import React, { useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import Toast from '../../base/toast'
import { ModelTypeEnum } from '../../header/account-setting/model-provider-page/declarations'
import type { RetrievalConfig } from '@/types/app'
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
import Button from '@/app/components/base/button'
import { isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { useDocLink } from '@/context/i18n'
import { checkShowMultiModalTip } from '../settings/utils'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import type { IndexingType } from '../create/step-two'
import { useDocLink } from '@/context/i18n'
import Toast from '../../base/toast'
import { ModelTypeEnum } from '../../header/account-setting/model-provider-page/declarations'
import { checkShowMultiModalTip } from '../settings/utils'
type Props = {
indexMethod: string
@ -82,62 +82,62 @@ const ModifyRetrievalModal: FC<Props> = ({
return (
<div
className='flex w-full flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9'
className="flex w-full flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9"
style={{
height: 'calc(100vh - 72px)',
}}
ref={ref}
>
<div className='h-15 flex shrink-0 justify-between px-3 pb-1 pt-3.5'>
<div className='text-base font-semibold text-text-primary'>
<div className="h-15 flex shrink-0 justify-between px-3 pb-1 pt-3.5">
<div className="text-base font-semibold text-text-primary">
<div>{t('datasetSettings.form.retrievalSetting.title')}</div>
<div className='text-xs font-normal leading-[18px] text-text-tertiary'>
<div className="text-xs font-normal leading-[18px] text-text-tertiary">
<a
target='_blank'
rel='noopener noreferrer'
target="_blank"
rel="noopener noreferrer"
href={docLink('/guides/knowledge-base/retrieval-test-and-citation#modify-text-retrieval-setting', {
'zh-Hans': '/guides/knowledge-base/retrieval-test-and-citation#修改文本检索方式',
'ja-JP': '/guides/knowledge-base/retrieval-test-and-citation',
})}
className='text-text-accent'
className="text-text-accent"
>
{t('datasetSettings.form.retrievalSetting.learnMore')}
</a>
{t('datasetSettings.form.retrievalSetting.description')}
</div>
</div>
<div className='flex'>
<div className="flex">
<div
onClick={onHide}
className='flex h-8 w-8 cursor-pointer items-center justify-center'
className="flex h-8 w-8 cursor-pointer items-center justify-center"
>
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
<RiCloseLine className="h-4 w-4 text-text-tertiary" />
</div>
</div>
</div>
<div className='px-4 py-2'>
<div className='mb-1 text-[13px] font-semibold leading-6 text-text-secondary'>
<div className="px-4 py-2">
<div className="mb-1 text-[13px] font-semibold leading-6 text-text-secondary">
{t('datasetSettings.form.retrievalSetting.method')}
</div>
{indexMethod === 'high_quality'
? (
<RetrievalMethodConfig
value={retrievalConfig}
onChange={setRetrievalConfig}
showMultiModalTip={showMultiModalTip}
/>
)
<RetrievalMethodConfig
value={retrievalConfig}
onChange={setRetrievalConfig}
showMultiModalTip={showMultiModalTip}
/>
)
: (
<EconomicalRetrievalMethodConfig
value={retrievalConfig}
onChange={setRetrievalConfig}
/>
)}
<EconomicalRetrievalMethodConfig
value={retrievalConfig}
onChange={setRetrievalConfig}
/>
)}
</div>
<div className='flex justify-end p-4 pt-2'>
<Button className='mr-2 shrink-0' onClick={onHide}>{t('common.operation.cancel')}</Button>
<Button variant='primary' className='shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
<div className="flex justify-end p-4 pt-2">
<Button className="mr-2 shrink-0" onClick={onHide}>{t('common.operation.cancel')}</Button>
<Button variant="primary" className="shrink-0" onClick={handleSave}>{t('common.operation.save')}</Button>
</div>
</div>
)