feat: config vector model and qa model
This commit is contained in:
@@ -68,7 +68,6 @@ const ChunkImport = ({ kbId }: { kbId: string }) => {
|
||||
for (let i = 0; i < chunks.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
model,
|
||||
data: chunks.slice(i, i + step),
|
||||
mode: TrainingModeEnum.index
|
||||
});
|
||||
|
||||
@@ -43,7 +43,6 @@ const CsvImport = ({ kbId }: { kbId: string }) => {
|
||||
for (let i = 0; i < chunks.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
model,
|
||||
data: chunks.slice(i, i + step),
|
||||
mode: TrainingModeEnum.index
|
||||
});
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Box, type BoxProps, Flex, Textarea, useTheme, Button } from '@chakra-ui/react';
|
||||
import MyRadio from '@/components/Radio/index';
|
||||
import React from 'react';
|
||||
import { Box, Textarea, Button } from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useRequest } from '@/hooks/useRequest';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { vectorModelList } from '@/store/static';
|
||||
import { postKbDataFromList } from '@/api/plugins/kb';
|
||||
import { TrainingModeEnum } from '@/constants/plugin';
|
||||
|
||||
@@ -35,7 +33,6 @@ const ManualImport = ({ kbId }: { kbId: string }) => {
|
||||
};
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
model: vectorModelList[0].model,
|
||||
mode: TrainingModeEnum.index,
|
||||
data: [data]
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ import { postKbDataFromList } from '@/api/plugins/kb';
|
||||
import { splitText2Chunks } from '@/utils/file';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { formatPrice } from '@/utils/user';
|
||||
import { qaModelList } from '@/store/static';
|
||||
import { qaModel } from '@/store/static';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import CloseIcon from '@/components/Icon/close';
|
||||
import DeleteIcon, { hoverDeleteStyles } from '@/components/Icon/delete';
|
||||
@@ -20,9 +20,8 @@ import { useRouter } from 'next/router';
|
||||
const fileExtension = '.txt, .doc, .docx, .pdf, .md';
|
||||
|
||||
const QAImport = ({ kbId }: { kbId: string }) => {
|
||||
const model = qaModelList[0]?.model;
|
||||
const unitPrice = qaModelList[0]?.price || 3;
|
||||
const chunkLen = qaModelList[0].maxToken * 0.45;
|
||||
const unitPrice = qaModel.price || 3;
|
||||
const chunkLen = qaModel.maxToken * 0.45;
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
@@ -58,7 +57,6 @@ const QAImport = ({ kbId }: { kbId: string }) => {
|
||||
for (let i = 0; i < chunks.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
model,
|
||||
data: chunks.slice(i, i + step),
|
||||
mode: TrainingModeEnum.qa,
|
||||
prompt: prompt || '下面是一段长文本'
|
||||
|
||||
@@ -7,7 +7,7 @@ import React, {
|
||||
ForwardedRef
|
||||
} from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Box, Flex, Button, FormControl, IconButton, Input, Card } from '@chakra-ui/react';
|
||||
import { Box, Flex, Button, FormControl, IconButton, Input } from '@chakra-ui/react';
|
||||
import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons';
|
||||
import { delKbById, putKbById } from '@/api/plugins/kb';
|
||||
import { useSelectFile } from '@/hooks/useSelectFile';
|
||||
@@ -17,8 +17,6 @@ import { useConfirm } from '@/hooks/useConfirm';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { compressImg } from '@/utils/file';
|
||||
import type { KbItemType } from '@/types/plugin';
|
||||
import { vectorModelList } from '@/store/static';
|
||||
import MySelect from '@/components/Select';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import Tag from '@/components/Tag';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
@@ -138,7 +136,6 @@ const Info = (
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
initInput: (tags: string) => {
|
||||
console.log(tags);
|
||||
if (InputRef.current) {
|
||||
InputRef.current.value = tags;
|
||||
}
|
||||
@@ -153,20 +150,27 @@ const Info = (
|
||||
</Box>
|
||||
<Box flex={1}>{kbDetail._id}</Box>
|
||||
</Flex>
|
||||
<Flex mt={8} w={'100%'} alignItems={'center'}>
|
||||
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
|
||||
索引模型
|
||||
</Box>
|
||||
<Box flex={[1, '0 0 300px']}>{getValues('vectorModelName')}</Box>
|
||||
</Flex>
|
||||
<Flex mt={5} w={'100%'} alignItems={'center'}>
|
||||
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
|
||||
知识库头像
|
||||
</Box>
|
||||
<Box flex={[1, '0 0 300px']}>
|
||||
<Avatar
|
||||
m={'auto'}
|
||||
src={getValues('avatar')}
|
||||
w={['32px', '40px']}
|
||||
h={['32px', '40px']}
|
||||
cursor={'pointer'}
|
||||
title={'点击切换头像'}
|
||||
onClick={onOpenSelectFile}
|
||||
/>
|
||||
<MyTooltip label={'点击切换头像'}>
|
||||
<Avatar
|
||||
m={'auto'}
|
||||
src={getValues('avatar')}
|
||||
w={['32px', '40px']}
|
||||
h={['32px', '40px']}
|
||||
cursor={'pointer'}
|
||||
onClick={onOpenSelectFile}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
</Flex>
|
||||
<FormControl mt={8} w={'100%'} display={'flex'} alignItems={'center'}>
|
||||
@@ -180,27 +184,9 @@ const Info = (
|
||||
})}
|
||||
/>
|
||||
</FormControl>
|
||||
<Flex mt={8} w={'100%'} alignItems={'center'}>
|
||||
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
|
||||
索引模型
|
||||
</Box>
|
||||
<Box flex={[1, '0 0 300px']}>
|
||||
<MySelect
|
||||
w={'100%'}
|
||||
value={getValues('model')}
|
||||
list={vectorModelList.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.model
|
||||
}))}
|
||||
onchange={(res) => {
|
||||
setValue('model', res);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={8} alignItems={'center'} w={'100%'} flexWrap={'wrap'}>
|
||||
<Box flex={['0 0 90px', '0 0 160px']} w={0}>
|
||||
分类标签
|
||||
标签
|
||||
<MyTooltip label={'用空格隔开多个标签,便于搜索'} forceShow>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
@@ -208,6 +194,7 @@ const Info = (
|
||||
<Input
|
||||
flex={[1, '0 0 300px']}
|
||||
ref={InputRef}
|
||||
defaultValue={getValues('tags')}
|
||||
placeholder={'标签,使用空格分割。'}
|
||||
maxLength={30}
|
||||
onChange={(e) => {
|
||||
@@ -226,7 +213,6 @@ const Info = (
|
||||
))}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex mt={5} w={'100%'} alignItems={'flex-end'}>
|
||||
<Box flex={['0 0 90px', '0 0 160px']} w={0}></Box>
|
||||
<Button
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Box, Flex, IconButton, useTheme } from '@chakra-ui/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
@@ -71,8 +71,8 @@ const Detail = ({ kbId, currentTab }: { kbId: string; currentTab: `${TabEnum}` }
|
||||
|
||||
useQuery([kbId], () => getKbDetail(kbId), {
|
||||
onSuccess(res) {
|
||||
InfoRef.current?.initInput(res.tags);
|
||||
form.reset(res);
|
||||
InfoRef.current?.initInput(res.tags);
|
||||
},
|
||||
onError(err: any) {
|
||||
router.replace(`/kb/list`);
|
||||
|
||||
165
client/src/pages/kb/list/component/CreateModal.tsx
Normal file
165
client/src/pages/kb/list/component/CreateModal.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import React, { useCallback, useState, useRef } from 'react';
|
||||
import { Box, Flex, Button, ModalHeader, ModalFooter, ModalBody, Input } from '@chakra-ui/react';
|
||||
import { useSelectFile } from '@/hooks/useSelectFile';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { compressImg } from '@/utils/file';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { useRequest } from '@/hooks/useRequest';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { postCreateKb } from '@/api/plugins/kb';
|
||||
import type { CreateKbParams } from '@/api/request/kb';
|
||||
import { vectorModelList } from '@/store/static';
|
||||
import MySelect from '@/components/Select';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import Tag from '@/components/Tag';
|
||||
|
||||
const CreateModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const { isPc } = useGlobalStore();
|
||||
const { register, setValue, getValues, handleSubmit } = useForm<CreateKbParams>({
|
||||
defaultValues: {
|
||||
avatar: '/icon/logo.svg',
|
||||
name: '',
|
||||
tags: [],
|
||||
vectorModel: vectorModelList[0].model
|
||||
}
|
||||
});
|
||||
const InputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
const onSelectFile = useCallback(
|
||||
async (e: File[]) => {
|
||||
const file = e[0];
|
||||
if (!file) return;
|
||||
try {
|
||||
const src = await compressImg({
|
||||
file,
|
||||
maxW: 100,
|
||||
maxH: 100
|
||||
});
|
||||
setValue('avatar', src);
|
||||
setRefresh((state) => !state);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: getErrText(err, '头像选择异常'),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
},
|
||||
[setValue, toast]
|
||||
);
|
||||
|
||||
/* create a new kb and router to it */
|
||||
const { mutate: onclickCreate, isLoading: creating } = useRequest({
|
||||
mutationFn: async (data: CreateKbParams) => {
|
||||
const id = await postCreateKb(data);
|
||||
return id;
|
||||
},
|
||||
successToast: '创建成功',
|
||||
errorToast: '创建知识库出现意外',
|
||||
onSuccess(id) {
|
||||
router.push(`/kb/detail?kbId=${id}`);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal isOpen onClose={onClose} isCentered={!isPc} w={'400px'}>
|
||||
<ModalHeader fontSize={'2xl'}>创建一个知识库</ModalHeader>
|
||||
<ModalBody>
|
||||
<Box color={'myGray.800'} fontWeight={'bold'}>
|
||||
取个名字
|
||||
</Box>
|
||||
<Flex mt={3} alignItems={'center'}>
|
||||
<MyTooltip label={'点击设置头像'}>
|
||||
<Avatar
|
||||
flexShrink={0}
|
||||
src={getValues('avatar')}
|
||||
w={['28px', '32px']}
|
||||
h={['28px', '32px']}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
onClick={onOpenSelectFile}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Input
|
||||
ml={3}
|
||||
flex={1}
|
||||
autoFocus
|
||||
bg={'myWhite.600'}
|
||||
{...register('name', {
|
||||
required: '知识库名称不能为空~'
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={6} alignItems={'center'}>
|
||||
<Box flex={'0 0 80px'}>索引模型</Box>
|
||||
<Box flex={1}>
|
||||
<MySelect
|
||||
w={'100%'}
|
||||
value={getValues('vectorModel')}
|
||||
list={vectorModelList.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.model
|
||||
}))}
|
||||
onchange={(e) => {
|
||||
setValue('vectorModel', e);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={6} alignItems={'center'} w={'100%'}>
|
||||
<Box flex={'0 0 80px'}>
|
||||
标签
|
||||
<MyTooltip label={'用空格隔开多个标签,便于搜索'} forceShow>
|
||||
<QuestionOutlineIcon ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Input
|
||||
flex={1}
|
||||
ref={InputRef}
|
||||
placeholder={'标签,使用空格分割。'}
|
||||
maxLength={30}
|
||||
onChange={(e) => {
|
||||
setValue('tags', e.target.value.split(' '));
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={2} flexWrap={'wrap'}>
|
||||
{getValues('tags')
|
||||
.filter((item) => item)
|
||||
.map((item, i) => (
|
||||
<Tag mr={2} mb={2} key={i} whiteSpace={'nowrap'}>
|
||||
{item}
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'base'} mr={3} onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}>
|
||||
确认创建
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<File onSelect={onSelectFile} />
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateModal;
|
||||
@@ -1,5 +1,14 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Box, Card, Flex, Grid, useTheme, Button, IconButton } from '@chakra-ui/react';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
Flex,
|
||||
Grid,
|
||||
useTheme,
|
||||
Button,
|
||||
IconButton,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import PageContainer from '@/components/PageContainer';
|
||||
@@ -7,12 +16,14 @@ import { useConfirm } from '@/hooks/useConfirm';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { delKbById, postCreateKb } from '@/api/plugins/kb';
|
||||
import { useRequest } from '@/hooks/useRequest';
|
||||
import { delKbById } from '@/api/plugins/kb';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import Tag from '@/components/Tag';
|
||||
import { serviceSideProps } from '@/utils/i18n';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const CreateModal = dynamic(() => import('./component/CreateModal'), { ssr: false });
|
||||
|
||||
const Kb = () => {
|
||||
const theme = useTheme();
|
||||
@@ -24,7 +35,13 @@ const Kb = () => {
|
||||
});
|
||||
const { myKbList, loadKbList, setKbList } = useUserStore();
|
||||
|
||||
useQuery(['loadKbList'], () => loadKbList());
|
||||
const {
|
||||
isOpen: isOpenCreateModal,
|
||||
onOpen: onOpenCreateModal,
|
||||
onClose: onCloseCreateModal
|
||||
} = useDisclosure();
|
||||
|
||||
const { refetch } = useQuery(['loadKbList'], () => loadKbList());
|
||||
|
||||
/* 点击删除 */
|
||||
const onclickDelKb = useCallback(
|
||||
@@ -46,32 +63,13 @@ const Kb = () => {
|
||||
[toast, setKbList, myKbList]
|
||||
);
|
||||
|
||||
/* create a new kb and router to it */
|
||||
const { mutate: onclickCreate, isLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
const name = `知识库${myKbList.length + 1}`;
|
||||
const id = await postCreateKb({ name });
|
||||
return id;
|
||||
},
|
||||
successToast: '创建成功',
|
||||
errorToast: '创建知识库出现意外',
|
||||
onSuccess(id) {
|
||||
router.push(`/kb/detail?kbId=${id}`);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<Flex pt={3} px={5} alignItems={'center'}>
|
||||
<Box flex={1} className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
|
||||
我的知识库
|
||||
</Box>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
leftIcon={<AddIcon />}
|
||||
variant={'base'}
|
||||
onClick={onclickCreate}
|
||||
>
|
||||
<Button leftIcon={<AddIcon />} variant={'base'} onClick={onOpenCreateModal}>
|
||||
新建
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -141,6 +139,10 @@ const Kb = () => {
|
||||
))}
|
||||
</Flex>
|
||||
</Box>
|
||||
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
|
||||
<MyIcon mr={1} name="kbTest" w={'12px'} />
|
||||
<Box color={'myGray.500'}>{kb.vectorModelName}</Box>
|
||||
</Flex>
|
||||
</Card>
|
||||
))}
|
||||
</Grid>
|
||||
@@ -153,6 +155,7 @@ const Kb = () => {
|
||||
</Flex>
|
||||
)}
|
||||
<ConfirmModal />
|
||||
{isOpenCreateModal && <CreateModal onClose={onCloseCreateModal} />}
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user