mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-30 02:12:38 +00:00
v4.6.6-search test adapt diff search mode (#685)
This commit is contained in:
@@ -14,7 +14,14 @@ import { searchQueryExtension } from '@fastgpt/service/core/ai/functions/queryEx
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { datasetId, text, limit = 20, searchMode, usingReRank } = req.body as SearchTestProps;
|
||||
const {
|
||||
datasetId,
|
||||
text,
|
||||
limit = 1500,
|
||||
similarity,
|
||||
searchMode,
|
||||
usingReRank
|
||||
} = req.body as SearchTestProps;
|
||||
|
||||
if (!datasetId || !text) {
|
||||
throw new Error('缺少参数');
|
||||
@@ -40,11 +47,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
// model: global.chatModels[0].model
|
||||
// });
|
||||
|
||||
const { searchRes, tokens } = await searchDatasetData({
|
||||
const { searchRes, tokens, ...result } = await searchDatasetData({
|
||||
rawQuery: text,
|
||||
queries: [text],
|
||||
model: dataset.vectorModel,
|
||||
limit: Math.min(limit * 800, 30000),
|
||||
limit: Math.min(limit, 20000),
|
||||
similarity,
|
||||
datasetIds: [datasetId],
|
||||
searchMode,
|
||||
usingReRank
|
||||
@@ -68,7 +76,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
jsonRes<SearchTestResponse>(res, {
|
||||
data: {
|
||||
list: searchRes,
|
||||
duration: `${((Date.now() - start) / 1000).toFixed(3)}s`
|
||||
duration: `${((Date.now() - start) / 1000).toFixed(3)}s`,
|
||||
...result
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
|
@@ -133,7 +133,7 @@ const InfoModal = ({
|
||||
w={['26px', '34px']}
|
||||
h={['26px', '34px']}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
mr={4}
|
||||
title={'点击切换头像'}
|
||||
onClick={() => onOpenSelectFile()}
|
||||
|
@@ -128,7 +128,7 @@ const Logs = ({ appId }: { appId: string }) => {
|
||||
py={1}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
<MyIcon
|
||||
@@ -148,7 +148,7 @@ const Logs = ({ appId }: { appId: string }) => {
|
||||
py={1}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
<MyIcon
|
||||
|
@@ -20,7 +20,7 @@ const OutLink = ({ appId }: { appId: string }) => {
|
||||
</Box>
|
||||
<Box pb={[5, 7]} px={[4, 8]} borderBottom={theme.borders.base}>
|
||||
<MyRadio
|
||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(auto-fill, minmax(0, 360px))']}
|
||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(auto-fill, minmax(0, 400px))']}
|
||||
iconSize={'20px'}
|
||||
list={[
|
||||
{
|
||||
|
@@ -597,7 +597,7 @@ function Settings({ appId }: { appId: string }) {
|
||||
<Box
|
||||
borderWidth={'1px'}
|
||||
borderColor={'primary.1'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
mt={2}
|
||||
px={5}
|
||||
py={4}
|
||||
|
@@ -108,7 +108,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
borderRight={theme.borders.base}
|
||||
>
|
||||
<Flex mb={4} alignItems={'center'}>
|
||||
<Avatar src={appDetail.avatar} w={'34px'} borderRadius={'lg'} />
|
||||
<Avatar src={appDetail.avatar} w={'34px'} borderRadius={'md'} />
|
||||
<Box ml={2} fontWeight={'bold'}>
|
||||
{appDetail.name}
|
||||
</Box>
|
||||
|
@@ -99,7 +99,7 @@ const MyApps = () => {
|
||||
borderWidth={'1.5px'}
|
||||
borderColor={'borderColor.low'}
|
||||
bg={'white'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
userSelect={'none'}
|
||||
position={'relative'}
|
||||
display={'flex'}
|
||||
|
@@ -170,7 +170,7 @@ const ChatHistorySlider = ({
|
||||
variant={'whiteDanger'}
|
||||
size={'mdSquare'}
|
||||
aria-label={''}
|
||||
borderRadius={'xl'}
|
||||
borderRadius={'50%'}
|
||||
onClick={openConfirm(onClearHistory)}
|
||||
>
|
||||
<MyIcon name={'clear'} w={'16px'} />
|
||||
@@ -191,7 +191,7 @@ const ChatHistorySlider = ({
|
||||
px={4}
|
||||
cursor={'pointer'}
|
||||
userSelect={'none'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
mb={2}
|
||||
_hover={{
|
||||
bg: 'myGray.100',
|
||||
@@ -287,7 +287,7 @@ const ChatHistorySlider = ({
|
||||
py={2}
|
||||
px={3}
|
||||
mb={3}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
alignItems={'center'}
|
||||
{...(item._id === appId
|
||||
? {
|
||||
|
@@ -46,7 +46,7 @@ const SliderApps = ({ appId }: { appId: string }) => {
|
||||
px={3}
|
||||
mb={3}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
alignItems={'center'}
|
||||
{...(item._id === appId
|
||||
? {
|
||||
|
@@ -380,7 +380,7 @@ const FileSelect = ({
|
||||
textAlign={'center'}
|
||||
bg={'myWhite.400'}
|
||||
p={5}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
border={'1px dashed'}
|
||||
borderColor={'myGray.300'}
|
||||
w={'100%'}
|
||||
|
@@ -1,13 +1,26 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Box, Textarea, Button, Flex, useTheme, Grid, useDisclosure } from '@chakra-ui/react';
|
||||
import {
|
||||
Box,
|
||||
Textarea,
|
||||
Button,
|
||||
Flex,
|
||||
useTheme,
|
||||
Grid,
|
||||
useDisclosure,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
TableContainer
|
||||
} from '@chakra-ui/react';
|
||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||
import { useSearchTestStore, SearchTestStoreItemType } from '@/web/core/dataset/store/searchTest';
|
||||
import { getDatasetDataItemById, postSearchText } from '@/web/core/dataset/api';
|
||||
import { postSearchText } from '@/web/core/dataset/api';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { formatTimeToChatTime } from '@/utils/tools';
|
||||
import InputDataModal, { RawSourceText, type InputDataType } from './InputDataModal';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
@@ -17,26 +30,55 @@ import { useTranslation } from 'next-i18next';
|
||||
import { SearchTestResponse } from '@/global/core/dataset/api';
|
||||
import { DatasetSearchModeEnum, DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constant';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import MySelect from '@/components/Select';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { fileDownload, readCsvContent } from '@/web/common/file/utils';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import QuoteItem from '@/components/core/dataset/QuoteItem';
|
||||
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal'));
|
||||
|
||||
type FormType = {
|
||||
inputText: string;
|
||||
searchParams: {
|
||||
searchMode: `${DatasetSearchModeEnum}`;
|
||||
usingReRank: boolean;
|
||||
limit: number;
|
||||
similarity: number;
|
||||
};
|
||||
};
|
||||
|
||||
const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { setLoading } = useSystemStore();
|
||||
const { datasetDetail } = useDatasetStore();
|
||||
const { datasetTestList, pushDatasetTestItem, delDatasetTestItemById, updateDatasetItemById } =
|
||||
useSearchTestStore();
|
||||
const [inputText, setInputText] = useState('');
|
||||
const { pushDatasetTestItem } = useSearchTestStore();
|
||||
const [inputType, setInputType] = useState<'text' | 'file'>('text');
|
||||
const [datasetTestItem, setDatasetTestItem] = useState<SearchTestStoreItemType>();
|
||||
const [editInputData, setEditInputData] = useState<InputDataType & { collectionId: string }>();
|
||||
const [searchMode, setSearchMode] = useState<`${DatasetSearchModeEnum}`>(
|
||||
DatasetSearchModeEnum.embedding
|
||||
);
|
||||
const [usingReRank, setUsingReRank] = useState(false);
|
||||
const searchModeData = DatasetSearchModeMap[searchMode];
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const { File, onOpen } = useSelectFile({
|
||||
fileType: '.csv',
|
||||
multiple: false
|
||||
});
|
||||
const [selectFile, setSelectFile] = useState<File>();
|
||||
|
||||
const { getValues, setValue, register, handleSubmit } = useForm<FormType>({
|
||||
defaultValues: {
|
||||
inputText: '',
|
||||
searchParams: {
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
usingReRank: false,
|
||||
limit: 5000,
|
||||
similarity: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const searchModeData = DatasetSearchModeMap[getValues('searchParams.searchMode')];
|
||||
|
||||
const {
|
||||
isOpen: isOpenSelectMode,
|
||||
@@ -44,14 +86,9 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
onClose: onCloseSelectMode
|
||||
} = useDisclosure();
|
||||
|
||||
const testHistories = useMemo(
|
||||
() => datasetTestList.filter((item) => item.datasetId === datasetId),
|
||||
[datasetId, datasetTestList]
|
||||
);
|
||||
|
||||
const { mutate, isLoading } = useRequest({
|
||||
mutationFn: () =>
|
||||
postSearchText({ datasetId, text: inputText.trim(), searchMode, usingReRank, limit: 20 }),
|
||||
const { mutate: onTextTest, isLoading: textTestIsLoading } = useRequest({
|
||||
mutationFn: ({ inputText, searchParams }: FormType) =>
|
||||
postSearchText({ datasetId, text: inputText.trim(), ...searchParams }),
|
||||
onSuccess(res: SearchTestResponse) {
|
||||
if (!res || res.list.length === 0) {
|
||||
return toast({
|
||||
@@ -62,11 +99,14 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
const testItem: SearchTestStoreItemType = {
|
||||
id: nanoid(),
|
||||
datasetId,
|
||||
text: inputText.trim(),
|
||||
text: getValues('inputText').trim(),
|
||||
time: new Date(),
|
||||
results: res.list,
|
||||
duration: res.duration,
|
||||
searchMode
|
||||
searchMode: res.searchMode,
|
||||
usingReRank: res.usingReRank,
|
||||
limit: res.limit,
|
||||
similarity: res.similarity
|
||||
};
|
||||
pushDatasetTestItem(testItem);
|
||||
setDatasetTestItem(testItem);
|
||||
@@ -78,6 +118,40 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
});
|
||||
}
|
||||
});
|
||||
const { mutate: onFileTest, isLoading: fileTestIsLoading } = useRequest({
|
||||
mutationFn: async ({ searchParams }: FormType) => {
|
||||
if (!selectFile) return Promise.reject('File is not selected');
|
||||
const { data } = await readCsvContent(selectFile);
|
||||
const testList = data.slice(0, 100);
|
||||
const results: SearchTestResponse[] = [];
|
||||
|
||||
for await (const item of testList) {
|
||||
try {
|
||||
const result = await postSearchText({ datasetId, text: item[0].trim(), ...searchParams });
|
||||
results.push(result);
|
||||
} catch (error) {
|
||||
await delay(500);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
onSuccess(res: SearchTestResponse[]) {
|
||||
console.log(res);
|
||||
},
|
||||
onError(err) {
|
||||
toast({
|
||||
title: getErrText(err),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const onSelectFile = async (files: File[]) => {
|
||||
const file = files[0];
|
||||
if (!file) return;
|
||||
setSelectFile(file);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setDatasetTestItem(undefined);
|
||||
@@ -85,7 +159,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
|
||||
return (
|
||||
<Box h={'100%'} display={['block', 'flex']}>
|
||||
{/* input */}
|
||||
{/* left */}
|
||||
<Box
|
||||
h={['auto', '100%']}
|
||||
display={['block', 'flex']}
|
||||
@@ -96,11 +170,39 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
borderRight={['none', theme.borders.base]}
|
||||
>
|
||||
<Box border={'2px solid'} borderColor={'primary.500'} p={3} mx={4} borderRadius={'md'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontSize={'sm'} fontWeight={'bold'} flex={1}>
|
||||
<MyIcon mr={2} name={'text'} w={'18px'} h={'18px'} color={'primary.600'} />
|
||||
{t('core.dataset.test.Test Text')}
|
||||
</Box>
|
||||
{/* header */}
|
||||
<Flex alignItems={'center'} justifyContent={'space-between'}>
|
||||
<MySelect
|
||||
size={'sm'}
|
||||
w={'150px'}
|
||||
list={[
|
||||
{
|
||||
label: (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon mr={2} name={'text'} w={'14px'} color={'primary.600'} />
|
||||
<Box fontSize={'sm'} fontWeight={'bold'} flex={1}>
|
||||
{t('core.dataset.test.Test Text')}
|
||||
</Box>
|
||||
</Flex>
|
||||
),
|
||||
value: 'text'
|
||||
}
|
||||
// {
|
||||
// label: (
|
||||
// <Flex alignItems={'center'}>
|
||||
// <MyIcon mr={2} name={'csvImport'} w={'14px'} color={'primary.600'} />
|
||||
// <Box fontSize={'sm'} fontWeight={'bold'} flex={1}>
|
||||
// {t('core.dataset.test.Batch test')}
|
||||
// </Box>
|
||||
// </Flex>
|
||||
// ),
|
||||
// value: 'file'
|
||||
// }
|
||||
]}
|
||||
value={inputType}
|
||||
onchange={(e) => setInputType(e)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<MyIcon name={searchModeData.icon as any} w={'14px'} />}
|
||||
@@ -110,257 +212,293 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
{t(searchModeData.title)}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Textarea
|
||||
rows={6}
|
||||
resize={'none'}
|
||||
variant={'unstyled'}
|
||||
maxLength={datasetDetail.vectorModel.maxToken}
|
||||
placeholder={t('core.dataset.test.Test Text Placeholder')}
|
||||
defaultValue={inputText}
|
||||
onBlur={(e) => setInputText(e.target.value)}
|
||||
/>
|
||||
<Flex alignItems={'center'} justifyContent={'flex-end'}>
|
||||
<Box mx={3} color={'myGray.500'}>
|
||||
{inputText.length}
|
||||
</Box>
|
||||
<Button isDisabled={inputText === ''} isLoading={isLoading} onClick={mutate}>
|
||||
|
||||
<Box h={'180px'}>
|
||||
{inputType === 'text' && (
|
||||
<Textarea
|
||||
h={'100%'}
|
||||
resize={'none'}
|
||||
variant={'unstyled'}
|
||||
maxLength={datasetDetail.vectorModel.maxToken}
|
||||
placeholder={t('core.dataset.test.Test Text Placeholder')}
|
||||
{...register('inputText', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{inputType === 'file' && (
|
||||
<Box pt={5}>
|
||||
<Flex
|
||||
p={3}
|
||||
borderRadius={'md'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={'borderColor.base'}
|
||||
borderStyle={'dashed'}
|
||||
bg={'white'}
|
||||
cursor={'pointer'}
|
||||
justifyContent={'center'}
|
||||
_hover={{
|
||||
bg: 'primary.100',
|
||||
borderColor: 'primary.500',
|
||||
borderStyle: 'solid'
|
||||
}}
|
||||
onClick={onOpen}
|
||||
>
|
||||
<MyIcon mr={2} name={'csvImport'} w={'24px'} />
|
||||
<Box>
|
||||
{selectFile ? selectFile.name : t('core.dataset.test.Batch test Placeholder')}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box mt={3} fontSize={'sm'}>
|
||||
读取 CSV 文件第一列进行批量测试,单次最多支持 100 组数据。
|
||||
<Box
|
||||
as={'span'}
|
||||
color={'primary.600'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
fileDownload({
|
||||
text: `"问题"\n"问题1"\n"问题2"\n"问题3"`,
|
||||
type: 'text/csv',
|
||||
filename: 'Test Template'
|
||||
});
|
||||
}}
|
||||
>
|
||||
点击下载批量测试模板
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Flex justifyContent={'flex-end'}>
|
||||
<Button
|
||||
size={'sm'}
|
||||
isLoading={textTestIsLoading || fileTestIsLoading}
|
||||
isDisabled={inputType === 'file' && !selectFile}
|
||||
onClick={() => {
|
||||
if (inputType === 'text') {
|
||||
handleSubmit((data) => onTextTest(data))();
|
||||
} else {
|
||||
handleSubmit((data) => onFileTest(data))();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('core.dataset.test.Test')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box mt={5} flex={'1 0 0'} px={4} overflow={'overlay'} display={['none', 'block']}>
|
||||
<Flex alignItems={'center'} color={'myGray.600'}>
|
||||
<MyIcon mr={2} name={'history'} w={'16px'} h={'16px'} />
|
||||
<Box fontSize={'2xl'}>{t('core.dataset.test.test history')}</Box>
|
||||
</Flex>
|
||||
<Box mt={2}>
|
||||
<Flex py={2} fontWeight={'bold'} borderBottom={theme.borders.sm}>
|
||||
<Box w={'80px'}>{t('core.dataset.search.search mode')}</Box>
|
||||
<Box flex={1}>{t('core.dataset.test.Test Text')}</Box>
|
||||
<Box w={'70px'}>{t('common.Time')}</Box>
|
||||
<Box w={'14px'}></Box>
|
||||
</Flex>
|
||||
{testHistories.map((item) => (
|
||||
<Flex
|
||||
key={item.id}
|
||||
p={1}
|
||||
alignItems={'center'}
|
||||
borderBottom={theme.borders.base}
|
||||
_hover={{
|
||||
bg: '#f4f4f4',
|
||||
'& .delete': {
|
||||
display: 'block'
|
||||
}
|
||||
}}
|
||||
cursor={'pointer'}
|
||||
fontSize={'sm'}
|
||||
onClick={() => setDatasetTestItem(item)}
|
||||
>
|
||||
<Box w={'80px'}>
|
||||
{DatasetSearchModeMap[item.searchMode] ? (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon
|
||||
name={DatasetSearchModeMap[item.searchMode].icon as any}
|
||||
w={'12px'}
|
||||
mr={'1px'}
|
||||
/>
|
||||
{t(DatasetSearchModeMap[item.searchMode].title)}
|
||||
</Flex>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</Box>
|
||||
<Box flex={1} mr={2}>
|
||||
{item.text}
|
||||
</Box>
|
||||
<Box w={'70px'}>{formatTimeToChatTime(item.time)}</Box>
|
||||
<MyTooltip label={t('core.dataset.test.delete test history')}>
|
||||
<Box w={'14px'} h={'14px'}>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
display={'none'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
delDatasetTestItemById(item.id);
|
||||
datasetTestItem?.id === item.id && setDatasetTestItem(undefined);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
<TestHistories
|
||||
datasetId={datasetId}
|
||||
datasetTestItem={datasetTestItem}
|
||||
setDatasetTestItem={setDatasetTestItem}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
{/* result show */}
|
||||
<Box p={4} h={['auto', '100%']} overflow={'overlay'} flex={'1 0 0'}>
|
||||
{!datasetTestItem?.results || datasetTestItem.results.length === 0 ? (
|
||||
<Flex
|
||||
mt={[10, 0]}
|
||||
h={'100%'}
|
||||
flexDirection={'column'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
>
|
||||
<MyIcon name={'empty'} color={'transparent'} w={'54px'} />
|
||||
<Box mt={3} color={'myGray.600'}>
|
||||
{t('core.dataset.test.test result placeholder')}
|
||||
</Box>
|
||||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontSize={'3xl'} color={'myGray.600'}>
|
||||
{t('core.dataset.test.Test Result')}
|
||||
</Box>
|
||||
<MyTooltip label={t('core.dataset.test.test result tip')} forceShow>
|
||||
<QuestionOutlineIcon
|
||||
mx={2}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
fontSize={'lg'}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box>({datasetTestItem.duration})</Box>
|
||||
</Flex>
|
||||
<Grid
|
||||
mt={1}
|
||||
gridTemplateColumns={[
|
||||
'repeat(1,1fr)',
|
||||
'repeat(1,1fr)',
|
||||
'repeat(1,1fr)',
|
||||
'repeat(1,1fr)',
|
||||
'repeat(2,1fr)'
|
||||
]}
|
||||
gridGap={4}
|
||||
>
|
||||
{datasetTestItem?.results.map((item, index) => (
|
||||
<Box
|
||||
key={item.id}
|
||||
pb={2}
|
||||
borderRadius={'lg'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
cursor={'pointer'}
|
||||
title={t('common.Edit')}
|
||||
onClick={async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await getDatasetDataItemById(item.id);
|
||||
|
||||
if (!data) {
|
||||
throw new Error(t('core.dataset.data.data is deleted'));
|
||||
}
|
||||
|
||||
setEditInputData({
|
||||
id: data.id,
|
||||
collectionId: data.collectionId,
|
||||
q: data.q,
|
||||
a: data.a,
|
||||
indexes: data.indexes
|
||||
});
|
||||
} catch (err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(err)
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<Flex p={3} alignItems={'center'} color={'myGray.500'}>
|
||||
<Box
|
||||
border={theme.borders.base}
|
||||
px={2}
|
||||
fontSize={'sm'}
|
||||
mr={3}
|
||||
borderRadius={'md'}
|
||||
>
|
||||
# {index + 1}
|
||||
</Box>
|
||||
<RawSourceText
|
||||
fontWeight={'bold'}
|
||||
color={'black'}
|
||||
sourceName={item.sourceName}
|
||||
sourceId={item.sourceId}
|
||||
canView
|
||||
/>
|
||||
{/* <MyIcon name={'kbTest'} w={'14px'} />
|
||||
<Progress
|
||||
mx={2}
|
||||
flex={'1 0 0'}
|
||||
value={item.score * 100}
|
||||
size="sm"
|
||||
borderRadius={'20px'}
|
||||
colorScheme="gray"
|
||||
/>
|
||||
<Box>{item.score.toFixed(4)}</Box> */}
|
||||
</Flex>
|
||||
<Box px={2} fontSize={'xs'} color={'myGray.600'} wordBreak={'break-word'}>
|
||||
<Box>{item.q}</Box>
|
||||
<Box>{item.a}</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
<TestResults datasetTestItem={datasetTestItem} />
|
||||
</Box>
|
||||
|
||||
{!!editInputData && (
|
||||
<InputDataModal
|
||||
collectionId={editInputData.collectionId}
|
||||
defaultValue={editInputData}
|
||||
onClose={() => setEditInputData(undefined)}
|
||||
onSuccess={(data) => {
|
||||
if (datasetTestItem && editInputData.id) {
|
||||
const newTestItem: SearchTestStoreItemType = {
|
||||
...datasetTestItem,
|
||||
results: datasetTestItem.results.map((item) =>
|
||||
item.id === editInputData.id
|
||||
? {
|
||||
...item,
|
||||
q: data.q || '',
|
||||
a: data.a || ''
|
||||
}
|
||||
: item
|
||||
)
|
||||
};
|
||||
updateDatasetItemById(newTestItem);
|
||||
setDatasetTestItem(newTestItem);
|
||||
}
|
||||
|
||||
setEditInputData(undefined);
|
||||
}}
|
||||
onDelete={() => {
|
||||
if (datasetTestItem && editInputData.id) {
|
||||
const newTestItem = {
|
||||
...datasetTestItem,
|
||||
results: datasetTestItem.results.filter((item) => item.id !== editInputData.id)
|
||||
};
|
||||
updateDatasetItemById(newTestItem);
|
||||
setDatasetTestItem(newTestItem);
|
||||
}
|
||||
setEditInputData(undefined);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isOpenSelectMode && (
|
||||
<DatasetParamsModal
|
||||
searchMode={searchMode}
|
||||
usingReRank={usingReRank}
|
||||
{...getValues('searchParams')}
|
||||
maxTokens={20000}
|
||||
onClose={onCloseSelectMode}
|
||||
onSuccess={(e) => {
|
||||
setSearchMode(e.searchMode);
|
||||
e.usingReRank !== undefined && setUsingReRank(e.usingReRank);
|
||||
setValue('searchParams', {
|
||||
...getValues('searchParams'),
|
||||
...e
|
||||
});
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<File onSelect={onSelectFile} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Test;
|
||||
export default React.memo(Test);
|
||||
|
||||
const TestHistories = React.memo(function TestHistories({
|
||||
datasetId,
|
||||
datasetTestItem,
|
||||
setDatasetTestItem
|
||||
}: {
|
||||
datasetId: string;
|
||||
datasetTestItem?: SearchTestStoreItemType;
|
||||
setDatasetTestItem: React.Dispatch<React.SetStateAction<SearchTestStoreItemType | undefined>>;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { datasetTestList, delDatasetTestItemById } = useSearchTestStore();
|
||||
|
||||
const testHistories = useMemo(
|
||||
() => datasetTestList.filter((item) => item.datasetId === datasetId),
|
||||
[datasetId, datasetTestList]
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Flex alignItems={'center'} color={'myGray.600'}>
|
||||
<MyIcon mr={2} name={'history'} w={'16px'} h={'16px'} />
|
||||
<Box fontSize={'2xl'}>{t('core.dataset.test.test history')}</Box>
|
||||
</Flex>
|
||||
<Box mt={2}>
|
||||
<Flex py={2} fontWeight={'bold'} borderBottom={theme.borders.sm}>
|
||||
<Box flex={'0 0 80px'}>{t('core.dataset.search.search mode')}</Box>
|
||||
<Box flex={1}>{t('core.dataset.test.Test Text')}</Box>
|
||||
<Box flex={'0 0 70px'}>{t('common.Time')}</Box>
|
||||
<Box w={'14px'}></Box>
|
||||
</Flex>
|
||||
{testHistories.map((item) => (
|
||||
<Flex
|
||||
key={item.id}
|
||||
p={1}
|
||||
alignItems={'center'}
|
||||
borderBottom={theme.borders.base}
|
||||
_hover={{
|
||||
bg: '#f4f4f4',
|
||||
'& .delete': {
|
||||
display: 'block'
|
||||
}
|
||||
}}
|
||||
cursor={'pointer'}
|
||||
fontSize={'sm'}
|
||||
onClick={() => setDatasetTestItem(item)}
|
||||
>
|
||||
<Box flex={'0 0 80px'}>
|
||||
{DatasetSearchModeMap[item.searchMode] ? (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon
|
||||
name={DatasetSearchModeMap[item.searchMode].icon as any}
|
||||
w={'12px'}
|
||||
mr={'1px'}
|
||||
/>
|
||||
{t(DatasetSearchModeMap[item.searchMode].title)}
|
||||
</Flex>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</Box>
|
||||
<Box flex={1} mr={2} wordBreak={'break-all'}>
|
||||
{item.text}
|
||||
</Box>
|
||||
<Box flex={'0 0 70px'}>{formatTimeToChatTime(item.time)}</Box>
|
||||
<MyTooltip label={t('core.dataset.test.delete test history')}>
|
||||
<Box w={'14px'} h={'14px'}>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
display={'none'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
delDatasetTestItemById(item.id);
|
||||
datasetTestItem?.id === item.id && setDatasetTestItem(undefined);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
const TestResults = React.memo(function TestResults({
|
||||
datasetTestItem
|
||||
}: {
|
||||
datasetTestItem?: SearchTestStoreItemType;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
{!datasetTestItem?.results || datasetTestItem.results.length === 0 ? (
|
||||
<Flex
|
||||
mt={[10, 0]}
|
||||
h={'100%'}
|
||||
flexDirection={'column'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
>
|
||||
<MyIcon name={'empty'} color={'transparent'} w={'54px'} />
|
||||
<Box mt={3} color={'myGray.600'}>
|
||||
{t('core.dataset.test.test result placeholder')}
|
||||
</Box>
|
||||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
<Box fontSize={'xl'} color={'myGray.600'}>
|
||||
{t('core.dataset.test.Test params')}
|
||||
</Box>
|
||||
<TableContainer mb={3} bg={'myGray.150'} borderRadius={'md'}>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('core.dataset.search.search mode')}</Th>
|
||||
<Th>{t('core.dataset.search.ReRank')}</Th>
|
||||
<Th>{t('core.dataset.search.Max Tokens')}</Th>
|
||||
<Th>{t('core.dataset.search.Min Similarity')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<Tr>
|
||||
<Td>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon
|
||||
name={DatasetSearchModeMap[datasetTestItem.searchMode]?.icon as any}
|
||||
w={'12px'}
|
||||
mr={'1px'}
|
||||
/>
|
||||
{t(DatasetSearchModeMap[datasetTestItem.searchMode]?.title)}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>{datasetTestItem.usingReRank ? '✅' : '❌'}</Td>
|
||||
<Td>{datasetTestItem.limit}</Td>
|
||||
<Td>{datasetTestItem.similarity}</Td>
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box fontSize={'xl'} color={'myGray.600'}>
|
||||
{t('core.dataset.test.Test Result')}
|
||||
</Box>
|
||||
<MyTooltip label={t('core.dataset.test.test result tip')} forceShow>
|
||||
<QuestionOutlineIcon mx={2} color={'myGray.600'} cursor={'pointer'} fontSize={'lg'} />
|
||||
</MyTooltip>
|
||||
<Box>({datasetTestItem.duration})</Box>
|
||||
</Flex>
|
||||
<Grid
|
||||
mt={1}
|
||||
gridTemplateColumns={[
|
||||
'repeat(1,minmax(0, 1fr))',
|
||||
'repeat(1,minmax(0, 1fr))',
|
||||
'repeat(1,minmax(0, 1fr))',
|
||||
'repeat(1,minmax(0, 1fr))',
|
||||
'repeat(2,minmax(0, 1fr))'
|
||||
]}
|
||||
gridGap={4}
|
||||
>
|
||||
{datasetTestItem?.results.map((item, index) => (
|
||||
<Box
|
||||
key={item.id}
|
||||
p={2}
|
||||
borderRadius={'sm'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
>
|
||||
<QuoteItem quoteItem={item} canViewSource />
|
||||
</Box>
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
@@ -156,7 +156,7 @@ const Detail = ({ datasetId, currentTab }: { datasetId: string; currentTab: `${T
|
||||
borderRight={theme.borders.base}
|
||||
>
|
||||
<Flex mb={4} alignItems={'center'}>
|
||||
<Avatar src={datasetDetail.avatar} w={'34px'} borderRadius={'lg'} />
|
||||
<Avatar src={datasetDetail.avatar} w={'34px'} borderRadius={'md'} />
|
||||
<Box ml={2}>
|
||||
<Box fontWeight={'bold'}>{datasetDetail.name}</Box>
|
||||
</Box>
|
||||
|
@@ -211,7 +211,7 @@ const Kb = () => {
|
||||
borderWidth={1.5}
|
||||
borderColor={dragTargetId === dataset._id ? 'primary.600' : 'borderColor.low'}
|
||||
bg={'white'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
minH={'130px'}
|
||||
position={'relative'}
|
||||
data-drag-id={dataset.type === DatasetTypeEnum.folder ? dataset._id : undefined}
|
||||
@@ -390,7 +390,7 @@ const Kb = () => {
|
||||
/>
|
||||
)}
|
||||
<Flex alignItems={'center'} h={'38px'}>
|
||||
<Avatar src={dataset.avatar} borderRadius={'lg'} w={'28px'} />
|
||||
<Avatar src={dataset.avatar} borderRadius={'md'} w={'28px'} />
|
||||
<Box mx={3} className="textEllipsis3">
|
||||
{dataset.name}
|
||||
</Box>
|
||||
|
@@ -60,7 +60,7 @@ const MyModules = () => {
|
||||
borderWidth={'1.5px'}
|
||||
borderColor={'borderColor.low'}
|
||||
bg={'white'}
|
||||
borderRadius={'lg'}
|
||||
borderRadius={'md'}
|
||||
userSelect={'none'}
|
||||
position={'relative'}
|
||||
_hover={{
|
||||
|
Reference in New Issue
Block a user