perf: model framwork

This commit is contained in:
archer
2023-04-29 15:55:47 +08:00
parent cd9acab938
commit 78762498eb
30 changed files with 649 additions and 757 deletions

View File

@@ -45,9 +45,10 @@ const ModelDataCard = ({ modelId, isOwner }: { modelId: string; isOwner: boolean
const [searchText, setSearchText] = useState('');
const tdStyles = useRef<BoxProps>({
fontSize: 'xs',
minW: '150px',
maxW: '500px',
whiteSpace: 'pre-wrap',
maxH: '250px',
whiteSpace: 'pre-wrap',
overflowY: 'auto'
});
const {
@@ -132,7 +133,7 @@ const ModelDataCard = ({ modelId, isOwner }: { modelId: string; isOwner: boolean
<>
<Flex>
<Box fontWeight={'bold'} fontSize={'lg'} flex={1} mr={2}>
: {total}
: {total}
</Box>
{isOwner && (
<>

View File

@@ -21,7 +21,7 @@ import {
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import type { ModelSchema } from '@/types/mongoSchema';
import { UseFormReturn } from 'react-hook-form';
import { modelList, ModelVectorSearchModeMap } from '@/constants/model';
import { ChatModelMap, modelList, ModelVectorSearchModeMap } from '@/constants/model';
import { formatPrice } from '@/utils/user';
import { useConfirm } from '@/hooks/useConfirm';
import { useSelectFile } from '@/hooks/useSelectFile';
@@ -30,12 +30,10 @@ import { fileToBase64 } from '@/utils/file';
const ModelEditForm = ({
formHooks,
canTrain,
isOwner,
handleDelModel
}: {
formHooks: UseFormReturn<ModelSchema>;
canTrain: boolean;
isOwner: boolean;
handleDelModel: () => void;
}) => {
@@ -73,6 +71,12 @@ const ModelEditForm = ({
<>
<Card p={4}>
<Box fontWeight={'bold'}></Box>
<Flex alignItems={'center'} mt={4}>
<Box flex={'0 0 80px'} w={0}>
modelId:
</Box>
<Box>{getValues('_id')}</Box>
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={'0 0 80px'} w={0}>
:
@@ -101,17 +105,12 @@ const ModelEditForm = ({
></Input>
</Flex>
</FormControl>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 80px'} w={0}>
modelId:
:
</Box>
<Box>{getValues('_id')}</Box>
</Flex>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 80px'} w={0}>
:
</Box>
<Box>{modelList.find((item) => item.model === getValues('service.modelName'))?.name}</Box>
<Box>{ChatModelMap[getValues('chat.chatModel')]}</Box>
</Flex>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 80px'} w={0}>
@@ -119,7 +118,7 @@ const ModelEditForm = ({
</Box>
<Box>
{formatPrice(
modelList.find((item) => item.model === getValues('service.modelName'))?.price || 0,
modelList.find((item) => item.chatModel === getValues('chat.chatModel'))?.price || 0,
1000
)}
/1K tokens()
@@ -163,15 +162,15 @@ const ModelEditForm = ({
min={0}
max={10}
step={1}
value={getValues('temperature')}
value={getValues('chat.temperature')}
isDisabled={!isOwner}
onChange={(e) => {
setValue('temperature', e);
setValue('chat.temperature', e);
setRefresh(!refresh);
}}
>
<SliderMark
value={getValues('temperature')}
value={getValues('chat.temperature')}
textAlign="center"
bg="blue.500"
color="white"
@@ -181,7 +180,7 @@ const ModelEditForm = ({
fontSize={'xs'}
transform={'translate(-50%, -200%)'}
>
{getValues('temperature')}
{getValues('chat.temperature')}
</SliderMark>
<SliderTrack>
<SliderFilledTrack />
@@ -190,35 +189,42 @@ const ModelEditForm = ({
</Slider>
</Flex>
</FormControl>
{canTrain && (
<FormControl mt={4}>
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}></Box>
<Select
isDisabled={!isOwner}
{...register('search.mode', { required: '搜索模式不能为空' })}
>
{Object.entries(ModelVectorSearchModeMap).map(([key, { text }]) => (
<option key={key} value={key}>
{text}
</option>
))}
</Select>
</Flex>
</FormControl>
<Flex mt={4} alignItems={'center'}>
<Box mr={4}></Box>
<Switch
isChecked={getValues('chat.useKb')}
onChange={() => {
setValue('chat.useKb', !getValues('chat.useKb'));
setRefresh(!refresh);
}}
/>
</Flex>
{getValues('chat.useKb') && (
<Flex mt={4} alignItems={'center'}>
<Box mr={4} whiteSpace={'nowrap'}>
&emsp;
</Box>
<Select
isDisabled={!isOwner}
{...register('chat.searchMode', { required: '搜索模式不能为空' })}
>
{Object.entries(ModelVectorSearchModeMap).map(([key, { text }]) => (
<option key={key} value={key}>
{text}
</option>
))}
</Select>
</Flex>
)}
<Box mt={4}>
<Box mb={1}></Box>
<Textarea
rows={8}
maxLength={-1}
isDisabled={!isOwner}
placeholder={
canTrain
? '训练的模型会根据知识库内容,生成一部分系统提示词,因此在对话时需要消耗更多的 tokens。你可以增加提示词让效果更符合预期。例如: \n1. 请根据知识库内容回答用户问题。\n2. 知识库是电影《铃芽之旅》的内容,根据知识库内容回答。无关问题,拒绝回复!'
: '模型默认的 prompt 词,通过调整该内容,可以生成一个限定范围的模型。\n注意改功能会影响对话的整体朝向'
}
{...register('systemPrompt')}
placeholder={'模型默认的 prompt 词,通过调整该内容,可以引导模型聊天方向。'}
{...register('chat.systemPrompt')}
/>
</Box>
</Card>

View File

@@ -27,11 +27,6 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
defaultValues: model
});
const canTrain = useMemo(() => {
const openai = modelList.find((item) => item.model === model?.service.modelName);
return !!(openai && openai.trainName);
}, [model]);
const isOwner = useMemo(() => model.userId === userInfo?._id, [model.userId, userInfo?._id]);
/* 加载模型数据 */
@@ -86,11 +81,8 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
await putModelById(data._id, {
name: data.name,
avatar: data.avatar || '/icon/logo.png',
systemPrompt: data.systemPrompt,
temperature: data.temperature,
search: data.search,
chat: data.chat,
share: data.share,
service: data.service,
security: data.security
});
toast({
@@ -171,11 +163,15 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
</Tag>
</Flex>
<Box mt={4} textAlign={'right'}>
<Button variant={'outline'} onClick={handlePreviewChat}>
<Button variant={'outline'} size={'sm'} onClick={handlePreviewChat}>
</Button>
{isOwner && (
<Button ml={4} onClick={formHooks.handleSubmit(saveSubmitSuccess, saveSubmitError)}>
<Button
ml={4}
size={'sm'}
onClick={formHooks.handleSubmit(saveSubmitSuccess, saveSubmitError)}
>
</Button>
)}
@@ -184,16 +180,11 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
)}
</Card>
<Grid mt={5} gridTemplateColumns={['1fr', '1fr 1fr']} gridGap={5}>
<ModelEditForm
formHooks={formHooks}
handleDelModel={handleDelModel}
canTrain={canTrain}
isOwner={isOwner}
/>
<ModelEditForm formHooks={formHooks} handleDelModel={handleDelModel} isOwner={isOwner} />
{canTrain && !!model._id && (
{modelId && (
<Card p={4} gridColumnStart={[1, 1]} gridColumnEnd={[2, 3]}>
<ModelDataCard modelId={model._id} isOwner={isOwner} />
<ModelDataCard modelId={modelId} isOwner={isOwner} />
</Card>
)}
</Grid>

View File

@@ -1,138 +0,0 @@
import React, { Dispatch, useState, useCallback, useMemo } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
FormControl,
FormErrorMessage,
Button,
useToast,
Input,
Select,
Box
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { postCreateModel } from '@/api/model';
import type { ModelSchema } from '@/types/mongoSchema';
import { modelList } from '@/constants/model';
import { formatPrice } from '@/utils/user';
interface CreateFormType {
name: string;
serviceModelName: string;
}
const CreateModel = ({
setCreateModelOpen,
onSuccess
}: {
setCreateModelOpen: Dispatch<boolean>;
onSuccess: Dispatch<ModelSchema>;
}) => {
const [requesting, setRequesting] = useState(false);
const [refresh, setRefresh] = useState(false);
const toast = useToast({
duration: 2000,
position: 'top'
});
const {
getValues,
register,
handleSubmit,
formState: { errors }
} = useForm<CreateFormType>({
defaultValues: {
serviceModelName: modelList[0].model
}
});
const handleCreateModel = useCallback(
async (data: CreateFormType) => {
setRequesting(true);
try {
const res = await postCreateModel(data);
toast({
title: '创建成功',
status: 'success'
});
onSuccess(res);
setCreateModelOpen(false);
} catch (err: any) {
toast({
title: typeof err === 'string' ? err : err.message || '出现了意外',
status: 'error'
});
}
setRequesting(false);
},
[onSuccess, setCreateModelOpen, toast]
);
return (
<>
<Modal isOpen={true} onClose={() => setCreateModelOpen(false)}>
<ModalOverlay />
<ModalContent>
<ModalHeader></ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl mb={8} isInvalid={!!errors.name}>
<Input
placeholder="模型名称"
{...register('name', {
required: '模型名不能为空'
})}
/>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.name && errors.name.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={!!errors.serviceModelName}>
<Select
placeholder="选择基础模型类型"
{...register('serviceModelName', {
required: '底层模型不能为空',
onChange() {
setRefresh(!refresh);
}
})}
>
{modelList.map((item) => (
<option key={item.model} value={item.model}>
{item.name}
</option>
))}
</Select>
<FormErrorMessage position={'absolute'} fontSize="xs">
{!!errors.serviceModelName && errors.serviceModelName.message}
</FormErrorMessage>
</FormControl>
<Box mt={3} textAlign={'center'} fontSize={'sm'} color={'blackAlpha.600'}>
{formatPrice(
modelList.find((item) => item.model === getValues('serviceModelName'))?.price || 0,
1000
)}
/1K tokens()
</Box>
</ModalBody>
<ModalFooter>
<Button mr={3} colorScheme={'gray'} onClick={() => setCreateModelOpen(false)}>
</Button>
<Button isLoading={requesting} onClick={handleSubmit(handleCreateModel)}>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default CreateModel;

View File

@@ -2,8 +2,8 @@ import React, { useEffect } from 'react';
import { Box, Button, Flex, Tag } from '@chakra-ui/react';
import type { ModelSchema } from '@/types/mongoSchema';
import { formatModelStatus } from '@/constants/model';
import dayjs from 'dayjs';
import { useRouter } from 'next/router';
import { ChatModelMap } from '@/constants/model';
const ModelPhoneList = ({
models,
@@ -42,12 +42,12 @@ const ModelPhoneList = ({
</Tag>
</Flex>
<Flex mt={5}>
<Box flex={'0 0 100px'}>: </Box>
<Box color={'blackAlpha.500'}>{dayjs(model.updateTime).format('YYYY-MM-DD HH:mm')}</Box>
<Box flex={'0 0 100px'}>: </Box>
<Box color={'blackAlpha.500'}>{ChatModelMap[model.chat.chatModel]}</Box>
</Flex>
<Flex mt={5}>
<Box flex={'0 0 100px'}>AI模型: </Box>
<Box color={'blackAlpha.500'}>{model.service.modelName}</Box>
<Box flex={'0 0 100px'}>: </Box>
<Box color={'blackAlpha.500'}>{model.chat.temperature}</Box>
</Flex>
<Flex mt={5} justifyContent={'flex-end'}>
<Button

View File

@@ -13,10 +13,9 @@ import {
Box
} from '@chakra-ui/react';
import { formatModelStatus } from '@/constants/model';
import dayjs from 'dayjs';
import type { ModelSchema } from '@/types/mongoSchema';
import { useRouter } from 'next/router';
import { modelList } from '@/constants/model';
import { ChatModelMap } from '@/constants/model';
const ModelTable = ({
models = [],
@@ -33,18 +32,18 @@ const ModelTable = ({
dataIndex: 'name'
},
{
title: '模型类型',
title: '对话模型',
key: 'service',
render: (model: ModelSchema) => (
<Box fontWeight={'bold'} whiteSpace={'pre-wrap'} maxW={'200px'}>
{modelList.find((item) => item.model === model.service.modelName)?.name}
{ChatModelMap[model.chat.chatModel]}
</Box>
)
},
{
title: '最后更新时间',
key: 'updateTime',
render: (item: ModelSchema) => dayjs(item.updateTime).format('YYYY-MM-DD HH:mm')
title: '温度',
key: 'temperature',
render: (model: ModelSchema) => <>{model.chat.temperature}</>
},
{
title: '状态',

View File

@@ -1,4 +1,4 @@
import React, { useState, useCallback } from 'react';
import React, { useCallback } from 'react';
import { Box, Button, Flex, Card } from '@chakra-ui/react';
import type { ModelSchema } from '@/types/mongoSchema';
import { useRouter } from 'next/router';
@@ -7,30 +7,37 @@ import ModelPhoneList from './components/ModelPhoneList';
import { useScreen } from '@/hooks/useScreen';
import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading';
import dynamic from 'next/dynamic';
import { useToast } from '@/hooks/useToast';
import { useUserStore } from '@/store/user';
const CreateModel = dynamic(() => import('./components/CreateModel'));
import { postCreateModel } from '@/api/model';
const modelList = () => {
const { toast } = useToast();
const { isPc } = useScreen();
const router = useRouter();
const { myModels, setMyModels, getMyModels } = useUserStore();
const [openCreateModel, setOpenCreateModel] = useState(false);
const { myModels, getMyModels } = useUserStore();
const { Loading, setIsLoading } = useLoading();
/* 加载模型 */
const { isLoading } = useQuery(['loadModels'], getMyModels);
/* 创建成功回调 */
const createModelSuccess = useCallback(
(data: ModelSchema) => {
setMyModels([data, ...myModels]);
},
[myModels, setMyModels]
);
const handleCreateModel = useCallback(async () => {
setIsLoading(true);
try {
const id = await postCreateModel({ name: `模型${myModels.length}` });
toast({
title: '创建成功',
status: 'success'
});
router.push(`/model/detail?modelId=${id}`);
} catch (err: any) {
toast({
title: typeof err === 'string' ? err : err.message || '出现了意外',
status: 'error'
});
}
setIsLoading(false);
}, [myModels.length, router, setIsLoading, toast]);
/* 点前往聊天预览页 */
const handlePreviewChat = useCallback(
@@ -61,7 +68,7 @@ const modelList = () => {
</Box>
<Button flex={'0 0 145px'} variant={'outline'} onClick={() => setOpenCreateModel(true)}>
<Button flex={'0 0 145px'} variant={'outline'} onClick={handleCreateModel}>
</Button>
</Flex>
@@ -74,10 +81,6 @@ const modelList = () => {
<ModelPhoneList models={myModels} handlePreviewChat={handlePreviewChat} />
)}
</Box>
{/* 创建弹窗 */}
{openCreateModel && (
<CreateModel setCreateModelOpen={setOpenCreateModel} onSuccess={createModelSuccess} />
)}
<Loading loading={isLoading} />
</Box>