feat: model related kb
This commit is contained in:
parent
a79429fdcd
commit
5bf95bd846
@ -2,7 +2,6 @@ import { GET, POST, DELETE, PUT } from './request';
|
|||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import type { ModelUpdateParams, ShareModelItem } from '@/types/model';
|
import type { ModelUpdateParams, ShareModelItem } from '@/types/model';
|
||||||
import { RequestPaging } from '../types/index';
|
import { RequestPaging } from '../types/index';
|
||||||
import { Obj2Query } from '@/utils/tools';
|
|
||||||
import type { ModelListResponse } from './response/model';
|
import type { ModelListResponse } from './response/model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { getSystemModelList } from '@/api/system';
|
import { getSystemModelList } from '@/api/system';
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
import type { ShareChatEditType } from '@/types/model';
|
||||||
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
|
|
||||||
export const embeddingModel = 'text-embedding-ada-002';
|
export const embeddingModel = 'text-embedding-ada-002';
|
||||||
export type EmbeddingModelType = 'text-embedding-ada-002';
|
export type EmbeddingModelType = 'text-embedding-ada-002';
|
||||||
@ -142,7 +142,7 @@ export const defaultModel: ModelSchema = {
|
|||||||
status: ModelStatusEnum.pending,
|
status: ModelStatusEnum.pending,
|
||||||
updateTime: Date.now(),
|
updateTime: Date.now(),
|
||||||
chat: {
|
chat: {
|
||||||
useKb: false,
|
relatedKbs: [],
|
||||||
searchMode: ModelVectorSearchModeEnum.hightSimilarity,
|
searchMode: ModelVectorSearchModeEnum.hightSimilarity,
|
||||||
systemPrompt: '',
|
systemPrompt: '',
|
||||||
temperature: 0,
|
temperature: 0,
|
||||||
@ -153,13 +153,6 @@ export const defaultModel: ModelSchema = {
|
|||||||
isShareDetail: false,
|
isShareDetail: false,
|
||||||
intro: '',
|
intro: '',
|
||||||
collection: 0
|
collection: 0
|
||||||
},
|
|
||||||
security: {
|
|
||||||
domain: ['*'],
|
|
||||||
contextMaxLen: 1,
|
|
||||||
contentMaxLen: 1,
|
|
||||||
expiredTime: 9999,
|
|
||||||
maxLoadAmount: 1
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { extendTheme, defineStyleConfig, ComponentStyleConfig } from '@chakra-ui/react';
|
import { extendTheme, defineStyleConfig, ComponentStyleConfig } from '@chakra-ui/react';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { modalAnatomy, switchAnatomy, selectAnatomy } from '@chakra-ui/anatomy';
|
import { modalAnatomy, switchAnatomy, selectAnatomy, checkboxAnatomy } from '@chakra-ui/anatomy';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
|
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
|
||||||
|
|
||||||
@ -11,6 +11,8 @@ const { definePartsStyle: switchPart, defineMultiStyleConfig: switchMultiStyle }
|
|||||||
createMultiStyleConfigHelpers(switchAnatomy.keys);
|
createMultiStyleConfigHelpers(switchAnatomy.keys);
|
||||||
const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } =
|
const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } =
|
||||||
createMultiStyleConfigHelpers(selectAnatomy.keys);
|
createMultiStyleConfigHelpers(selectAnatomy.keys);
|
||||||
|
const { definePartsStyle: checkboxPart, defineMultiStyleConfig: checkboxMultiStyle } =
|
||||||
|
createMultiStyleConfigHelpers(checkboxAnatomy.keys);
|
||||||
|
|
||||||
// modal 弹窗
|
// modal 弹窗
|
||||||
const ModalTheme = defineMultiStyleConfig({
|
const ModalTheme = defineMultiStyleConfig({
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const prompts = [...content, prompt];
|
const prompts = [...content, prompt];
|
||||||
|
|
||||||
// 使用了知识库搜索
|
// 使用了知识库搜索
|
||||||
if (model.chat.useKb) {
|
if (model.chat.relatedKbs.length > 0) {
|
||||||
const { code, searchPrompts } = await searchKb({
|
const { code, searchPrompts } = await searchKb({
|
||||||
userOpenAiKey,
|
userOpenAiKey,
|
||||||
prompts,
|
prompts,
|
||||||
|
|||||||
@ -50,7 +50,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||||
|
|
||||||
// 使用了知识库搜索
|
// 使用了知识库搜索
|
||||||
if (model.chat.useKb) {
|
if (model.chat.relatedKbs.length > 0) {
|
||||||
const { code, searchPrompts } = await searchKb({
|
const { code, searchPrompts } = await searchKb({
|
||||||
userOpenAiKey,
|
userOpenAiKey,
|
||||||
prompts,
|
prompts,
|
||||||
|
|||||||
@ -9,10 +9,10 @@ import { authModel } from '@/service/utils/auth';
|
|||||||
/* 获取我的模型 */
|
/* 获取我的模型 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { name, avatar, chat, share, security } = req.body as ModelUpdateParams;
|
const { name, avatar, chat, share } = req.body as ModelUpdateParams;
|
||||||
const { modelId } = req.query as { modelId: string };
|
const { modelId } = req.query as { modelId: string };
|
||||||
|
|
||||||
if (!name || !chat || !security || !modelId) {
|
if (!name || !chat || !modelId) {
|
||||||
throw new Error('参数错误');
|
throw new Error('参数错误');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,8 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
chat,
|
chat,
|
||||||
'share.isShare': share.isShare,
|
'share.isShare': share.isShare,
|
||||||
'share.isShareDetail': share.isShareDetail,
|
'share.isShareDetail': share.isShareDetail,
|
||||||
'share.intro': share.intro,
|
'share.intro': share.intro
|
||||||
security
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -70,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||||
|
|
||||||
// 使用了知识库搜索
|
// 使用了知识库搜索
|
||||||
if (model.chat.useKb) {
|
if (model.chat.relatedKbs.length > 0) {
|
||||||
const similarity = ModelVectorSearchModeMap[model.chat.searchMode]?.similarity || 0.22;
|
const similarity = ModelVectorSearchModeMap[model.chat.searchMode]?.similarity || 0.22;
|
||||||
|
|
||||||
const { code, searchPrompts } = await searchKb({
|
const { code, searchPrompts } = await searchKb({
|
||||||
|
|||||||
@ -32,11 +32,9 @@ import {
|
|||||||
Th,
|
Th,
|
||||||
Td,
|
Td,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
IconButton
|
Checkbox
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { DeleteIcon } from '@chakra-ui/icons';
|
|
||||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
|
||||||
import { useForm, UseFormReturn } from 'react-hook-form';
|
import { useForm, UseFormReturn } from 'react-hook-form';
|
||||||
import { ChatModelMap, ModelVectorSearchModeMap, getChatModelList } from '@/constants/model';
|
import { ChatModelMap, ModelVectorSearchModeMap, getChatModelList } from '@/constants/model';
|
||||||
import { formatPrice } from '@/utils/user';
|
import { formatPrice } from '@/utils/user';
|
||||||
@ -49,9 +47,12 @@ import { getShareChatList, createShareChat, delShareChatById } from '@/api/chat'
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { defaultShareChat } from '@/constants/model';
|
import { defaultShareChat } from '@/constants/model';
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
import type { ShareChatEditType } from '@/types/model';
|
||||||
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { formatTimeToChatTime, useCopyData } from '@/utils/tools';
|
import { formatTimeToChatTime, useCopyData } from '@/utils/tools';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
|
import { useUserStore } from '@/store/user';
|
||||||
|
import type { KbItemType } from '@/types/plugin';
|
||||||
|
|
||||||
const ModelEditForm = ({
|
const ModelEditForm = ({
|
||||||
formHooks,
|
formHooks,
|
||||||
@ -62,10 +63,11 @@ const ModelEditForm = ({
|
|||||||
isOwner: boolean;
|
isOwner: boolean;
|
||||||
handleDelModel: () => void;
|
handleDelModel: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { toast } = useToast();
|
|
||||||
const { modelId } = useRouter().query as { modelId: string };
|
const { modelId } = useRouter().query as { modelId: string };
|
||||||
const { setLoading } = useGlobalStore();
|
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
|
const { toast } = useToast();
|
||||||
|
const { setLoading } = useGlobalStore();
|
||||||
|
const { loadKbList } = useUserStore();
|
||||||
|
|
||||||
const { openConfirm, ConfirmChild } = useConfirm({
|
const { openConfirm, ConfirmChild } = useConfirm({
|
||||||
content: '确认删除该AI助手?'
|
content: '确认删除该AI助手?'
|
||||||
@ -86,6 +88,11 @@ const ModelEditForm = ({
|
|||||||
onOpen: onOpenCreateShareChat,
|
onOpen: onOpenCreateShareChat,
|
||||||
onClose: onCloseCreateShareChat
|
onClose: onCloseCreateShareChat
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
const {
|
||||||
|
isOpen: isOpenKbSelect,
|
||||||
|
onOpen: onOpenKbSelect,
|
||||||
|
onClose: onCloseKbSelect
|
||||||
|
} = useDisclosure();
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
@ -153,11 +160,41 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// format share used token
|
||||||
const formatTokens = (tokens: number) => {
|
const formatTokens = (tokens: number) => {
|
||||||
if (tokens < 10000) return tokens;
|
if (tokens < 10000) return tokens;
|
||||||
return `${(tokens / 10000).toFixed(2)}万`;
|
return `${(tokens / 10000).toFixed(2)}万`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// init kb select list
|
||||||
|
const { data: kbList = [] } = useQuery(['loadKbList'], () => loadKbList());
|
||||||
|
const RenderSelectedKbList = useCallback(() => {
|
||||||
|
const kbs = getValues('chat.relatedKbs').map((id) => kbList.find((kb) => kb._id === id));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{kbs.map((item) =>
|
||||||
|
item ? (
|
||||||
|
<Card key={item._id} p={3} mt={3}>
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<Image
|
||||||
|
src={item.avatar}
|
||||||
|
fallbackSrc="/icon/logo.png"
|
||||||
|
w={'20px'}
|
||||||
|
h={'20px'}
|
||||||
|
alt=""
|
||||||
|
></Image>
|
||||||
|
<Box ml={3} fontWeight={'bold'}>
|
||||||
|
{item.name}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}, [getValues, kbList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* basic info */}
|
{/* basic info */}
|
||||||
@ -292,18 +329,7 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
|||||||
</Slider>
|
</Slider>
|
||||||
</Flex>
|
</Flex>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<Flex mt={4} alignItems={'center'}>
|
{getValues('chat.relatedKbs').length > 0 && (
|
||||||
<Box mr={4}>知识库搜索</Box>
|
|
||||||
<Switch
|
|
||||||
isDisabled={!isOwner}
|
|
||||||
isChecked={getValues('chat.useKb')}
|
|
||||||
onChange={() => {
|
|
||||||
setValue('chat.useKb', !getValues('chat.useKb'));
|
|
||||||
setRefresh(!refresh);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
{getValues('chat.useKb') && (
|
|
||||||
<Flex mt={4} alignItems={'center'}>
|
<Flex mt={4} alignItems={'center'}>
|
||||||
<Box mr={4} whiteSpace={'nowrap'}>
|
<Box mr={4} whiteSpace={'nowrap'}>
|
||||||
搜索模式 
|
搜索模式 
|
||||||
@ -339,7 +365,9 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
|||||||
<Box fontWeight={'bold'}>分享设置</Box>
|
<Box fontWeight={'bold'}>分享设置</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Flex mt={5} alignItems={'center'}>
|
<Flex mt={5} alignItems={'center'}>
|
||||||
<Box mr={1}>模型分享:</Box>
|
<Box mr={1} fontSize={['sm', 'md']}>
|
||||||
|
模型分享:
|
||||||
|
</Box>
|
||||||
<Tooltip label="开启模型分享后,你的模型将会出现在共享市场,可供 FastGpt 所有用户使用。用户使用时不会消耗你的 tokens,而是消耗使用者的 tokens。">
|
<Tooltip label="开启模型分享后,你的模型将会出现在共享市场,可供 FastGpt 所有用户使用。用户使用时不会消耗你的 tokens,而是消耗使用者的 tokens。">
|
||||||
<QuestionOutlineIcon mr={3} />
|
<QuestionOutlineIcon mr={3} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -350,7 +378,8 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
|||||||
setRefresh(!refresh);
|
setRefresh(!refresh);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Box ml={12} mr={1}>
|
|
||||||
|
<Box ml={12} mr={1} fontSize={['sm', 'md']}>
|
||||||
分享模型细节:
|
分享模型细节:
|
||||||
</Box>
|
</Box>
|
||||||
<Tooltip label="开启分享详情后,其他用户可以查看该模型的特有数据:温度、提示词和数据集。">
|
<Tooltip label="开启分享详情后,其他用户可以查看该模型的特有数据:温度、提示词和数据集。">
|
||||||
@ -376,8 +405,22 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Card>
|
</Card>
|
||||||
{/* shareChat */}
|
|
||||||
<Card p={4}>
|
<Card p={4}>
|
||||||
|
<Flex justifyContent={'space-between'}>
|
||||||
|
<Box fontWeight={'bold'}>关联的知识库</Box>
|
||||||
|
<Button
|
||||||
|
size={'sm'}
|
||||||
|
variant={'outline'}
|
||||||
|
colorScheme={'myBlue'}
|
||||||
|
onClick={onOpenKbSelect}
|
||||||
|
>
|
||||||
|
选择
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
<RenderSelectedKbList />
|
||||||
|
</Card>
|
||||||
|
{/* shareChat */}
|
||||||
|
<Card p={4} gridColumnStart={1} gridColumnEnd={[2, 3]}>
|
||||||
<Flex justifyContent={'space-between'}>
|
<Flex justifyContent={'space-between'}>
|
||||||
<Box fontWeight={'bold'}>
|
<Box fontWeight={'bold'}>
|
||||||
免登录聊天窗口
|
免登录聊天窗口
|
||||||
@ -410,7 +453,7 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
|||||||
<Th>最大上下文</Th>
|
<Th>最大上下文</Th>
|
||||||
<Th>tokens消耗</Th>
|
<Th>tokens消耗</Th>
|
||||||
<Th>最后使用时间</Th>
|
<Th>最后使用时间</Th>
|
||||||
<Th></Th>
|
<Th>操作</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
@ -539,6 +582,52 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
|
|||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
{/* select kb modal */}
|
||||||
|
<Modal isOpen={isOpenKbSelect} onClose={onCloseKbSelect}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader>选择关联的知识库</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody>
|
||||||
|
{kbList.map((item) => (
|
||||||
|
<Card key={item._id} p={3} mb={3}>
|
||||||
|
<Checkbox
|
||||||
|
isChecked={getValues('chat.relatedKbs').includes(item._id)}
|
||||||
|
onChange={(e) => {
|
||||||
|
const ids = getValues('chat.relatedKbs');
|
||||||
|
// toggle to true
|
||||||
|
if (e.target.checked) {
|
||||||
|
setValue('chat.relatedKbs', ids.concat(item._id));
|
||||||
|
} else {
|
||||||
|
const i = ids.findIndex((id) => id === item._id);
|
||||||
|
ids.splice(i, 1);
|
||||||
|
setValue('chat.relatedKbs', ids);
|
||||||
|
}
|
||||||
|
setRefresh(!refresh);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<Image
|
||||||
|
src={item.avatar}
|
||||||
|
fallbackSrc="/icon/logo.png"
|
||||||
|
w={'20px'}
|
||||||
|
h={'20px'}
|
||||||
|
alt=""
|
||||||
|
></Image>
|
||||||
|
<Box ml={3} fontWeight={'bold'}>
|
||||||
|
{item.name}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Checkbox>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onClick={onCloseKbSelect}>完成,记得点保存修改</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
<File onSelect={onSelectFile} />
|
<File onSelect={onSelectFile} />
|
||||||
<ConfirmChild />
|
<ConfirmChild />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -2,10 +2,9 @@ import React, { useCallback, useState, useMemo, useEffect } from 'react';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { delModelById, putModelById } from '@/api/model';
|
import { delModelById, putModelById } from '@/api/model';
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
import { Card, Box, Flex, Button, Tag, Grid } from '@chakra-ui/react';
|
import { Card, Box, Flex, Button, Grid } from '@chakra-ui/react';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { formatModelStatus } from '@/constants/model';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
import { useLoading } from '@/hooks/useLoading';
|
||||||
@ -18,7 +17,7 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
|||||||
const { Loading, setIsLoading } = useLoading();
|
const { Loading, setIsLoading } = useLoading();
|
||||||
const [btnLoading, setBtnLoading] = useState(false);
|
const [btnLoading, setBtnLoading] = useState(false);
|
||||||
|
|
||||||
const formHooks = useForm<ModelSchema>({
|
const formHooks = useForm({
|
||||||
defaultValues: modelDetail
|
defaultValues: modelDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -84,13 +83,9 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
avatar: data.avatar || '/icon/logo.png',
|
avatar: data.avatar || '/icon/logo.png',
|
||||||
chat: data.chat,
|
chat: data.chat,
|
||||||
share: data.share,
|
share: data.share
|
||||||
security: data.security
|
|
||||||
});
|
|
||||||
toast({
|
|
||||||
title: '更新成功',
|
|
||||||
status: 'success'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
refreshModel.updateModelDetail(data);
|
refreshModel.updateModelDetail(data);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toast({
|
toast({
|
||||||
@ -120,18 +115,16 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
|||||||
});
|
});
|
||||||
}, [formHooks.formState.errors, toast]);
|
}, [formHooks.formState.errors, toast]);
|
||||||
|
|
||||||
|
const saveUpdateModel = useCallback(
|
||||||
|
() => formHooks.handleSubmit(saveSubmitSuccess, saveSubmitError)(),
|
||||||
|
[formHooks, saveSubmitError, saveSubmitSuccess]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
router.prefetch('/chat');
|
|
||||||
|
|
||||||
window.onbeforeunload = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.returnValue = '内容已修改,确认离开页面吗?';
|
|
||||||
};
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.onbeforeunload = null;
|
saveUpdateModel();
|
||||||
};
|
};
|
||||||
}, [router]);
|
}, []);
|
||||||
|
|
||||||
return canRead ? (
|
return canRead ? (
|
||||||
<Box h={'100%'} p={5} overflow={'overlay'} position={'relative'}>
|
<Box h={'100%'} p={5} overflow={'overlay'} position={'relative'}>
|
||||||
@ -142,13 +135,6 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
|||||||
<Box fontSize={'xl'} fontWeight={'bold'}>
|
<Box fontSize={'xl'} fontWeight={'bold'}>
|
||||||
{modelDetail.name}
|
{modelDetail.name}
|
||||||
</Box>
|
</Box>
|
||||||
<Tag
|
|
||||||
ml={2}
|
|
||||||
variant="solid"
|
|
||||||
colorScheme={formatModelStatus[modelDetail.status].colorTheme}
|
|
||||||
>
|
|
||||||
{formatModelStatus[modelDetail.status].text}
|
|
||||||
</Tag>
|
|
||||||
<Box flex={1} />
|
<Box flex={1} />
|
||||||
<Button variant={'outline'} onClick={handlePreviewChat}>
|
<Button variant={'outline'} onClick={handlePreviewChat}>
|
||||||
开始对话
|
开始对话
|
||||||
@ -157,7 +143,18 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
|||||||
<Button
|
<Button
|
||||||
isLoading={btnLoading}
|
isLoading={btnLoading}
|
||||||
ml={4}
|
ml={4}
|
||||||
onClick={formHooks.handleSubmit(saveSubmitSuccess, saveSubmitError)}
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await saveUpdateModel();
|
||||||
|
toast({
|
||||||
|
title: '更新成功',
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
error;
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
保存修改
|
保存修改
|
||||||
</Button>
|
</Button>
|
||||||
@ -169,9 +166,6 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
|||||||
<Box as={'h3'} fontSize={'xl'} fontWeight={'bold'} flex={1}>
|
<Box as={'h3'} fontSize={'xl'} fontWeight={'bold'} flex={1}>
|
||||||
{modelDetail.name}
|
{modelDetail.name}
|
||||||
</Box>
|
</Box>
|
||||||
<Tag ml={2} colorScheme={formatModelStatus[modelDetail.status].colorTheme}>
|
|
||||||
{formatModelStatus[modelDetail.status].text}
|
|
||||||
</Tag>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box mt={4} textAlign={'right'}>
|
<Box mt={4} textAlign={'right'}>
|
||||||
<Button variant={'outline'} size={'sm'} onClick={handlePreviewChat}>
|
<Button variant={'outline'} size={'sm'} onClick={handlePreviewChat}>
|
||||||
@ -182,7 +176,18 @@ const ModelDetail = ({ modelId, isPc }: { modelId: string; isPc: boolean }) => {
|
|||||||
ml={4}
|
ml={4}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
isLoading={btnLoading}
|
isLoading={btnLoading}
|
||||||
onClick={formHooks.handleSubmit(saveSubmitSuccess, saveSubmitError)}
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await saveUpdateModel();
|
||||||
|
toast({
|
||||||
|
title: '更新成功',
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
error;
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
保存修改
|
保存修改
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -31,10 +31,10 @@ const ModelSchema = new Schema({
|
|||||||
default: () => new Date()
|
default: () => new Date()
|
||||||
},
|
},
|
||||||
chat: {
|
chat: {
|
||||||
useKb: {
|
relatedKbs: {
|
||||||
// use knowledge base to search
|
type: [Schema.Types.ObjectId],
|
||||||
type: Boolean,
|
ref: 'kb',
|
||||||
default: false
|
default: []
|
||||||
},
|
},
|
||||||
searchMode: {
|
searchMode: {
|
||||||
// knowledge base search mode
|
// knowledge base search mode
|
||||||
@ -79,33 +79,6 @@ const ModelSchema = new Schema({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
}
|
}
|
||||||
},
|
|
||||||
security: {
|
|
||||||
type: {
|
|
||||||
domain: {
|
|
||||||
type: [String],
|
|
||||||
default: ['*']
|
|
||||||
},
|
|
||||||
contextMaxLen: {
|
|
||||||
type: Number,
|
|
||||||
default: 20
|
|
||||||
},
|
|
||||||
contentMaxLen: {
|
|
||||||
type: Number,
|
|
||||||
default: 4000
|
|
||||||
},
|
|
||||||
expiredTime: {
|
|
||||||
type: Number,
|
|
||||||
default: 1,
|
|
||||||
set: (val: number) => val * (60 * 60 * 1000)
|
|
||||||
},
|
|
||||||
maxLoadAmount: {
|
|
||||||
// 负数代表不限制
|
|
||||||
type: Number,
|
|
||||||
default: -1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
default: {}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export const searchKb = async ({
|
|||||||
where: [
|
where: [
|
||||||
['status', ModelDataStatusEnum.ready],
|
['status', ModelDataStatusEnum.ready],
|
||||||
'AND',
|
'AND',
|
||||||
['model_id', model._id],
|
`kb_id IN (${model.chat.relatedKbs.map((item) => `'${item}'`).join(',')})`,
|
||||||
'AND',
|
'AND',
|
||||||
`vector <=> '[${promptVector}]' < ${similarity}`
|
`vector <=> '[${promptVector}]' < ${similarity}`
|
||||||
],
|
],
|
||||||
|
|||||||
@ -34,6 +34,13 @@ export const authToken = (req: NextApiRequest): Promise<string> => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getOpenAiKey = () => {
|
||||||
|
// 纯字符串类型
|
||||||
|
const keys = process.env.OPENAIKEY?.split(',') || [];
|
||||||
|
const i = Math.floor(Math.random() * keys.length);
|
||||||
|
return keys[i] || (process.env.OPENAIKEY as string);
|
||||||
|
};
|
||||||
|
|
||||||
/* 获取 api 请求的 key */
|
/* 获取 api 请求的 key */
|
||||||
export const getApiKey = async ({
|
export const getApiKey = async ({
|
||||||
model,
|
model,
|
||||||
@ -52,7 +59,7 @@ export const getApiKey = async ({
|
|||||||
const keyMap = {
|
const keyMap = {
|
||||||
[OpenAiChatEnum.GPT35]: {
|
[OpenAiChatEnum.GPT35]: {
|
||||||
userOpenAiKey: user.openaiKey || '',
|
userOpenAiKey: user.openaiKey || '',
|
||||||
systemAuthKey: process.env.OPENAIKEY as string
|
systemAuthKey: getOpenAiKey() as string
|
||||||
},
|
},
|
||||||
[OpenAiChatEnum.GPT4]: {
|
[OpenAiChatEnum.GPT4]: {
|
||||||
userOpenAiKey: user.openaiKey || '',
|
userOpenAiKey: user.openaiKey || '',
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { adaptChatItem_openAI } from '@/utils/chat/openai';
|
|||||||
import { modelToolMap } from '@/utils/chat';
|
import { modelToolMap } from '@/utils/chat';
|
||||||
import { ChatCompletionType, ChatContextFilter, StreamResponseType } from './index';
|
import { ChatCompletionType, ChatContextFilter, StreamResponseType } from './index';
|
||||||
import { ChatRoleEnum } from '@/constants/chat';
|
import { ChatRoleEnum } from '@/constants/chat';
|
||||||
|
import { getOpenAiKey } from '../auth';
|
||||||
|
|
||||||
export const getOpenAIApi = (apiKey: string) => {
|
export const getOpenAIApi = (apiKey: string) => {
|
||||||
const configuration = new Configuration({
|
const configuration = new Configuration({
|
||||||
@ -27,7 +28,7 @@ export const openaiCreateEmbedding = async ({
|
|||||||
userId: string;
|
userId: string;
|
||||||
textArr: string[];
|
textArr: string[];
|
||||||
}) => {
|
}) => {
|
||||||
const systemAuthKey = process.env.OPENAIKEY as string;
|
const systemAuthKey = getOpenAiKey();
|
||||||
|
|
||||||
// 获取 chatAPI
|
// 获取 chatAPI
|
||||||
const chatAPI = getOpenAIApi(userOpenAiKey || systemAuthKey);
|
const chatAPI = getOpenAIApi(userOpenAiKey || systemAuthKey);
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { create } from 'zustand';
|
|||||||
import { devtools, persist } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import type { UserType, UserUpdateParams } from '@/types/user';
|
import type { UserType, UserUpdateParams } from '@/types/user';
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
|
||||||
import { getMyModels, getModelById } from '@/api/model';
|
import { getMyModels, getModelById } from '@/api/model';
|
||||||
import { formatPrice } from '@/utils/user';
|
import { formatPrice } from '@/utils/user';
|
||||||
import { getTokenLogin } from '@/api/user';
|
import { getTokenLogin } from '@/api/user';
|
||||||
@ -11,6 +10,7 @@ import { ModelListItemType } from '@/types/model';
|
|||||||
import { KbItemType } from '@/types/plugin';
|
import { KbItemType } from '@/types/plugin';
|
||||||
import { getKbList } from '@/api/plugins/kb';
|
import { getKbList } from '@/api/plugins/kb';
|
||||||
import { defaultKbDetail } from '@/constants/kb';
|
import { defaultKbDetail } from '@/constants/kb';
|
||||||
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
userInfo: UserType | null;
|
userInfo: UserType | null;
|
||||||
@ -34,7 +34,7 @@ type State = {
|
|||||||
lastKbId: string;
|
lastKbId: string;
|
||||||
setLastKbId: (id: string) => void;
|
setLastKbId: (id: string) => void;
|
||||||
myKbList: KbItemType[];
|
myKbList: KbItemType[];
|
||||||
loadKbList: (init?: boolean) => Promise<null>;
|
loadKbList: (init?: boolean) => Promise<KbItemType[]>;
|
||||||
KbDetail: KbItemType;
|
KbDetail: KbItemType;
|
||||||
getKbDetail: (id: string) => KbItemType;
|
getKbDetail: (id: string) => KbItemType;
|
||||||
};
|
};
|
||||||
@ -123,12 +123,12 @@ export const useUserStore = create<State>()(
|
|||||||
},
|
},
|
||||||
myKbList: [],
|
myKbList: [],
|
||||||
async loadKbList(init = false) {
|
async loadKbList(init = false) {
|
||||||
if (get().myKbList.length > 0 && !init) return null;
|
if (get().myKbList.length > 0 && !init) return get().myKbList;
|
||||||
const res = await getKbList();
|
const res = await getKbList();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.myKbList = res;
|
state.myKbList = res;
|
||||||
});
|
});
|
||||||
return null;
|
return res;
|
||||||
},
|
},
|
||||||
KbDetail: defaultKbDetail,
|
KbDetail: defaultKbDetail,
|
||||||
getKbDetail(id: string) {
|
getKbDetail(id: string) {
|
||||||
|
|||||||
4
src/types/model.d.ts
vendored
4
src/types/model.d.ts
vendored
@ -1,5 +1,6 @@
|
|||||||
import { ModelStatusEnum } from '@/constants/model';
|
import { ModelStatusEnum } from '@/constants/model';
|
||||||
import type { ModelSchema } from './mongoSchema';
|
import type { ModelSchema, kbSchema } from './mongoSchema';
|
||||||
|
import { ChatModelType, ModelVectorSearchModeEnum } from '@/constants/model';
|
||||||
|
|
||||||
export type ModelListItemType = {
|
export type ModelListItemType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@ -13,7 +14,6 @@ export interface ModelUpdateParams {
|
|||||||
avatar: string;
|
avatar: string;
|
||||||
chat: ModelSchema['chat'];
|
chat: ModelSchema['chat'];
|
||||||
share: ModelSchema['share'];
|
share: ModelSchema['share'];
|
||||||
security: ModelSchema['security'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShareModelItem {
|
export interface ShareModelItem {
|
||||||
|
|||||||
9
src/types/mongoSchema.d.ts
vendored
9
src/types/mongoSchema.d.ts
vendored
@ -38,7 +38,7 @@ export interface ModelSchema {
|
|||||||
status: `${ModelStatusEnum}`;
|
status: `${ModelStatusEnum}`;
|
||||||
updateTime: number;
|
updateTime: number;
|
||||||
chat: {
|
chat: {
|
||||||
useKb: boolean;
|
relatedKbs: string[];
|
||||||
searchMode: `${ModelVectorSearchModeEnum}`;
|
searchMode: `${ModelVectorSearchModeEnum}`;
|
||||||
systemPrompt: string;
|
systemPrompt: string;
|
||||||
temperature: number;
|
temperature: number;
|
||||||
@ -50,13 +50,6 @@ export interface ModelSchema {
|
|||||||
intro: string;
|
intro: string;
|
||||||
collection: number;
|
collection: number;
|
||||||
};
|
};
|
||||||
security: {
|
|
||||||
domain: string[];
|
|
||||||
contextMaxLen: number;
|
|
||||||
contentMaxLen: number;
|
|
||||||
expiredTime: number;
|
|
||||||
maxLoadAmount: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModelPopulate extends ModelSchema {
|
export interface ModelPopulate extends ModelSchema {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user