perf: bill framwork

This commit is contained in:
archer
2023-05-21 13:07:40 +08:00
parent e45c1eb1e0
commit 98444fd04b
19 changed files with 68 additions and 78 deletions

View File

@@ -2,10 +2,9 @@ import { GET, POST, PUT } from './request';
import { createHashPassword, Obj2Query } from '@/utils/tools';
import { ResLogin, PromotionRecordType } from './response/user';
import { UserAuthTypeEnum } from '@/constants/common';
import { UserType, UserUpdateParams } from '@/types/user';
import { UserBillType, UserType, UserUpdateParams } from '@/types/user';
import type { PagingData, RequestPaging } from '@/types';
import { BillSchema, PaySchema } from '@/types/mongoSchema';
import { adaptBill } from '@/utils/adapt';
import { PaySchema } from '@/types/mongoSchema';
export const sendAuthCode = ({
username,
@@ -69,10 +68,7 @@ export const loginOut = () => GET('/user/loginout');
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data);
export const getUserBills = (data: RequestPaging) =>
GET<PagingData<BillSchema>>(`/user/getBill?${Obj2Query(data)}`).then((res) => ({
...res,
data: res.data.map((bill) => adaptBill(bill))
}));
GET<PagingData<UserBillType>>(`/user/getBill?${Obj2Query(data)}`);
export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`);

View File

@@ -1,8 +1,7 @@
export enum BillTypeEnum {
chat = 'chat',
splitData = 'splitData',
openapiChat = 'openapiChat',
QA = 'QA',
abstract = 'abstract',
vector = 'vector',
return = 'return'
}
@@ -14,9 +13,8 @@ export enum PageTypeEnum {
export const BillTypeMap: Record<`${BillTypeEnum}`, string> = {
[BillTypeEnum.chat]: '对话',
[BillTypeEnum.splitData]: 'QA拆分',
[BillTypeEnum.openapiChat]: 'api 对话',
[BillTypeEnum.QA]: 'QA拆分',
[BillTypeEnum.abstract]: '摘要总结',
[BillTypeEnum.vector]: '索引生成',
[BillTypeEnum.return]: '退款'
};

View File

@@ -1,18 +0,0 @@
import React, { useState, useCallback, useRef } from 'react';
export const useTabs = ({
tabs = []
}: {
tabs: {
id: string;
label: string;
}[];
}) => {
const [activeTab, setActiveTab] = useState(tabs[0].id);
return {
tabs,
activeTab,
setActiveTab
};
};

View File

@@ -9,6 +9,7 @@ import { pushChatBill } from '@/service/events/pushBill';
import { resStreamResponse } from '@/service/utils/chat';
import { searchKb } from '@/service/plugins/searchKb';
import { ChatRoleEnum } from '@/constants/chat';
import { BillTypeEnum } from '@/constants/user';
/* 发送提示词 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -108,7 +109,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
userId,
chatId,
textLen: finishMessages.map((item) => item.value).join('').length,
tokens: totalTokens
tokens: totalTokens,
type: BillTypeEnum.chat
});
} catch (err: any) {
if (step === 1) {

View File

@@ -9,6 +9,7 @@ import { pushChatBill, updateShareChatBill } from '@/service/events/pushBill';
import { resStreamResponse } from '@/service/utils/chat';
import { searchKb } from '@/service/plugins/searchKb';
import { ChatRoleEnum } from '@/constants/chat';
import { BillTypeEnum } from '@/constants/user';
/* 发送提示词 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -98,7 +99,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
chatModel: model.chat.chatModel,
userId,
textLen: finishMessages.map((item) => item.value).join('').length,
tokens: totalTokens
tokens: totalTokens,
type: BillTypeEnum.chat
});
updateShareChatBill({
shareId,

View File

@@ -9,6 +9,7 @@ import { pushChatBill } from '@/service/events/pushBill';
import { searchKb } from '@/service/plugins/searchKb';
import { ChatRoleEnum } from '@/constants/chat';
import { withNextCors } from '@/service/utils/tools';
import { BillTypeEnum } from '@/constants/user';
/* 发送提示词 */
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -45,7 +46,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
let startTime = Date.now();
/* 凭证校验 */
const { userId } = await authUser({ req, authOpenApi: true });
const { userId } = await authUser({ req });
const { model } = await authModel({
userId,
@@ -74,7 +75,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// search result is empty
if (code === 201) {
return res.send(searchPrompts[0]?.value);
return isStream
? res.send(searchPrompts[0]?.value)
: jsonRes(res, { data: searchPrompts[0]?.value });
}
prompts.splice(prompts.length - 3, 0, ...searchPrompts);
} else {
@@ -129,7 +132,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
chatModel: model.chat.chatModel,
userId,
textLen,
tokens
tokens,
type: BillTypeEnum.openapiChat
});
} catch (err: any) {
if (step === 1) {

View File

@@ -15,7 +15,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const { userId } = await authUser({ req });
await PgClient.delete('modelData', {
where: [['user_id', userId], 'AND', ['id', dataId]]

View File

@@ -15,7 +15,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const { userId } = await authUser({ req });
// 更新 pg 内容.仅修改a不需要更新向量。
await PgClient.update('modelData', {

View File

@@ -22,7 +22,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}
await connectToDatabase();
const { userId } = await authUser({ req, authToken: true });
const { userId } = await authUser({ req });
// 验证是否是该用户的 model
await authKb({

View File

@@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Bill } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import type { BillSchema } from '@/types/mongoSchema';
import { adaptBill } from '@/utils/adapt';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -17,7 +17,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase();
// 根据 id 获取用户账单
const bills = await Bill.find<BillSchema>({
const bills = await Bill.find({
userId
})
.sort({ _id: -1 }) // 按照创建时间倒序排列
@@ -33,7 +33,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
data: {
pageNum,
pageSize,
data: bills,
data: bills.map(adaptBill),
total
}
});

View File

@@ -5,6 +5,7 @@ import { getUserBills } from '@/api/user';
import type { UserBillType } from '@/types/user';
import { usePagination } from '@/hooks/usePagination';
import { useLoading } from '@/hooks/useLoading';
import dayjs from 'dayjs';
const BillTable = () => {
const { Loading } = useLoading();
@@ -28,6 +29,7 @@ const BillTable = () => {
<Tr>
<Th></Th>
<Th></Th>
<Th></Th>
<Th></Th>
<Th>Tokens </Th>
<Th></Th>
@@ -36,8 +38,9 @@ const BillTable = () => {
<Tbody fontSize={'sm'}>
{bills.map((item) => (
<Tr key={item.id}>
<Td>{item.time}</Td>
<Td>{BillTypeMap[item.type]}</Td>
<Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Td>{BillTypeMap[item.type] || '-'}</Td>
<Td>{item.modelName}</Td>
<Td>{item.textLen}</Td>
<Td>{item.tokenLen}</Td>
<Td>{item.price}</Td>

View File

@@ -9,6 +9,7 @@ import { SplitDataSchema } from '@/types/mongoSchema';
import { modelServiceToolMap } from '../utils/chat';
import { ChatRoleEnum } from '@/constants/chat';
import { getErrText } from '@/utils/tools';
import { BillTypeEnum } from '@/constants/user';
export async function generateQA(next = false): Promise<any> {
if (process.env.queueTask !== '1') {
@@ -98,7 +99,7 @@ A2:
pushSplitDataBill({
isPay: !userOpenAiKey && result.length > 0,
userId: dataItem.userId,
type: 'QA',
type: BillTypeEnum.QA,
textLen: responseMessages.map((item) => item.value).join('').length,
totalTokens
});

View File

@@ -8,7 +8,8 @@ export const pushChatBill = async ({
userId,
chatId,
textLen,
tokens
tokens,
type
}: {
isPay: boolean;
chatModel: ChatModelType;
@@ -16,6 +17,7 @@ export const pushChatBill = async ({
chatId?: '' | string;
textLen: number;
tokens: number;
type: BillTypeEnum.chat | BillTypeEnum.openapiChat;
}) => {
console.log(`chat generate success. text len: ${textLen}. token len: ${tokens}. pay:${isPay}`);
if (!isPay) return;
@@ -33,7 +35,7 @@ export const pushChatBill = async ({
// 插入 Bill 记录
const res = await Bill.create({
userId,
type: 'chat',
type,
modelName: chatModel,
chatId: chatId ? chatId : undefined,
textLen,
@@ -83,7 +85,7 @@ export const pushSplitDataBill = async ({
userId: string;
totalTokens: number;
textLen: number;
type: `${BillTypeEnum}`;
type: BillTypeEnum.QA;
}) => {
console.log(
`splitData generate success. text len: ${textLen}. token len: ${totalTokens}. pay:${isPay}`

View File

@@ -1,5 +1,5 @@
import { Schema, model, models, Model } from 'mongoose';
import { ChatModelMap } from '@/constants/model';
import { ChatModelMap, embeddingModel } from '@/constants/model';
import { BillSchema as BillType } from '@/types/mongoSchema';
import { BillTypeMap } from '@/constants/user';
@@ -16,7 +16,7 @@ const BillSchema = new Schema({
},
modelName: {
type: String,
enum: [...Object.keys(ChatModelMap), 'text-embedding-ada-002'],
enum: [...Object.keys(ChatModelMap), embeddingModel],
required: true
},
chatId: {

View File

@@ -1,35 +1,34 @@
import axios from 'axios';
{/*Bing 搜索*/}
{
/*Bing 搜索*/
}
const BingSearch = async (wait_val: string) => {
const response = await axios.post("newbing中转服务器", {
prompt: wait_val,
const response = await axios.post('newbing中转服务器', {
prompt: wait_val
});
const result = response.data.result;
return result;
};
{/*google 搜索*/}
{
/*google 搜索*/
}
const GoogleSearch = async (wait_val: string) => {
const response = await axios.get('https://www.googleapis.com/customsearch/v1', {
params: {
key: google_api,
key: process.env.GOOGLE_KEY,
q: wait_val,
cx: searchEngineId,
cx: process.env.searchEngineId,
start: 1,
num: 3,
dateRestrict: 'm[1]', //搜索结果限定为一个月内
},
dateRestrict: 'm[1]' //搜索结果限定为一个月内
}
});
const results = response.data.items;
if (results !== null) {
const result = results.map((item: { snippet: string }) => item.snippet).join('\n');
return result;
}
}
export {
BingSearch,
GoogleSearch
};
export { BingSearch, GoogleSearch };

View File

@@ -14,13 +14,11 @@ import { hashPassword } from '@/service/utils/tools';
/* uniform auth user */
export const authUser = async ({
req,
userId = '',
authToken = false,
authOpenApi = false,
authRoot = false
}: {
req: NextApiRequest;
userId?: string;
authToken?: boolean;
authOpenApi?: boolean;
authRoot?: boolean;
@@ -68,17 +66,18 @@ export const authUser = async ({
return Promise.reject(error);
}
};
const parseRootKey = async (rootKey?: string) => {
if (!rootKey || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) {
const parseRootKey = async (rootKey?: string, userId?: string) => {
if (!rootKey || !userId || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
return userId;
};
const { cookie, apikey, rootkey } = (req.headers || {}) as {
const { cookie, apikey, rootkey, userid } = (req.headers || {}) as {
cookie?: string;
apikey?: string;
rootkey?: string;
userid?: string;
};
let uid = '';
@@ -88,13 +87,13 @@ export const authUser = async ({
} else if (authOpenApi) {
uid = await parseOpenApiKey(apikey);
} else if (authRoot) {
uid = await parseRootKey(rootkey);
uid = await parseRootKey(rootkey, userid);
} else if (cookie) {
uid = await parseCookie(cookie);
} else if (apikey) {
uid = await parseOpenApiKey(apikey);
} else if (rootkey) {
uid = await parseRootKey(rootkey);
uid = await parseRootKey(rootkey, userid);
} else {
return Promise.reject(ERROR_ENUM.unAuthorization);
}

View File

@@ -3,9 +3,11 @@ import {
ModelStatusEnum,
ModelNameEnum,
ModelVectorSearchModeEnum,
ChatModelType
ChatModelType,
EmbeddingModelType
} from '@/constants/model';
import type { DataType } from './data';
import { BillTypeEnum } from '@/constants/user';
export interface UserModelSchema {
_id: string;
@@ -89,7 +91,8 @@ export interface ChatPopulate extends ChatSchema {
export interface BillSchema {
_id: string;
userId: string;
type: 'chat' | 'splitData' | 'return';
type: `${BillTypeEnum}`;
modelName: ChatModelType | EmbeddingModelType;
chatId: string;
time: Date;
textLen: number;

8
src/types/user.d.ts vendored
View File

@@ -1,3 +1,4 @@
import type { BillSchema } from './mongoSchema';
export interface UserType {
_id: string;
username: string;
@@ -17,11 +18,10 @@ export interface UserUpdateParams {
export interface UserBillType {
id: string;
time: string;
type: 'chat' | 'splitData' | 'return';
time: Date;
modelName: BillSchema['modelName'];
type: BillSchema['type'];
textLen: number;
tokenLen: number;
userId: string;
chatId: string;
price: number;
}

View File

@@ -7,9 +7,8 @@ export const adaptBill = (bill: BillSchema): UserBillType => {
return {
id: bill._id,
type: bill.type,
userId: bill.userId,
chatId: bill.chatId,
time: dayjs(bill.time).format('YYYY/MM/DD HH:mm:ss'),
modelName: bill.modelName,
time: bill.time,
textLen: bill.textLen,
tokenLen: bill.tokenLen,
price: formatPrice(bill.price)