perf: model data code

This commit is contained in:
archer
2023-04-24 09:50:56 +08:00
parent e0b1a78344
commit 29c95d24ae
6 changed files with 67 additions and 110 deletions

View File

@@ -1,11 +1,8 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const path = require('path');
const isDev = process.env.NODE_ENV === 'development';
const nextConfig = { const nextConfig = {
output: 'standalone', output: 'standalone',
reactStrictMode: false, reactStrictMode: true,
compress: true, compress: true,
webpack(config) { webpack(config) {
config.module.rules = config.module.rules.concat([ config.module.rules = config.module.rules.concat([

View File

@@ -1,5 +1,5 @@
import { GET, POST, DELETE, PUT } from './request'; import { GET, POST, DELETE, PUT } from './request';
import type { ModelSchema, ModelDataSchema, ModelSplitDataSchema } from '@/types/mongoSchema'; import type { ModelSchema, ModelDataSchema } from '@/types/mongoSchema';
import { ModelUpdateParams } from '@/types/model'; import { ModelUpdateParams } from '@/types/model';
import { TrainingItemType } from '../types/training'; import { TrainingItemType } from '../types/training';
import { RequestPaging } from '../types/index'; import { RequestPaging } from '../types/index';

View File

@@ -1,4 +1,4 @@
import type { ModelDataType, ModelSchema } from '@/types/mongoSchema'; import type { ModelSchema } from '@/types/mongoSchema';
export enum ModelDataStatusEnum { export enum ModelDataStatusEnum {
ready = 'ready', ready = 'ready',
@@ -120,7 +120,7 @@ export const ModelVectorSearchModeMap: Record<
export const defaultModel: ModelSchema = { export const defaultModel: ModelSchema = {
_id: '', _id: '',
userId: '', userId: '',
name: '', name: 'modelName',
avatar: '', avatar: '',
status: ModelStatusEnum.pending, status: ModelStatusEnum.pending,
updateTime: Date.now(), updateTime: Date.now(),

15
src/pages/_error.tsx Normal file
View File

@@ -0,0 +1,15 @@
function Error({ statusCode }: { statusCode: number }) {
return (
<p>
{statusCode ? `An error ${statusCode} occurred on server` : 'An error occurred on client'}
</p>
);
}
Error.getInitialProps = ({ res, err }: { res: any; err: any }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
console.log(err);
return { statusCode };
};
export default Error;

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState, useRef } from 'react';
import { import {
Box, Box,
TableContainer, TableContainer,
@@ -19,7 +19,6 @@ import {
Input Input
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
import type { ModelSchema } from '@/types/mongoSchema';
import type { ModelDataItemType } from '@/types/model'; import type { ModelDataItemType } from '@/types/model';
import { ModelDataStatusMap } from '@/constants/model'; import { ModelDataStatusMap } from '@/constants/model';
import { usePagination } from '@/hooks/usePagination'; import { usePagination } from '@/hooks/usePagination';
@@ -34,19 +33,23 @@ import { useLoading } from '@/hooks/useLoading';
import { fileDownload } from '@/utils/file'; import { fileDownload } from '@/utils/file';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useMutation, useQuery } from '@tanstack/react-query'; import { useMutation, useQuery } from '@tanstack/react-query';
import type { FormData as InputDataType } from './InputDataModal';
import Papa from 'papaparse'; import Papa from 'papaparse';
import InputModal, { FormData as InputDataType } from './InputDataModal';
const InputModel = dynamic(() => import('./InputDataModal')); const SelectFileModal = dynamic(() => import('./SelectFileModal'));
const SelectFileModel = dynamic(() => import('./SelectFileModal'));
const SelectUrlModel = dynamic(() => import('./SelectUrlModal'));
const SelectCsvModal = dynamic(() => import('./SelectCsvModal')); const SelectCsvModal = dynamic(() => import('./SelectCsvModal'));
let lastSearch = ''; const ModelDataCard = ({ modelId }: { modelId: string }) => {
const ModelDataCard = ({ model }: { model: ModelSchema }) => {
const { Loading, setIsLoading } = useLoading(); const { Loading, setIsLoading } = useLoading();
const lastSearch = useRef('');
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
const tdStyles = useRef<BoxProps>({
fontSize: 'xs',
maxW: '500px',
whiteSpace: 'pre-wrap',
maxH: '250px',
overflowY: 'auto'
});
const { const {
data: modelDataList, data: modelDataList,
isLoading, isLoading,
@@ -58,7 +61,7 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
api: getModelDataList, api: getModelDataList,
pageSize: 10, pageSize: 10,
params: { params: {
modelId: model._id, modelId,
searchText searchText
} }
}); });
@@ -70,19 +73,20 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
onOpen: onOpenSelectFileModal, onOpen: onOpenSelectFileModal,
onClose: onCloseSelectFileModal onClose: onCloseSelectFileModal
} = useDisclosure(); } = useDisclosure();
const {
isOpen: isOpenSelectUrlModal,
onOpen: onOpenSelectUrlModal,
onClose: onCloseSelectUrlModal
} = useDisclosure();
const { const {
isOpen: isOpenSelectCsvModal, isOpen: isOpenSelectCsvModal,
onOpen: onOpenSelectCsvModal, onOpen: onOpenSelectCsvModal,
onClose: onCloseSelectCsvModal onClose: onCloseSelectCsvModal
} = useDisclosure(); } = useDisclosure();
const { data: splitDataLen, refetch } = useQuery(['getModelSplitDataList'], () => const { data: splitDataLen = 0, refetch } = useQuery(
getModelSplitDataListLen(model._id) ['getModelSplitDataList'],
() => getModelSplitDataListLen(modelId),
{
onError(err) {
console.log(err);
}
}
); );
const refetchData = useCallback( const refetchData = useCallback(
@@ -94,8 +98,8 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
); );
// 获取所有的数据,并导出 json // 获取所有的数据,并导出 json
const { mutate: onclickExport, isLoading: isLoadingExport } = useMutation({ const { mutate: onclickExport, isLoading: isLoadingExport = false } = useMutation({
mutationFn: () => getExportDataList(model._id), mutationFn: () => getExportDataList(modelId),
onSuccess(res) { onSuccess(res) {
try { try {
setIsLoading(true); setIsLoading(true);
@@ -112,17 +116,12 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
error; error;
} }
setIsLoading(false); setIsLoading(false);
},
onError(err) {
console.log(err);
} }
}); });
const tdStyles: BoxProps = {
fontSize: 'xs',
maxW: '500px',
whiteSpace: 'pre-wrap',
maxH: '250px',
overflowY: 'auto'
};
return ( return (
<> <>
<Flex> <Flex>
@@ -150,7 +149,7 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
> >
</Button> </Button>
<Menu> <Menu autoSelect={false}>
<MenuButton as={Button} size={'sm'}> <MenuButton as={Button} size={'sm'}>
</MenuButton> </MenuButton>
@@ -166,17 +165,13 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
</MenuItem> </MenuItem>
<MenuItem onClick={onOpenSelectFileModal}>/</MenuItem> <MenuItem onClick={onOpenSelectFileModal}>/</MenuItem>
{/* <MenuItem onClick={onOpenSelectUrlModal}>网站内容拆分</MenuItem> */}
<MenuItem onClick={onOpenSelectCsvModal}>csv </MenuItem> <MenuItem onClick={onOpenSelectCsvModal}>csv </MenuItem>
</MenuList> </MenuList>
</Menu> </Menu>
</Flex> </Flex>
<Flex mt={4}> <Flex mt={4}>
{/* 拆分数据提示 */} {splitDataLen > 0 && <Box fontSize={'xs'}>{splitDataLen}...</Box>}
{!!(splitDataLen && splitDataLen > 0) && ( <Box flex={1} />
<Box fontSize={'xs'}>{splitDataLen}...</Box>
)}
<Box flex={1}></Box>
<Input <Input
maxW={'240px'} maxW={'240px'}
size={'sm'} size={'sm'}
@@ -184,15 +179,15 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
placeholder="搜索相关问题和答案,回车确认" placeholder="搜索相关问题和答案,回车确认"
onChange={(e) => setSearchText(e.target.value)} onChange={(e) => setSearchText(e.target.value)}
onBlur={() => { onBlur={() => {
if (searchText === lastSearch) return; if (searchText === lastSearch.current) return;
getData(1); getData(1);
lastSearch = searchText; lastSearch.current = searchText;
}} }}
onKeyDown={(e) => { onKeyDown={(e) => {
if (searchText === lastSearch) return; if (searchText === lastSearch.current) return;
if (e.key === 'Enter') { if (e.key === 'Enter') {
getData(1); getData(1);
lastSearch = searchText; lastSearch.current = searchText;
} }
}} }}
/> />
@@ -203,7 +198,7 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
<Table variant={'simple'} w={'100%'}> <Table variant={'simple'} w={'100%'}>
<Thead> <Thead>
<Tr> <Tr>
<Th>()</Th> <Th>{'匹配内容(问题)'}</Th>
<Th></Th> <Th></Th>
<Th></Th> <Th></Th>
<Th></Th> <Th></Th>
@@ -213,10 +208,10 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
{modelDataList.map((item) => ( {modelDataList.map((item) => (
<Tr key={item.id}> <Tr key={item.id}>
<Td> <Td>
<Box {...tdStyles}>{item.q}</Box> <Box {...tdStyles.current}>{item.q}</Box>
</Td> </Td>
<Td> <Td>
<Box {...tdStyles}>{item.a || '-'}</Box> <Box {...tdStyles.current}>{item.a || '-'}</Box>
</Td> </Td>
<Td>{ModelDataStatusMap[item.status]}</Td> <Td>{ModelDataStatusMap[item.status]}</Td>
<Td> <Td>
@@ -258,33 +253,22 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
<Loading loading={isLoading} fixed={false} /> <Loading loading={isLoading} fixed={false} />
{editInputData !== undefined && ( {editInputData !== undefined && (
<InputModel <InputModal
modelId={model._id} modelId={modelId}
defaultValues={editInputData} defaultValues={editInputData}
onClose={() => setEditInputData(undefined)} onClose={() => setEditInputData(undefined)}
onSuccess={refetchData} onSuccess={refetchData}
/> />
)} )}
{isOpenSelectFileModal && ( {isOpenSelectFileModal && (
<SelectFileModel <SelectFileModal
modelId={model._id} modelId={modelId}
onClose={onCloseSelectFileModal} onClose={onCloseSelectFileModal}
onSuccess={refetchData} onSuccess={refetchData}
/> />
)} )}
{isOpenSelectUrlModal && (
<SelectUrlModel
modelId={model._id}
onClose={onCloseSelectUrlModal}
onSuccess={refetchData}
/>
)}
{isOpenSelectCsvModal && ( {isOpenSelectCsvModal && (
<SelectCsvModal <SelectCsvModal modelId={modelId} onClose={onCloseSelectCsvModal} onSuccess={refetchData} />
modelId={model._id}
onClose={onCloseSelectCsvModal}
onSuccess={refetchData}
/>
)} )}
</> </>
); );

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useState, useRef, useMemo, useEffect } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { getModelById, delModelById, putModelTrainingStatus, putModelById } from '@/api/model'; import { getModelById, delModelById, putModelTrainingStatus, putModelById } from '@/api/model';
import type { ModelSchema } from '@/types/mongoSchema'; import type { ModelSchema } from '@/types/mongoSchema';
@@ -8,16 +8,16 @@ import { useForm } from 'react-hook-form';
import { formatModelStatus, ModelStatusEnum, modelList, defaultModel } from '@/constants/model'; import { formatModelStatus, ModelStatusEnum, modelList, defaultModel } from '@/constants/model';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import { useScreen } from '@/hooks/useScreen'; import { useScreen } from '@/hooks/useScreen';
import ModelEditForm from './components/ModelEditForm';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
const ModelEditForm = dynamic(() => import('./components/ModelEditForm'));
const ModelDataCard = dynamic(() => import('./components/ModelDataCard')); const ModelDataCard = dynamic(() => import('./components/ModelDataCard'));
const ModelDetail = ({ modelId }: { modelId: string }) => { const ModelDetail = ({ modelId }: { modelId: string }) => {
const { toast } = useToast(); const { toast } = useToast();
const router = useRouter(); const router = useRouter();
const { isPc, media } = useScreen(); const { isPc } = useScreen();
const { setLoading } = useGlobalStore(); const { setLoading } = useGlobalStore();
const [model, setModel] = useState<ModelSchema>(defaultModel); const [model, setModel] = useState<ModelSchema>(defaultModel);
@@ -76,36 +76,6 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
setLoading(false); setLoading(false);
}, [setLoading, router, modelId]); }, [setLoading, router, modelId]);
/* 上传数据集,触发微调 */
// const startTraining = useCallback(
// async (e: React.ChangeEvent<HTMLInputElement>) => {
// if (!modelId || !e.target.files || e.target.files?.length === 0) return;
// setLoading(true);
// try {
// const file = e.target.files[0];
// const formData = new FormData();
// formData.append('file', file);
// await postTrainModel(modelId, formData);
// toast({
// title: '开始训练...',
// status: 'success'
// });
// // 重新获取模型
// loadModel();
// } catch (err: any) {
// toast({
// title: err?.message || '上传文件失败',
// status: 'error'
// });
// console.log('error->', err);
// }
// setLoading(false);
// },
// [setLoading, loadModel, modelId, toast]
// );
/* 点击更新模型状态 */ /* 点击更新模型状态 */
const handleClickUpdateStatus = useCallback(async () => { const handleClickUpdateStatus = useCallback(async () => {
if (!model || model.status !== ModelStatusEnum.training) return; if (!model || model.status !== ModelStatusEnum.training) return;
@@ -236,21 +206,12 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
</> </>
)} )}
</Card> </Card>
<Grid mt={5} gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}> <Grid mt={5} gridTemplateColumns={['1fr', '1fr 1fr']} gridGap={5}>
<ModelEditForm formHooks={formHooks} handleDelModel={handleDelModel} canTrain={canTrain} /> <ModelEditForm formHooks={formHooks} handleDelModel={handleDelModel} canTrain={canTrain} />
{canTrain && model._id && ( {canTrain && !!model._id && (
<Card <Card p={4} gridColumnStart={[1, 1]} gridColumnEnd={[2, 3]}>
p={4} <ModelDataCard modelId={model._id} />
{...media(
{
gridColumnStart: 1,
gridColumnEnd: 3
},
{}
)}
>
<ModelDataCard model={model} />
</Card> </Card>
)} )}
</Grid> </Grid>