perf: bill
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import { TrainingData } from '@/service/mongo';
|
||||
import { getApiKey } from '../utils/auth';
|
||||
import { OpenAiChatEnum } from '@/constants/model';
|
||||
import { pushSplitDataBill } from '@/service/events/pushBill';
|
||||
import { openaiAccountError } from '../errorCode';
|
||||
import { modelServiceToolMap } from '../utils/chat';
|
||||
import { ChatRoleEnum } from '@/constants/chat';
|
||||
import { BillTypeEnum } from '@/constants/user';
|
||||
import { BillSourceEnum } from '@/constants/user';
|
||||
import { pushDataToKb } from '@/pages/api/openapi/kb/pushData';
|
||||
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 { ChatCompletionRequestMessage } from 'openai';
|
||||
|
||||
const reduceQueue = () => {
|
||||
global.qaQueueLen = global.qaQueueLen > 0 ? global.qaQueueLen - 1 : 0;
|
||||
@@ -37,7 +38,8 @@ export async function generateQA(): Promise<any> {
|
||||
kbId: 1,
|
||||
prompt: 1,
|
||||
q: 1,
|
||||
source: 1
|
||||
source: 1,
|
||||
model: 1
|
||||
});
|
||||
|
||||
// task preemption
|
||||
@@ -51,54 +53,59 @@ export async function generateQA(): Promise<any> {
|
||||
userId = String(data.userId);
|
||||
const kbId = String(data.kbId);
|
||||
|
||||
// 余额校验并获取 openapi Key
|
||||
const { systemAuthKey } = await getApiKey({
|
||||
model: OpenAiChatEnum.GPT35,
|
||||
userId,
|
||||
mustPay: true
|
||||
});
|
||||
await authBalanceByUid(userId);
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
const chatAPI = getOpenAIApi();
|
||||
|
||||
// 请求 chatgpt 获取回答
|
||||
const response = await Promise.all(
|
||||
[data.q].map((text) =>
|
||||
modelServiceToolMap
|
||||
.chatCompletion({
|
||||
model: OpenAiChatEnum.GPT3516k,
|
||||
apiKey: systemAuthKey,
|
||||
temperature: 0.8,
|
||||
messages: [
|
||||
{
|
||||
obj: ChatRoleEnum.System,
|
||||
value: `你是出题人.
|
||||
[data.q].map((text) => {
|
||||
const messages: ChatCompletionRequestMessage[] = [
|
||||
{
|
||||
role: 'system',
|
||||
content: `你是出题人.
|
||||
${data.prompt || '用户会发送一段长文本'}.
|
||||
从中选出 25 个问题和答案. 答案详细完整. 按格式回答: Q1:
|
||||
A1:
|
||||
Q2:
|
||||
A2:
|
||||
...`
|
||||
},
|
||||
{
|
||||
obj: 'Human',
|
||||
value: text
|
||||
}
|
||||
],
|
||||
stream: false
|
||||
})
|
||||
.then(({ totalTokens, responseText, responseMessages }) => {
|
||||
const result = formatSplitText(responseText); // 格式化后的QA对
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: text
|
||||
}
|
||||
];
|
||||
return chatAPI
|
||||
.createChatCompletion(
|
||||
{
|
||||
model: data.model,
|
||||
temperature: 0.8,
|
||||
messages,
|
||||
stream: false
|
||||
},
|
||||
{
|
||||
timeout: 480000,
|
||||
...axiosConfig()
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
const answer = res.data.choices?.[0].message?.content;
|
||||
const totalTokens = res.data.usage?.total_tokens || 0;
|
||||
|
||||
const result = formatSplitText(answer || ''); // 格式化后的QA对
|
||||
console.log(`split result length: `, result.length);
|
||||
// 计费
|
||||
pushSplitDataBill({
|
||||
isPay: result.length > 0,
|
||||
userId: data.userId,
|
||||
type: BillTypeEnum.QA,
|
||||
textLen: responseMessages.map((item) => item.value).join('').length,
|
||||
totalTokens
|
||||
totalTokens,
|
||||
model: data.model,
|
||||
appName: 'QA 拆分'
|
||||
});
|
||||
return {
|
||||
rawContent: responseText,
|
||||
rawContent: answer,
|
||||
result
|
||||
};
|
||||
})
|
||||
@@ -106,8 +113,8 @@ A2:
|
||||
console.log('QA拆分错误');
|
||||
console.log(err.response?.status, err.response?.statusText, err.response?.data);
|
||||
return Promise.reject(err);
|
||||
})
|
||||
)
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const responseList = response.map((item) => item.result).flat();
|
||||
@@ -120,6 +127,7 @@ A2:
|
||||
source: data.source
|
||||
})),
|
||||
userId,
|
||||
model: global.vectorModels[0].model,
|
||||
mode: TrainingModeEnum.index
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { openaiAccountError } from '../errorCode';
|
||||
import { insertKbItem } from '@/service/pg';
|
||||
import { openaiEmbedding } from '@/pages/api/openapi/plugin/openaiEmbedding';
|
||||
import { getVector } from '@/pages/api/openapi/plugin/vector';
|
||||
import { TrainingData } from '../models/trainingData';
|
||||
import { ERROR_ENUM } from '../errorCode';
|
||||
import { TrainingModeEnum } from '@/constants/plugin';
|
||||
@@ -33,7 +33,8 @@ export async function generateVector(): Promise<any> {
|
||||
kbId: 1,
|
||||
q: 1,
|
||||
a: 1,
|
||||
source: 1
|
||||
source: 1,
|
||||
model: 1
|
||||
});
|
||||
|
||||
// task preemption
|
||||
@@ -55,10 +56,10 @@ export async function generateVector(): Promise<any> {
|
||||
];
|
||||
|
||||
// 生成词向量
|
||||
const vectors = await openaiEmbedding({
|
||||
const vectors = await getVector({
|
||||
model: data.model,
|
||||
input: dataItems.map((item) => item.q),
|
||||
userId,
|
||||
mustPay: true
|
||||
userId
|
||||
});
|
||||
|
||||
// 生成结果插入到 pg
|
||||
|
||||
@@ -1,66 +1,85 @@
|
||||
import { connectToDatabase, Bill, User, ShareChat } from '../mongo';
|
||||
import {
|
||||
ChatModelMap,
|
||||
OpenAiChatEnum,
|
||||
ChatModelType,
|
||||
embeddingModel,
|
||||
embeddingPrice
|
||||
} from '@/constants/model';
|
||||
import { BillTypeEnum } from '@/constants/user';
|
||||
import { BillSourceEnum } from '@/constants/user';
|
||||
import { getModel } from '../utils/data';
|
||||
import type { BillListItemType } from '@/types/mongoSchema';
|
||||
|
||||
export const pushChatBill = async ({
|
||||
isPay,
|
||||
chatModel,
|
||||
userId,
|
||||
export const createTaskBill = async ({
|
||||
appName,
|
||||
appId,
|
||||
textLen,
|
||||
tokens,
|
||||
type
|
||||
userId,
|
||||
source
|
||||
}: {
|
||||
isPay: boolean;
|
||||
chatModel: ChatModelType;
|
||||
userId: string;
|
||||
appName: string;
|
||||
appId: string;
|
||||
textLen: number;
|
||||
tokens: number;
|
||||
type: BillTypeEnum.chat | BillTypeEnum.openapiChat;
|
||||
userId: string;
|
||||
source: `${BillSourceEnum}`;
|
||||
}) => {
|
||||
console.log(`chat generate success. text len: ${textLen}. token len: ${tokens}. pay:${isPay}`);
|
||||
if (!isPay) return;
|
||||
const res = await Bill.create({
|
||||
userId,
|
||||
appName,
|
||||
appId,
|
||||
total: 0,
|
||||
source,
|
||||
list: []
|
||||
});
|
||||
return String(res._id);
|
||||
};
|
||||
|
||||
let billId = '';
|
||||
export const pushTaskBillListItem = async ({
|
||||
billId,
|
||||
moduleName,
|
||||
amount,
|
||||
model,
|
||||
tokenLen
|
||||
}: { billId?: string } & BillListItemType) => {
|
||||
if (!billId) return;
|
||||
try {
|
||||
await Bill.findByIdAndUpdate(billId, {
|
||||
$push: {
|
||||
list: {
|
||||
moduleName,
|
||||
amount,
|
||||
model,
|
||||
tokenLen
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {}
|
||||
};
|
||||
export const finishTaskBill = async ({ billId }: { billId: string }) => {
|
||||
try {
|
||||
// update bill
|
||||
const res = await Bill.findByIdAndUpdate(billId, [
|
||||
{
|
||||
$set: {
|
||||
total: {
|
||||
$sum: '$list.amount'
|
||||
},
|
||||
time: new Date()
|
||||
}
|
||||
}
|
||||
]);
|
||||
if (!res) return;
|
||||
const total = res.list.reduce((sum, item) => sum + item.amount, 0) || 0;
|
||||
|
||||
console.log('finish bill:', total);
|
||||
|
||||
// 账号扣费
|
||||
await User.findByIdAndUpdate(res.userId, {
|
||||
$inc: { balance: -total }
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('Finish bill failed:', error);
|
||||
billId && Bill.findByIdAndDelete(billId);
|
||||
}
|
||||
};
|
||||
|
||||
export const delTaskBill = async (billId?: string) => {
|
||||
if (!billId) return;
|
||||
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
// 计算价格
|
||||
const unitPrice = ChatModelMap[chatModel]?.price || 3;
|
||||
const price = unitPrice * tokens;
|
||||
|
||||
try {
|
||||
// 插入 Bill 记录
|
||||
const res = await Bill.create({
|
||||
userId,
|
||||
type,
|
||||
modelName: chatModel,
|
||||
appId,
|
||||
textLen,
|
||||
tokenLen: tokens,
|
||||
price
|
||||
});
|
||||
billId = res._id;
|
||||
|
||||
// 账号扣费
|
||||
await User.findByIdAndUpdate(userId, {
|
||||
$inc: { balance: -price }
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('创建账单失败:', error);
|
||||
billId && Bill.findByIdAndDelete(billId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
await Bill.findByIdAndRemove(billId);
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const updateShareChatBill = async ({
|
||||
@@ -81,22 +100,17 @@ export const updateShareChatBill = async ({
|
||||
};
|
||||
|
||||
export const pushSplitDataBill = async ({
|
||||
isPay,
|
||||
userId,
|
||||
totalTokens,
|
||||
textLen,
|
||||
type
|
||||
model,
|
||||
appName
|
||||
}: {
|
||||
isPay: boolean;
|
||||
model: string;
|
||||
userId: string;
|
||||
totalTokens: number;
|
||||
textLen: number;
|
||||
type: BillTypeEnum.QA;
|
||||
appName: string;
|
||||
}) => {
|
||||
console.log(
|
||||
`splitData generate success. text len: ${textLen}. token len: ${totalTokens}. pay:${isPay}`
|
||||
);
|
||||
if (!isPay) return;
|
||||
console.log(`splitData generate success. token len: ${totalTokens}.`);
|
||||
|
||||
let billId;
|
||||
|
||||
@@ -104,24 +118,22 @@ export const pushSplitDataBill = async ({
|
||||
await connectToDatabase();
|
||||
|
||||
// 获取模型单价格, 都是用 gpt35 拆分
|
||||
const unitPrice = ChatModelMap[OpenAiChatEnum.GPT3516k].price || 3;
|
||||
const unitPrice = global.chatModels.find((item) => item.model === model)?.price || 3;
|
||||
// 计算价格
|
||||
const price = unitPrice * totalTokens;
|
||||
const total = unitPrice * totalTokens;
|
||||
|
||||
// 插入 Bill 记录
|
||||
const res = await Bill.create({
|
||||
userId,
|
||||
type,
|
||||
modelName: OpenAiChatEnum.GPT3516k,
|
||||
textLen,
|
||||
appName,
|
||||
tokenLen: totalTokens,
|
||||
price
|
||||
total
|
||||
});
|
||||
billId = res._id;
|
||||
|
||||
// 账号扣费
|
||||
await User.findByIdAndUpdate(userId, {
|
||||
$inc: { balance: -price }
|
||||
$inc: { balance: -total }
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('创建账单失败:', error);
|
||||
@@ -130,21 +142,14 @@ export const pushSplitDataBill = async ({
|
||||
};
|
||||
|
||||
export const pushGenerateVectorBill = async ({
|
||||
isPay,
|
||||
userId,
|
||||
text,
|
||||
tokenLen
|
||||
tokenLen,
|
||||
model
|
||||
}: {
|
||||
isPay: boolean;
|
||||
userId: string;
|
||||
text: string;
|
||||
tokenLen: number;
|
||||
model: string;
|
||||
}) => {
|
||||
// console.log(
|
||||
// `vector generate success. text len: ${text.length}. token len: ${tokenLen}. pay:${isPay}`
|
||||
// );
|
||||
if (!isPay) return;
|
||||
|
||||
let billId;
|
||||
|
||||
try {
|
||||
@@ -152,23 +157,22 @@ export const pushGenerateVectorBill = async ({
|
||||
|
||||
try {
|
||||
// 计算价格. 至少为1
|
||||
let price = embeddingPrice * tokenLen;
|
||||
price = price > 1 ? price : 1;
|
||||
const unitPrice = global.vectorModels.find((item) => item.model === model)?.price || 0.2;
|
||||
let total = unitPrice * tokenLen;
|
||||
total = total > 1 ? total : 1;
|
||||
|
||||
// 插入 Bill 记录
|
||||
const res = await Bill.create({
|
||||
userId,
|
||||
type: BillTypeEnum.vector,
|
||||
modelName: embeddingModel,
|
||||
textLen: text.length,
|
||||
tokenLen,
|
||||
price
|
||||
model,
|
||||
appName: '索引生成',
|
||||
total
|
||||
});
|
||||
billId = res._id;
|
||||
|
||||
// 账号扣费
|
||||
await User.findByIdAndUpdate(userId, {
|
||||
$inc: { balance: -price }
|
||||
$inc: { balance: -total }
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('创建账单失败:', error);
|
||||
@@ -178,3 +182,9 @@ export const pushGenerateVectorBill = async ({
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const countModelPrice = ({ model, tokens }: { model: string; tokens: number }) => {
|
||||
const modelData = getModel(model);
|
||||
if (!modelData) return 0;
|
||||
return modelData.price * tokens;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user