feat: set openai account
This commit is contained in:
parent
7a56680935
commit
dfda5285bd
BIN
client/public/imgs/openai.png
Normal file
BIN
client/public/imgs/openai.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
@ -33,10 +33,12 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Bill Detail": "Bill Detail",
|
"Bill Detail": "Bill Detail",
|
||||||
|
"Old password is error": "Old password is error",
|
||||||
|
"OpenAI Account Setting": "OpenAI Account Setting",
|
||||||
"Pay": "Pay",
|
"Pay": "Pay",
|
||||||
|
"Set OpenAI Account Failed": "Set OpenAI account failed",
|
||||||
"Update Password": "Update Password",
|
"Update Password": "Update Password",
|
||||||
"Update password failed": "Update password failed",
|
"Update password failed": "Update password failed",
|
||||||
"Update password succseful": "Update password succseful",
|
"Update password succseful": "Update password succseful"
|
||||||
"Old password is error": "Old password is error"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,10 +33,12 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Bill Detail": "账单详情",
|
"Bill Detail": "账单详情",
|
||||||
|
"Old password is error": "旧密码错误",
|
||||||
|
"OpenAI Account Setting": "OpenAI 账号配置",
|
||||||
"Pay": "充值",
|
"Pay": "充值",
|
||||||
|
"Set OpenAI Account Failed": "设置 OpenAI 账号异常",
|
||||||
"Update Password": "修改密码",
|
"Update Password": "修改密码",
|
||||||
"Update password failed": "修改密码异常",
|
"Update password failed": "修改密码异常",
|
||||||
"Update password succseful": "修改密码成功",
|
"Update password succseful": "修改密码成功"
|
||||||
"Old password is error": "旧密码错误"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
import { GET, POST, DELETE } from './request';
|
|
||||||
@ -1,10 +1,8 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Box, Flex, Button, useDisclosure, useTheme, ModalBody } from '@chakra-ui/react';
|
import { Box, Flex, Button, useDisclosure, useTheme, Divider } from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { UserUpdateParams } from '@/types/user';
|
import { UserUpdateParams } from '@/types/user';
|
||||||
import { putUserInfo } from '@/api/user';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useGlobalStore } from '@/store/global';
|
|
||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import { UserType } from '@/types/user';
|
import { UserType } from '@/types/user';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
@ -28,13 +26,16 @@ const UpdatePswModal = dynamic(() => import('./UpdatePswModal'), {
|
|||||||
loading: () => <Loading fixed={false} />,
|
loading: () => <Loading fixed={false} />,
|
||||||
ssr: false
|
ssr: false
|
||||||
});
|
});
|
||||||
|
const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal'), {
|
||||||
|
loading: () => <Loading fixed={false} />,
|
||||||
|
ssr: false
|
||||||
|
});
|
||||||
|
|
||||||
const UserInfo = () => {
|
const UserInfo = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userInfo, updateUserInfo, initUserInfo } = useUserStore();
|
const { userInfo, updateUserInfo, initUserInfo } = useUserStore();
|
||||||
const { setLoading } = useGlobalStore();
|
const { reset } = useForm<UserUpdateParams>({
|
||||||
const { reset, register } = useForm<UserUpdateParams>({
|
|
||||||
defaultValues: userInfo as UserType
|
defaultValues: userInfo as UserType
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -49,41 +50,32 @@ const UserInfo = () => {
|
|||||||
onClose: onCloseUpdatePsw,
|
onClose: onCloseUpdatePsw,
|
||||||
onOpen: onOpenUpdatePsw
|
onOpen: onOpenUpdatePsw
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure();
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const onclickSave = useCallback(
|
const onclickSave = useCallback(
|
||||||
async (data: UserUpdateParams) => {
|
async (data: UserType) => {
|
||||||
setLoading(true);
|
await updateUserInfo({
|
||||||
try {
|
avatar: data.avatar,
|
||||||
await putUserInfo({
|
openaiAccount: data.openaiAccount
|
||||||
avatar: data.avatar
|
});
|
||||||
});
|
reset(data);
|
||||||
updateUserInfo({
|
toast({
|
||||||
avatar: data.avatar
|
title: '更新数据成功',
|
||||||
});
|
status: 'success'
|
||||||
reset(data);
|
});
|
||||||
toast({
|
|
||||||
title: '更新数据成功',
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(error),
|
|
||||||
status: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
},
|
},
|
||||||
[reset, setLoading, toast, updateUserInfo]
|
[reset, toast, updateUserInfo]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
const onSelectFile = useCallback(
|
||||||
async (e: File[]) => {
|
async (e: File[]) => {
|
||||||
const file = e[0];
|
const file = e[0];
|
||||||
if (!file) return;
|
if (!file || !userInfo) return;
|
||||||
try {
|
try {
|
||||||
const src = await compressImg({
|
const src = await compressImg({
|
||||||
file,
|
file,
|
||||||
@ -110,6 +102,7 @@ const UserInfo = () => {
|
|||||||
reset(res);
|
reset(res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box display={['block', 'flex']} py={[2, 10]} justifyContent={'center'} fontSize={['lg', 'xl']}>
|
<Box display={['block', 'flex']} py={[2, 10]} justifyContent={'center'} fontSize={['lg', 'xl']}>
|
||||||
<Flex
|
<Flex
|
||||||
@ -155,22 +148,65 @@ const UserInfo = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
{feConfigs?.show_userDetail && (
|
{feConfigs?.show_userDetail && (
|
||||||
<Box mt={6} whiteSpace={'nowrap'} w={['85%', '300px']}>
|
<>
|
||||||
<Flex alignItems={'center'}>
|
<Box mt={6} whiteSpace={'nowrap'} w={['85%', '300px']}>
|
||||||
<Box flex={'0 0 50px'}>余额:</Box>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={1}>
|
<Box flex={'0 0 50px'}>余额:</Box>
|
||||||
<strong>{userInfo?.balance.toFixed(3)}</strong> 元
|
<Box flex={1}>
|
||||||
</Box>
|
<strong>{userInfo?.balance.toFixed(3)}</strong> 元
|
||||||
<Button size={['sm', 'md']} ml={5} onClick={onOpenPayModal}>
|
</Box>
|
||||||
充值
|
<Button size={['sm', 'md']} ml={5} onClick={onOpenPayModal}>
|
||||||
</Button>
|
充值
|
||||||
</Flex>
|
</Button>
|
||||||
</Box>
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider my={3} />
|
||||||
|
|
||||||
|
<MyTooltip label={'点击配置账号'}>
|
||||||
|
<Flex
|
||||||
|
w={['85%', '300px']}
|
||||||
|
py={3}
|
||||||
|
px={6}
|
||||||
|
border={theme.borders.sm}
|
||||||
|
borderWidth={'1.5px'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
bg={'myWhite.300'}
|
||||||
|
alignItems={'center'}
|
||||||
|
cursor={'pointer'}
|
||||||
|
userSelect={'none'}
|
||||||
|
onClick={onOpenOpenai}
|
||||||
|
>
|
||||||
|
<Avatar src={'/imgs/openai.png'} w={'18px'} />
|
||||||
|
<Box ml={2} flex={1}>
|
||||||
|
OpenAI 账号
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
w={'9px'}
|
||||||
|
h={'9px'}
|
||||||
|
borderRadius={'50%'}
|
||||||
|
bg={userInfo?.openaiAccount?.key ? '#67c13b' : 'myGray.500'}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</MyTooltip>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{isOpenPayModal && <PayModal onClose={onClosePayModal} />}
|
{isOpenPayModal && <PayModal onClose={onClosePayModal} />}
|
||||||
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
|
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
|
||||||
|
{isOpenOpenai && userInfo && (
|
||||||
|
<OpenAIAccountModal
|
||||||
|
defaultData={userInfo?.openaiAccount}
|
||||||
|
onSuccess={(data) =>
|
||||||
|
onclickSave({
|
||||||
|
...userInfo,
|
||||||
|
openaiAccount: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onClose={onCloseOpenai}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<File onSelect={onSelectFile} />
|
<File onSelect={onSelectFile} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
62
client/src/pages/account/components/OpenAIAccountModal.tsx
Normal file
62
client/src/pages/account/components/OpenAIAccountModal.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/react';
|
||||||
|
import MyModal from '@/components/MyModal';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { useRequest } from '@/hooks/useRequest';
|
||||||
|
import { UserType } from '@/types/user';
|
||||||
|
|
||||||
|
const OpenAIAccountModal = ({
|
||||||
|
defaultData,
|
||||||
|
onSuccess,
|
||||||
|
onClose
|
||||||
|
}: {
|
||||||
|
defaultData: UserType['openaiAccount'];
|
||||||
|
onSuccess: (e: UserType['openaiAccount']) => Promise<any>;
|
||||||
|
onClose: () => void;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { register, handleSubmit } = useForm({
|
||||||
|
defaultValues: defaultData
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: onSubmit, isLoading } = useRequest({
|
||||||
|
mutationFn: async (data: UserType['openaiAccount']) => onSuccess(data),
|
||||||
|
onSuccess(res) {
|
||||||
|
onClose();
|
||||||
|
},
|
||||||
|
errorToast: t('user.Set OpenAI Account Failed')
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyModal isOpen onClose={onClose} title={t('user.OpenAI Account Setting')}>
|
||||||
|
<ModalBody>
|
||||||
|
<Box fontSize={'sm'} color={'myGray.500'}>
|
||||||
|
如果你填写了该内容,平台上的聊天不会计费(不包含知识库训练和 API 调用)
|
||||||
|
</Box>
|
||||||
|
<Flex alignItems={'center'} mt={5}>
|
||||||
|
<Box flex={'0 0 65px'}>API Key:</Box>
|
||||||
|
<Input flex={1} {...register('key')}></Input>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} mt={5}>
|
||||||
|
<Box flex={'0 0 65px'}>BaseUrl:</Box>
|
||||||
|
<Input
|
||||||
|
flex={1}
|
||||||
|
{...register('baseUrl')}
|
||||||
|
placeholder={'中转地址,未自动补全 "v1"'}
|
||||||
|
></Input>
|
||||||
|
</Flex>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button mr={3} variant={'base'} onClick={onClose}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
|
||||||
|
确认
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</MyModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OpenAIAccountModal;
|
||||||
@ -1,62 +0,0 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { connectToDatabase, TrainingData, User, promotionRecord } from '@/service/mongo';
|
|
||||||
import { TrainingModeEnum } from '@/constants/plugin';
|
|
||||||
import mongoose from 'mongoose';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await authUser({ req, authRoot: true });
|
|
||||||
const { amount, userId, type } = req.body as {
|
|
||||||
amount: number;
|
|
||||||
userId: number;
|
|
||||||
type: 'withdraw';
|
|
||||||
};
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
if (!userId || !amount || type !== 'withdraw' || amount <= 0) {
|
|
||||||
throw new Error('params is error');
|
|
||||||
}
|
|
||||||
|
|
||||||
// check promotion balance
|
|
||||||
const countResidue: { totalAmount: number }[] = await promotionRecord.aggregate([
|
|
||||||
{ $match: { userId: new mongoose.Types.ObjectId(userId) } },
|
|
||||||
{
|
|
||||||
$group: {
|
|
||||||
_id: null, // 分组条件,这里使用 null 表示不分组
|
|
||||||
totalAmount: { $sum: '$amount' } // 计算 amount 字段的总和
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$project: {
|
|
||||||
_id: false, // 排除 _id 字段
|
|
||||||
totalAmount: true // 只返回 totalAmount 字段
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const balance = countResidue[0].totalAmount;
|
|
||||||
|
|
||||||
if (balance < amount) {
|
|
||||||
throw new Error('可提现余额不足');
|
|
||||||
}
|
|
||||||
|
|
||||||
// add record
|
|
||||||
await promotionRecord.create({
|
|
||||||
userId,
|
|
||||||
type,
|
|
||||||
amount: -amount
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes(res, {
|
|
||||||
data: balance
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { authBalanceByUid, authUser } from '@/service/utils/auth';
|
import { authBalanceByUid, authUser } from '@/service/utils/auth';
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
import { withNextCors } from '@/service/utils/tools';
|
||||||
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
|
import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
|
||||||
import { pushGenerateVectorBill } from '@/service/events/pushBill';
|
import { pushGenerateVectorBill } from '@/service/events/pushBill';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -49,7 +49,7 @@ export async function getVector({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取 chatAPI
|
// 获取 chatAPI
|
||||||
const chatAPI = getOpenAIApi();
|
const chatAPI = getAIChatApi();
|
||||||
|
|
||||||
// 把输入的内容转成向量
|
// 把输入的内容转成向量
|
||||||
const result = await chatAPI
|
const result = await chatAPI
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { authUser, getSystemOpenAiKey } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { axiosConfig } from '@/service/ai/openai';
|
import { axiosConfig } from '@/service/ai/openai';
|
||||||
|
|
||||||
|
|||||||
@ -5,22 +5,44 @@ import { User } from '@/service/models/user';
|
|||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import { UserUpdateParams } from '@/types/user';
|
import { UserUpdateParams } from '@/types/user';
|
||||||
|
import { getAIChatApi, openaiBaseUrl } from '@/service/ai/openai';
|
||||||
|
|
||||||
/* 更新一些基本信息 */
|
/* 更新一些基本信息 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { avatar } = req.body as UserUpdateParams;
|
let { avatar, openaiAccount } = req.body as UserUpdateParams;
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const { userId } = await authUser({ req, authToken: true });
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
// auth key
|
||||||
|
if (openaiAccount?.key) {
|
||||||
|
console.log('auth user openai key', openaiAccount?.key);
|
||||||
|
|
||||||
|
const chatAPI = getAIChatApi({
|
||||||
|
base: openaiAccount?.baseUrl || openaiBaseUrl,
|
||||||
|
apikey: openaiAccount?.key
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await chatAPI.createChatCompletion({
|
||||||
|
model: 'gpt-3.5-turbo',
|
||||||
|
max_tokens: 1,
|
||||||
|
messages: [{ role: 'user', content: 'hi' }]
|
||||||
|
});
|
||||||
|
if (!response?.data?.choices?.[0]?.message?.content) {
|
||||||
|
throw new Error(JSON.stringify(response?.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新对应的记录
|
// 更新对应的记录
|
||||||
await User.updateOne(
|
await User.updateOne(
|
||||||
{
|
{
|
||||||
_id: userId
|
_id: userId
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...(avatar && { avatar })
|
...(avatar && { avatar }),
|
||||||
|
...(openaiAccount && { openaiAccount })
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -230,13 +230,13 @@ const OutLink = ({ appId }: { appId: string }) => {
|
|||||||
title: '免登录窗口',
|
title: '免登录窗口',
|
||||||
desc: '分享链接给其他用户,无需登录即可直接进行使用',
|
desc: '分享链接给其他用户,无需登录即可直接进行使用',
|
||||||
value: LinkTypeEnum.share
|
value: LinkTypeEnum.share
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: 'outlink_iframe',
|
|
||||||
title: '网页嵌入',
|
|
||||||
desc: '嵌入到已有网页中,右下角会生成对话按键',
|
|
||||||
value: LinkTypeEnum.iframe
|
|
||||||
}
|
}
|
||||||
|
// {
|
||||||
|
// icon: 'outlink_iframe',
|
||||||
|
// title: '网页嵌入',
|
||||||
|
// desc: '嵌入到已有网页中,右下角会生成对话按键',
|
||||||
|
// value: LinkTypeEnum.iframe
|
||||||
|
// }
|
||||||
]}
|
]}
|
||||||
value={linkType}
|
value={linkType}
|
||||||
onChange={(e) => setLinkType(e as `${LinkTypeEnum}`)}
|
onChange={(e) => setLinkType(e as `${LinkTypeEnum}`)}
|
||||||
|
|||||||
@ -1,27 +1,26 @@
|
|||||||
import { Configuration, OpenAIApi } from 'openai';
|
import { Configuration, OpenAIApi } from 'openai';
|
||||||
|
|
||||||
const baseUrl =
|
export const openaiBaseUrl = 'https://api.openai.com/v1';
|
||||||
process.env.ONEAPI_URL || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
|
export const baseUrl = process.env.ONEAPI_URL || process.env.OPENAI_BASE_URL || openaiBaseUrl;
|
||||||
|
|
||||||
export const getSystemOpenAiKey = () => {
|
export const systemAIChatKey = process.env.ONEAPI_KEY || process.env.OPENAIKEY || '';
|
||||||
return process.env.ONEAPI_KEY || process.env.OPENAIKEY || '';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getOpenAIApi = () => {
|
export const getAIChatApi = (props?: { base?: string; apikey?: string }) => {
|
||||||
return new OpenAIApi(
|
return new OpenAIApi(
|
||||||
new Configuration({
|
new Configuration({
|
||||||
basePath: baseUrl
|
basePath: props?.base || baseUrl,
|
||||||
|
apiKey: props?.apikey || systemAIChatKey
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* openai axios config */
|
/* openai axios config */
|
||||||
export const axiosConfig = () => {
|
export const axiosConfig = (props?: { base?: string; apikey?: string }) => {
|
||||||
return {
|
return {
|
||||||
baseURL: baseUrl, // 此处仅对非 npm 模块有效
|
baseURL: props?.base || baseUrl, // 此处仅对非 npm 模块有效
|
||||||
httpsAgent: global.httpsAgent,
|
httpsAgent: global.httpsAgent,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${getSystemOpenAiKey()}`,
|
Authorization: `Bearer ${props?.apikey || systemAIChatKey}`,
|
||||||
auth: process.env.OPENAI_BASE_URL_AUTH || ''
|
auth: process.env.OPENAI_BASE_URL_AUTH || ''
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { TrainingModeEnum } from '@/constants/plugin';
|
|||||||
import { ERROR_ENUM } from '../errorCode';
|
import { ERROR_ENUM } from '../errorCode';
|
||||||
import { sendInform } from '@/pages/api/user/inform/send';
|
import { sendInform } from '@/pages/api/user/inform/send';
|
||||||
import { authBalanceByUid } from '../utils/auth';
|
import { authBalanceByUid } from '../utils/auth';
|
||||||
import { axiosConfig, getOpenAIApi } from '../ai/openai';
|
import { axiosConfig, getAIChatApi } from '../ai/openai';
|
||||||
import { ChatCompletionRequestMessage } from 'openai';
|
import { ChatCompletionRequestMessage } from 'openai';
|
||||||
import { modelToolMap } from '@/utils/plugin';
|
import { modelToolMap } from '@/utils/plugin';
|
||||||
import { gptMessage2ChatType } from '@/utils/adapt';
|
import { gptMessage2ChatType } from '@/utils/adapt';
|
||||||
@ -55,7 +55,7 @@ export async function generateQA(): Promise<any> {
|
|||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
const chatAPI = getOpenAIApi();
|
const chatAPI = getAIChatApi();
|
||||||
|
|
||||||
// 请求 chatgpt 获取回答
|
// 请求 chatgpt 获取回答
|
||||||
const response = await Promise.all(
|
const response = await Promise.all(
|
||||||
|
|||||||
@ -35,18 +35,17 @@ const UserSchema = new Schema({
|
|||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'user'
|
ref: 'user'
|
||||||
},
|
},
|
||||||
promotion: {
|
|
||||||
rate: {
|
|
||||||
// 返现比例
|
|
||||||
type: Number,
|
|
||||||
default: 15
|
|
||||||
}
|
|
||||||
},
|
|
||||||
limit: {
|
limit: {
|
||||||
exportKbTime: {
|
exportKbTime: {
|
||||||
// Every half hour
|
// Every half hour
|
||||||
type: Date
|
type: Date
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
openaiAccount: {
|
||||||
|
type: {
|
||||||
|
key: String,
|
||||||
|
baseUrl: String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { adaptChatItem_openAI } from '@/utils/plugin/openai';
|
|||||||
import { ChatContextFilter } from '@/service/utils/chat/index';
|
import { ChatContextFilter } from '@/service/utils/chat/index';
|
||||||
import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
|
import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
|
||||||
import { ChatModuleEnum, ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat';
|
import { ChatModuleEnum, ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat';
|
||||||
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
|
import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
|
||||||
import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
||||||
import { countModelPrice } from '@/service/events/pushBill';
|
import { countModelPrice } from '@/service/events/pushBill';
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ export const dispatchClassifyQuestion = async (props: Record<string, any>): Prom
|
|||||||
required: ['type']
|
required: ['type']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const chatAPI = getOpenAIApi();
|
const chatAPI = getAIChatApi();
|
||||||
|
|
||||||
const response = await chatAPI.createChatCompletion(
|
const response = await chatAPI.createChatCompletion(
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { adaptChatItem_openAI } from '@/utils/plugin/openai';
|
|||||||
import { ChatContextFilter } from '@/service/utils/chat/index';
|
import { ChatContextFilter } from '@/service/utils/chat/index';
|
||||||
import type { ChatItemType } from '@/types/chat';
|
import type { ChatItemType } from '@/types/chat';
|
||||||
import { ChatRoleEnum } from '@/constants/chat';
|
import { ChatRoleEnum } from '@/constants/chat';
|
||||||
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
|
import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
|
||||||
import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ export async function extract({ agents, history = [], userChatInput, description
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const chatAPI = getOpenAIApi();
|
const chatAPI = getAIChatApi();
|
||||||
|
|
||||||
const response = await chatAPI.createChatCompletion(
|
const response = await chatAPI.createChatCompletion(
|
||||||
{
|
{
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import type { ChatHistoryItemResType } from '@/types/chat';
|
|||||||
import { ChatModuleEnum, ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat';
|
import { ChatModuleEnum, ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat';
|
||||||
import { SSEParseData, parseStreamChunk } from '@/utils/sse';
|
import { SSEParseData, parseStreamChunk } from '@/utils/sse';
|
||||||
import { textAdaptGptResponse } from '@/utils/adapt';
|
import { textAdaptGptResponse } from '@/utils/adapt';
|
||||||
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
|
import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
|
||||||
import { TaskResponseKeyEnum } from '@/constants/chat';
|
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||||
import { getChatModel } from '@/service/utils/data';
|
import { getChatModel } from '@/service/utils/data';
|
||||||
import { countModelPrice } from '@/service/events/pushBill';
|
import { countModelPrice } from '@/service/events/pushBill';
|
||||||
@ -77,7 +77,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
|
|||||||
// FastGpt temperature range: 1~10
|
// FastGpt temperature range: 1~10
|
||||||
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
|
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
|
||||||
temperature = Math.max(temperature, 0.01);
|
temperature = Math.max(temperature, 0.01);
|
||||||
const chatAPI = getOpenAIApi();
|
const chatAPI = getAIChatApi();
|
||||||
|
|
||||||
const response = await chatAPI.createChatCompletion(
|
const response = await chatAPI.createChatCompletion(
|
||||||
{
|
{
|
||||||
|
|||||||
@ -162,11 +162,6 @@ export const authUser = async ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/* random get openai api key */
|
|
||||||
export const getSystemOpenAiKey = () => {
|
|
||||||
return process.env.ONEAPI_KEY || process.env.OPENAIKEY || '';
|
|
||||||
};
|
|
||||||
|
|
||||||
// 模型使用权校验
|
// 模型使用权校验
|
||||||
export const authApp = async ({
|
export const authApp = async ({
|
||||||
appId,
|
appId,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { immer } from 'zustand/middleware/immer';
|
|||||||
import type { UserType, UserUpdateParams } from '@/types/user';
|
import type { UserType, UserUpdateParams } from '@/types/user';
|
||||||
import { getMyModels, getModelById, putAppById } from '@/api/app';
|
import { getMyModels, getModelById, putAppById } from '@/api/app';
|
||||||
import { formatPrice } from '@/utils/user';
|
import { formatPrice } from '@/utils/user';
|
||||||
import { getTokenLogin } from '@/api/user';
|
import { getTokenLogin, putUserInfo } from '@/api/user';
|
||||||
import { defaultApp } from '@/constants/model';
|
import { defaultApp } from '@/constants/model';
|
||||||
import { AppListItemType, AppUpdateParams } from '@/types/app';
|
import { AppListItemType, AppUpdateParams } from '@/types/app';
|
||||||
import type { KbItemType, KbListItemType } from '@/types/plugin';
|
import type { KbItemType, KbListItemType } from '@/types/plugin';
|
||||||
@ -16,7 +16,7 @@ type State = {
|
|||||||
userInfo: UserType | null;
|
userInfo: UserType | null;
|
||||||
initUserInfo: () => Promise<UserType>;
|
initUserInfo: () => Promise<UserType>;
|
||||||
setUserInfo: (user: UserType | null) => void;
|
setUserInfo: (user: UserType | null) => void;
|
||||||
updateUserInfo: (user: UserUpdateParams) => void;
|
updateUserInfo: (user: UserUpdateParams) => Promise<void>;
|
||||||
myApps: AppListItemType[];
|
myApps: AppListItemType[];
|
||||||
myCollectionApps: AppListItemType[];
|
myCollectionApps: AppListItemType[];
|
||||||
loadMyApps: (init?: boolean) => Promise<AppListItemType[]>;
|
loadMyApps: (init?: boolean) => Promise<AppListItemType[]>;
|
||||||
@ -52,7 +52,7 @@ export const useUserStore = create<State>()(
|
|||||||
: null;
|
: null;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateUserInfo(user: UserUpdateParams) {
|
async updateUserInfo(user: UserUpdateParams) {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
if (!state.userInfo) return;
|
if (!state.userInfo) return;
|
||||||
state.userInfo = {
|
state.userInfo = {
|
||||||
@ -60,6 +60,7 @@ export const useUserStore = create<State>()(
|
|||||||
...user
|
...user
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
await putUserInfo(user);
|
||||||
},
|
},
|
||||||
myApps: [],
|
myApps: [],
|
||||||
myCollectionApps: [],
|
myCollectionApps: [],
|
||||||
|
|||||||
5
client/src/types/mongoSchema.d.ts
vendored
5
client/src/types/mongoSchema.d.ts
vendored
@ -16,8 +16,9 @@ export interface UserModelSchema {
|
|||||||
promotionAmount: number;
|
promotionAmount: number;
|
||||||
openaiKey: string;
|
openaiKey: string;
|
||||||
createTime: number;
|
createTime: number;
|
||||||
promotion: {
|
openaiAccount?: {
|
||||||
rate: number;
|
key: string;
|
||||||
|
baseUrl: string;
|
||||||
};
|
};
|
||||||
limit: {
|
limit: {
|
||||||
exportKbTime?: Date;
|
exportKbTime?: Date;
|
||||||
|
|||||||
7
client/src/types/user.d.ts
vendored
7
client/src/types/user.d.ts
vendored
@ -1,18 +1,17 @@
|
|||||||
import { BillSourceEnum } from '@/constants/user';
|
import { BillSourceEnum } from '@/constants/user';
|
||||||
import type { BillSchema } from './mongoSchema';
|
import type { BillSchema, UserModelSchema } from './mongoSchema';
|
||||||
export interface UserType {
|
export interface UserType {
|
||||||
_id: string;
|
_id: string;
|
||||||
username: string;
|
username: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
balance: number;
|
balance: number;
|
||||||
promotion: {
|
openaiAccount: UserModelSchema['openaiAccount'];
|
||||||
rate: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserUpdateParams {
|
export interface UserUpdateParams {
|
||||||
balance?: number;
|
balance?: number;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
|
openaiAccount?: UserModelSchema['openaiAccount'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserBillType {
|
export interface UserBillType {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user