From dfda5285bd32da0f81730fc44f066af0c3a22444 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Fri, 28 Jul 2023 12:02:23 +0800 Subject: [PATCH] feat: set openai account --- client/public/imgs/openai.png | Bin 0 -> 8085 bytes client/public/locales/en/common.json | 6 +- client/public/locales/zh/common.json | 6 +- client/src/api/common.ts | 1 - client/src/pages/account/components/Info.tsx | 114 ++++++++++++------ .../account/components/OpenAIAccountModal.tsx | 62 ++++++++++ client/src/pages/api/admin/withdraw.ts | 62 ---------- client/src/pages/api/openapi/plugin/vector.ts | 4 +- .../pages/api/openapi/text/sensitiveCheck.ts | 2 +- client/src/pages/api/user/account/update.ts | 26 +++- .../pages/app/detail/components/OutLink.tsx | 12 +- client/src/service/ai/openai.ts | 19 ++- client/src/service/events/generateQA.ts | 4 +- client/src/service/models/user.ts | 13 +- .../moduleDispatch/agent/classifyQuestion.ts | 4 +- .../service/moduleDispatch/agent/extract.ts | 4 +- .../src/service/moduleDispatch/chat/oneapi.ts | 4 +- client/src/service/utils/auth.ts | 5 - client/src/store/user.ts | 7 +- client/src/types/mongoSchema.d.ts | 5 +- client/src/types/user.d.ts | 7 +- 21 files changed, 211 insertions(+), 156 deletions(-) create mode 100644 client/public/imgs/openai.png delete mode 100644 client/src/api/common.ts create mode 100644 client/src/pages/account/components/OpenAIAccountModal.tsx delete mode 100644 client/src/pages/api/admin/withdraw.ts diff --git a/client/public/imgs/openai.png b/client/public/imgs/openai.png new file mode 100644 index 0000000000000000000000000000000000000000..0f237a226583e08f89f14a15d86aa330a11151ae GIT binary patch literal 8085 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 literal 0 HcmV?d00001 diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index 2aa1112cb..d6c63f0a6 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -33,10 +33,12 @@ }, "user": { "Bill Detail": "Bill Detail", + "Old password is error": "Old password is error", + "OpenAI Account Setting": "OpenAI Account Setting", "Pay": "Pay", + "Set OpenAI Account Failed": "Set OpenAI account failed", "Update Password": "Update Password", "Update password failed": "Update password failed", - "Update password succseful": "Update password succseful", - "Old password is error": "Old password is error" + "Update password succseful": "Update password succseful" } } diff --git a/client/public/locales/zh/common.json b/client/public/locales/zh/common.json index 14b4636c9..e2b7b0a77 100644 --- a/client/public/locales/zh/common.json +++ b/client/public/locales/zh/common.json @@ -33,10 +33,12 @@ }, "user": { "Bill Detail": "账单详情", + "Old password is error": "旧密码错误", + "OpenAI Account Setting": "OpenAI 账号配置", "Pay": "充值", + "Set OpenAI Account Failed": "设置 OpenAI 账号异常", "Update Password": "修改密码", "Update password failed": "修改密码异常", - "Update password succseful": "修改密码成功", - "Old password is error": "旧密码错误" + "Update password succseful": "修改密码成功" } } diff --git a/client/src/api/common.ts b/client/src/api/common.ts deleted file mode 100644 index 4157b1029..000000000 --- a/client/src/api/common.ts +++ /dev/null @@ -1 +0,0 @@ -import { GET, POST, DELETE } from './request'; diff --git a/client/src/pages/account/components/Info.tsx b/client/src/pages/account/components/Info.tsx index bb45dcea7..e8d071be4 100644 --- a/client/src/pages/account/components/Info.tsx +++ b/client/src/pages/account/components/Info.tsx @@ -1,10 +1,8 @@ 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 { UserUpdateParams } from '@/types/user'; -import { putUserInfo } from '@/api/user'; import { useToast } from '@/hooks/useToast'; -import { useGlobalStore } from '@/store/global'; import { useUserStore } from '@/store/user'; import { UserType } from '@/types/user'; import { useQuery } from '@tanstack/react-query'; @@ -28,13 +26,16 @@ const UpdatePswModal = dynamic(() => import('./UpdatePswModal'), { loading: () => , ssr: false }); +const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal'), { + loading: () => , + ssr: false +}); const UserInfo = () => { const theme = useTheme(); const { t } = useTranslation(); const { userInfo, updateUserInfo, initUserInfo } = useUserStore(); - const { setLoading } = useGlobalStore(); - const { reset, register } = useForm({ + const { reset } = useForm({ defaultValues: userInfo as UserType }); @@ -49,41 +50,32 @@ const UserInfo = () => { onClose: onCloseUpdatePsw, onOpen: onOpenUpdatePsw } = useDisclosure(); + const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure(); + const { File, onOpen: onOpenSelectFile } = useSelectFile({ fileType: '.jpg,.png', multiple: false }); const onclickSave = useCallback( - async (data: UserUpdateParams) => { - setLoading(true); - try { - await putUserInfo({ - avatar: data.avatar - }); - updateUserInfo({ - avatar: data.avatar - }); - reset(data); - toast({ - title: '更新数据成功', - status: 'success' - }); - } catch (error) { - toast({ - title: getErrText(error), - status: 'error' - }); - } - setLoading(false); + async (data: UserType) => { + await updateUserInfo({ + avatar: data.avatar, + openaiAccount: data.openaiAccount + }); + reset(data); + toast({ + title: '更新数据成功', + status: 'success' + }); }, - [reset, setLoading, toast, updateUserInfo] + [reset, toast, updateUserInfo] ); const onSelectFile = useCallback( async (e: File[]) => { const file = e[0]; - if (!file) return; + if (!file || !userInfo) return; try { const src = await compressImg({ file, @@ -110,6 +102,7 @@ const UserInfo = () => { reset(res); } }); + return ( { {feConfigs?.show_userDetail && ( - - - 余额: - - {userInfo?.balance.toFixed(3)} 元 - - - - + <> + + + 余额: + + {userInfo?.balance.toFixed(3)} 元 + + + + + + + + + + + + OpenAI 账号 + + + + + )} {isOpenPayModal && } {isOpenUpdatePsw && } + {isOpenOpenai && userInfo && ( + + onclickSave({ + ...userInfo, + openaiAccount: data + }) + } + onClose={onCloseOpenai} + /> + )} ); diff --git a/client/src/pages/account/components/OpenAIAccountModal.tsx b/client/src/pages/account/components/OpenAIAccountModal.tsx new file mode 100644 index 000000000..174e6ca47 --- /dev/null +++ b/client/src/pages/account/components/OpenAIAccountModal.tsx @@ -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; + 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 ( + + + + 如果你填写了该内容,平台上的聊天不会计费(不包含知识库训练和 API 调用) + + + API Key: + + + + BaseUrl: + + + + + + + + + ); +}; + +export default OpenAIAccountModal; diff --git a/client/src/pages/api/admin/withdraw.ts b/client/src/pages/api/admin/withdraw.ts deleted file mode 100644 index a674dc250..000000000 --- a/client/src/pages/api/admin/withdraw.ts +++ /dev/null @@ -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 - }); - } -} diff --git a/client/src/pages/api/openapi/plugin/vector.ts b/client/src/pages/api/openapi/plugin/vector.ts index bdcc7eee2..0445b4299 100644 --- a/client/src/pages/api/openapi/plugin/vector.ts +++ b/client/src/pages/api/openapi/plugin/vector.ts @@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { authBalanceByUid, authUser } from '@/service/utils/auth'; 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'; type Props = { @@ -49,7 +49,7 @@ export async function getVector({ } // 获取 chatAPI - const chatAPI = getOpenAIApi(); + const chatAPI = getAIChatApi(); // 把输入的内容转成向量 const result = await chatAPI diff --git a/client/src/pages/api/openapi/text/sensitiveCheck.ts b/client/src/pages/api/openapi/text/sensitiveCheck.ts index 12842f3c3..7966aa0b4 100644 --- a/client/src/pages/api/openapi/text/sensitiveCheck.ts +++ b/client/src/pages/api/openapi/text/sensitiveCheck.ts @@ -1,7 +1,7 @@ // 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, getSystemOpenAiKey } from '@/service/utils/auth'; +import { authUser } from '@/service/utils/auth'; import axios from 'axios'; import { axiosConfig } from '@/service/ai/openai'; diff --git a/client/src/pages/api/user/account/update.ts b/client/src/pages/api/user/account/update.ts index 397ee56ff..4189c9bc3 100644 --- a/client/src/pages/api/user/account/update.ts +++ b/client/src/pages/api/user/account/update.ts @@ -5,22 +5,44 @@ import { User } from '@/service/models/user'; import { connectToDatabase } from '@/service/mongo'; import { authUser } from '@/service/utils/auth'; import { UserUpdateParams } from '@/types/user'; +import { getAIChatApi, openaiBaseUrl } from '@/service/ai/openai'; /* 更新一些基本信息 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { avatar } = req.body as UserUpdateParams; + let { avatar, openaiAccount } = req.body as UserUpdateParams; const { userId } = await authUser({ req, authToken: true }); 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( { _id: userId }, { - ...(avatar && { avatar }) + ...(avatar && { avatar }), + ...(openaiAccount && { openaiAccount }) } ); diff --git a/client/src/pages/app/detail/components/OutLink.tsx b/client/src/pages/app/detail/components/OutLink.tsx index 26b3393ee..731f9bb4f 100644 --- a/client/src/pages/app/detail/components/OutLink.tsx +++ b/client/src/pages/app/detail/components/OutLink.tsx @@ -230,13 +230,13 @@ const OutLink = ({ appId }: { appId: string }) => { title: '免登录窗口', desc: '分享链接给其他用户,无需登录即可直接进行使用', value: LinkTypeEnum.share - }, - { - icon: 'outlink_iframe', - title: '网页嵌入', - desc: '嵌入到已有网页中,右下角会生成对话按键', - value: LinkTypeEnum.iframe } + // { + // icon: 'outlink_iframe', + // title: '网页嵌入', + // desc: '嵌入到已有网页中,右下角会生成对话按键', + // value: LinkTypeEnum.iframe + // } ]} value={linkType} onChange={(e) => setLinkType(e as `${LinkTypeEnum}`)} diff --git a/client/src/service/ai/openai.ts b/client/src/service/ai/openai.ts index 92481234e..c5beb46e3 100644 --- a/client/src/service/ai/openai.ts +++ b/client/src/service/ai/openai.ts @@ -1,27 +1,26 @@ import { Configuration, OpenAIApi } from 'openai'; -const baseUrl = - process.env.ONEAPI_URL || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1'; +export const openaiBaseUrl = 'https://api.openai.com/v1'; +export const baseUrl = process.env.ONEAPI_URL || process.env.OPENAI_BASE_URL || openaiBaseUrl; -export const getSystemOpenAiKey = () => { - return process.env.ONEAPI_KEY || process.env.OPENAIKEY || ''; -}; +export const systemAIChatKey = process.env.ONEAPI_KEY || process.env.OPENAIKEY || ''; -export const getOpenAIApi = () => { +export const getAIChatApi = (props?: { base?: string; apikey?: string }) => { return new OpenAIApi( new Configuration({ - basePath: baseUrl + basePath: props?.base || baseUrl, + apiKey: props?.apikey || systemAIChatKey }) ); }; /* openai axios config */ -export const axiosConfig = () => { +export const axiosConfig = (props?: { base?: string; apikey?: string }) => { return { - baseURL: baseUrl, // 此处仅对非 npm 模块有效 + baseURL: props?.base || baseUrl, // 此处仅对非 npm 模块有效 httpsAgent: global.httpsAgent, headers: { - Authorization: `Bearer ${getSystemOpenAiKey()}`, + Authorization: `Bearer ${props?.apikey || systemAIChatKey}`, auth: process.env.OPENAI_BASE_URL_AUTH || '' } }; diff --git a/client/src/service/events/generateQA.ts b/client/src/service/events/generateQA.ts index d3527b3a0..d9e9413cd 100644 --- a/client/src/service/events/generateQA.ts +++ b/client/src/service/events/generateQA.ts @@ -5,7 +5,7 @@ import { TrainingModeEnum } from '@/constants/plugin'; import { ERROR_ENUM } from '../errorCode'; import { sendInform } from '@/pages/api/user/inform/send'; import { authBalanceByUid } from '../utils/auth'; -import { axiosConfig, getOpenAIApi } from '../ai/openai'; +import { axiosConfig, getAIChatApi } from '../ai/openai'; import { ChatCompletionRequestMessage } from 'openai'; import { modelToolMap } from '@/utils/plugin'; import { gptMessage2ChatType } from '@/utils/adapt'; @@ -55,7 +55,7 @@ export async function generateQA(): Promise { const startTime = Date.now(); - const chatAPI = getOpenAIApi(); + const chatAPI = getAIChatApi(); // 请求 chatgpt 获取回答 const response = await Promise.all( diff --git a/client/src/service/models/user.ts b/client/src/service/models/user.ts index 61db5f1e7..baec31964 100644 --- a/client/src/service/models/user.ts +++ b/client/src/service/models/user.ts @@ -35,18 +35,17 @@ const UserSchema = new Schema({ type: Schema.Types.ObjectId, ref: 'user' }, - promotion: { - rate: { - // 返现比例 - type: Number, - default: 15 - } - }, limit: { exportKbTime: { // Every half hour type: Date } + }, + openaiAccount: { + type: { + key: String, + baseUrl: String + } } }); diff --git a/client/src/service/moduleDispatch/agent/classifyQuestion.ts b/client/src/service/moduleDispatch/agent/classifyQuestion.ts index 1d0298d59..5361ea90a 100644 --- a/client/src/service/moduleDispatch/agent/classifyQuestion.ts +++ b/client/src/service/moduleDispatch/agent/classifyQuestion.ts @@ -2,7 +2,7 @@ import { adaptChatItem_openAI } from '@/utils/plugin/openai'; import { ChatContextFilter } from '@/service/utils/chat/index'; import type { ChatHistoryItemResType, ChatItemType } from '@/types/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 { countModelPrice } from '@/service/events/pushBill'; @@ -63,7 +63,7 @@ export const dispatchClassifyQuestion = async (props: Record): Prom required: ['type'] } }; - const chatAPI = getOpenAIApi(); + const chatAPI = getAIChatApi(); const response = await chatAPI.createChatCompletion( { diff --git a/client/src/service/moduleDispatch/agent/extract.ts b/client/src/service/moduleDispatch/agent/extract.ts index 16b52f5ed..c1bfa2c83 100644 --- a/client/src/service/moduleDispatch/agent/extract.ts +++ b/client/src/service/moduleDispatch/agent/extract.ts @@ -5,7 +5,7 @@ import { adaptChatItem_openAI } from '@/utils/plugin/openai'; import { ChatContextFilter } from '@/service/utils/chat/index'; import type { ChatItemType } from '@/types/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 { 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( { diff --git a/client/src/service/moduleDispatch/chat/oneapi.ts b/client/src/service/moduleDispatch/chat/oneapi.ts index e33986904..5a6160c34 100644 --- a/client/src/service/moduleDispatch/chat/oneapi.ts +++ b/client/src/service/moduleDispatch/chat/oneapi.ts @@ -9,7 +9,7 @@ import type { ChatHistoryItemResType } from '@/types/chat'; import { ChatModuleEnum, ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; import { SSEParseData, parseStreamChunk } from '@/utils/sse'; 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 { getChatModel } from '@/service/utils/data'; import { countModelPrice } from '@/service/events/pushBill'; @@ -77,7 +77,7 @@ export const dispatchChatCompletion = async (props: Record): Promis // FastGpt temperature range: 1~10 temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2); temperature = Math.max(temperature, 0.01); - const chatAPI = getOpenAIApi(); + const chatAPI = getAIChatApi(); const response = await chatAPI.createChatCompletion( { diff --git a/client/src/service/utils/auth.ts b/client/src/service/utils/auth.ts index 73123d1e0..4457865ff 100644 --- a/client/src/service/utils/auth.ts +++ b/client/src/service/utils/auth.ts @@ -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 ({ appId, diff --git a/client/src/store/user.ts b/client/src/store/user.ts index 7d8e849f7..a537ebac0 100644 --- a/client/src/store/user.ts +++ b/client/src/store/user.ts @@ -4,7 +4,7 @@ import { immer } from 'zustand/middleware/immer'; import type { UserType, UserUpdateParams } from '@/types/user'; import { getMyModels, getModelById, putAppById } from '@/api/app'; import { formatPrice } from '@/utils/user'; -import { getTokenLogin } from '@/api/user'; +import { getTokenLogin, putUserInfo } from '@/api/user'; import { defaultApp } from '@/constants/model'; import { AppListItemType, AppUpdateParams } from '@/types/app'; import type { KbItemType, KbListItemType } from '@/types/plugin'; @@ -16,7 +16,7 @@ type State = { userInfo: UserType | null; initUserInfo: () => Promise; setUserInfo: (user: UserType | null) => void; - updateUserInfo: (user: UserUpdateParams) => void; + updateUserInfo: (user: UserUpdateParams) => Promise; myApps: AppListItemType[]; myCollectionApps: AppListItemType[]; loadMyApps: (init?: boolean) => Promise; @@ -52,7 +52,7 @@ export const useUserStore = create()( : null; }); }, - updateUserInfo(user: UserUpdateParams) { + async updateUserInfo(user: UserUpdateParams) { set((state) => { if (!state.userInfo) return; state.userInfo = { @@ -60,6 +60,7 @@ export const useUserStore = create()( ...user }; }); + await putUserInfo(user); }, myApps: [], myCollectionApps: [], diff --git a/client/src/types/mongoSchema.d.ts b/client/src/types/mongoSchema.d.ts index 45cda9630..0b9a05e52 100644 --- a/client/src/types/mongoSchema.d.ts +++ b/client/src/types/mongoSchema.d.ts @@ -16,8 +16,9 @@ export interface UserModelSchema { promotionAmount: number; openaiKey: string; createTime: number; - promotion: { - rate: number; + openaiAccount?: { + key: string; + baseUrl: string; }; limit: { exportKbTime?: Date; diff --git a/client/src/types/user.d.ts b/client/src/types/user.d.ts index db0670296..f8e8d6f60 100644 --- a/client/src/types/user.d.ts +++ b/client/src/types/user.d.ts @@ -1,18 +1,17 @@ import { BillSourceEnum } from '@/constants/user'; -import type { BillSchema } from './mongoSchema'; +import type { BillSchema, UserModelSchema } from './mongoSchema'; export interface UserType { _id: string; username: string; avatar: string; balance: number; - promotion: { - rate: number; - }; + openaiAccount: UserModelSchema['openaiAccount']; } export interface UserUpdateParams { balance?: number; avatar?: string; + openaiAccount?: UserModelSchema['openaiAccount']; } export interface UserBillType {