FastGPT/src/pages/model/detail/components/ModelEditForm.tsx
2023-05-03 15:50:31 +08:00

395 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useCallback } from 'react';
import {
Box,
Card,
Flex,
FormControl,
Input,
Textarea,
Slider,
SliderTrack,
SliderFilledTrack,
SliderThumb,
SliderMark,
Tooltip,
Button,
Select,
Grid,
Switch,
Image
} from '@chakra-ui/react';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import type { ModelSchema } from '@/types/mongoSchema';
import { UseFormReturn } from 'react-hook-form';
import { ChatModelMap, ModelVectorSearchModeMap, chatModelList } from '@/constants/model';
import { formatPrice } from '@/utils/user';
import { useConfirm } from '@/hooks/useConfirm';
import { useSelectFile } from '@/hooks/useSelectFile';
import { useToast } from '@/hooks/useToast';
import { fileToBase64 } from '@/utils/file';
const ModelEditForm = ({
formHooks,
isOwner,
handleDelModel
}: {
formHooks: UseFormReturn<ModelSchema>;
isOwner: boolean;
handleDelModel: () => void;
}) => {
const { openConfirm, ConfirmChild } = useConfirm({
content: '确认删除该模型?'
});
const { register, setValue, getValues } = formHooks;
const [refresh, setRefresh] = useState(false);
const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png',
multiple: false
});
const { toast } = useToast();
const onSelectFile = useCallback(
async (e: File[]) => {
const file = e[0];
if (!file) return;
if (file.size > 100 * 1024) {
return toast({
title: '头像需小于 100kb',
status: 'warning'
});
}
const base64 = (await fileToBase64(file)) as string;
setValue('avatar', base64);
setRefresh((state) => !state);
},
[setValue, toast]
);
return (
<>
<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}>
:
</Box>
<Image
src={getValues('avatar') || '/icon/logo.png'}
alt={'avatar'}
w={['28px', '36px']}
h={['28px', '36px']}
objectFit={'cover'}
cursor={isOwner ? 'pointer' : 'default'}
title={'点击切换头像'}
onClick={() => isOwner && onOpenSelectFile()}
/>
</Flex>
<FormControl mt={4}>
<Flex alignItems={'center'}>
<Box flex={'0 0 80px'} w={0}>
:
</Box>
<Input
isDisabled={!isOwner}
{...register('name', {
required: '展示名称不能为空'
})}
></Input>
</Flex>
</FormControl>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 80px'} w={0}>
:
</Box>
<Select isDisabled={!isOwner} {...register('chat.chatModel')}>
{chatModelList.map((item) => (
<option key={item.chatModel} value={item.chatModel}>
{item.name}
</option>
))}
</Select>
</Flex>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 80px'} w={0}>
:
</Box>
<Box>
{formatPrice(ChatModelMap[getValues('chat.chatModel')]?.price, 1000)}
/1K tokens()
</Box>
</Flex>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 80px'} w={0}>
:
</Box>
<Box>{getValues('share.collection')}</Box>
</Flex>
{isOwner && (
<Flex mt={5} alignItems={'center'}>
<Box flex={'0 0 150px'}></Box>
<Button
colorScheme={'gray'}
variant={'outline'}
size={'sm'}
onClick={openConfirm(handleDelModel)}
>
</Button>
</Flex>
)}
</Card>
<Card p={4}>
<Box fontWeight={'bold'}></Box>
<FormControl mt={4}>
<Flex alignItems={'center'}>
<Box flex={'0 0 80px'} w={0}>
<Box as={'span'} mr={2}>
</Box>
<Tooltip label={'温度越高,模型的发散能力越强;温度越低,内容越严谨。'}>
<QuestionOutlineIcon />
</Tooltip>
</Box>
<Slider
aria-label="slider-ex-1"
min={0}
max={10}
step={1}
value={getValues('chat.temperature')}
isDisabled={!isOwner}
onChange={(e) => {
setValue('chat.temperature', e);
setRefresh(!refresh);
}}
>
<SliderMark
value={getValues('chat.temperature')}
textAlign="center"
bg="blue.500"
color="white"
w={'18px'}
h={'18px'}
borderRadius={'100px'}
fontSize={'xs'}
transform={'translate(-50%, -200%)'}
>
{getValues('chat.temperature')}
</SliderMark>
<SliderTrack>
<SliderFilledTrack />
</SliderTrack>
<SliderThumb />
</Slider>
</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={'模型默认的 prompt 词,通过调整该内容,可以引导模型聊天方向。'}
{...register('chat.systemPrompt')}
/>
</Box>
</Card>
{isOwner && (
<Card p={4} gridColumnStart={[1, 1]} gridColumnEnd={[2, 3]}>
<Box fontWeight={'bold'}></Box>
<Grid gridTemplateColumns={['1fr', '1fr 410px']} gridGap={5}>
<Box>
<Flex mt={5} alignItems={'center'}>
<Box mr={3}>:</Box>
<Switch
isChecked={getValues('share.isShare')}
onChange={() => {
setValue('share.isShare', !getValues('share.isShare'));
setRefresh(!refresh);
}}
/>
<Box ml={12} mr={3}>
:
</Box>
<Switch
isChecked={getValues('share.isShareDetail')}
onChange={() => {
setValue('share.isShareDetail', !getValues('share.isShareDetail'));
setRefresh(!refresh);
}}
/>
</Flex>
<Box mt={5}>
<Box></Box>
<Textarea
mt={1}
rows={6}
maxLength={150}
{...register('share.intro')}
placeholder={'介绍模型的功能、场景等吸引更多人来使用最多150字。'}
/>
</Box>
</Box>
<Box
textAlign={'justify'}
fontSize={'sm'}
border={'1px solid #f4f4f4'}
borderRadius={'sm'}
p={3}
>
<Box fontWeight={'bold'}>Tips</Box>
<Box mt={1} as={'ul'} pl={4}>
<li>
FastGpt
使使 tokens使 tokens
</li>
<li></li>
</Box>
</Box>
</Grid>
</Card>
)}
<File onSelect={onSelectFile} />
{/* <Card p={4}>
<Box fontWeight={'bold'}>安全策略</Box>
<FormControl mt={2}>
<Flex alignItems={'center'}>
<Box flex={'0 0 120px'} w={0}>
单句最大长度:
</Box>
<Input
flex={1}
type={'number'}
{...register('security.contentMaxLen', {
required: '单句长度不能为空',
min: {
value: 0,
message: '单句长度最小为0'
},
max: {
value: 4000,
message: '单句长度最长为4000'
},
valueAsNumber: true
})}
></Input>
</Flex>
</FormControl>
<FormControl mt={5}>
<Flex alignItems={'center'}>
<Box flex={'0 0 120px'} w={0}>
上下文最大长度:
</Box>
<Input
flex={1}
type={'number'}
{...register('security.contextMaxLen', {
required: '上下文长度不能为空',
min: {
value: 1,
message: '上下文长度最小为5'
},
max: {
value: 400000,
message: '上下文长度最长为 400000'
},
valueAsNumber: true
})}
></Input>
</Flex>
</FormControl>
<FormControl mt={5}>
<Flex alignItems={'center'}>
<Box flex={'0 0 120px'} w={0}>
聊天过期时间:
</Box>
<Input
flex={1}
type={'number'}
{...register('security.expiredTime', {
required: '聊天过期时间不能为空',
min: {
value: 0.1,
message: '聊天过期时间最小为0.1小时'
},
max: {
value: 999999,
message: '聊天过期时间最长为 999999 小时'
},
valueAsNumber: true
})}
></Input>
<Box ml={3}>小时</Box>
</Flex>
</FormControl>
<FormControl mt={5} pb={5}>
<Flex alignItems={'center'}>
<Box flex={'0 0 130px'} w={0}>
聊天最大加载次数:
</Box>
<Box flex={1}>
<Input
type={'number'}
{...register('security.maxLoadAmount', {
required: '聊天最大加载次数不能为空',
max: {
value: 999999,
message: '聊天最大加载次数最小为 999999 次'
},
valueAsNumber: true
})}
></Input>
<Box fontSize={'sm'} color={'blackAlpha.400'} position={'absolute'}>
设置为-1代表不限制次数
</Box>
</Box>
<Box ml={3}>次</Box>
</Flex>
</FormControl>
</Card> */}
<ConfirmChild />
</>
);
};
export default ModelEditForm;