perf: bill logs

This commit is contained in:
archer
2023-03-27 13:58:57 +08:00
parent 9280a21d12
commit 5249297cb1
6 changed files with 160 additions and 152 deletions

View File

@@ -148,13 +148,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const promptsContent = formatPrompts.map((item) => item.content).join(''); const promptsContent = formatPrompts.map((item) => item.content).join('');
// 只有使用平台的 key 才计费 // 只有使用平台的 key 才计费
!userApiKey && pushChatBill({
pushChatBill({ isPay: !userApiKey,
modelName: model.service.modelName, modelName: model.service.modelName,
userId, userId,
chatId, chatId,
text: promptsContent + responseContent text: promptsContent + responseContent
}); });
} catch (err: any) { } catch (err: any) {
if (step === 1) { if (step === 1) {
// 直接结束流 // 直接结束流

View File

@@ -149,13 +149,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
stream.destroy(); stream.destroy();
// 只有使用平台的 key 才计费 // 只有使用平台的 key 才计费
!userApiKey && pushChatBill({
pushChatBill({ isPay: !userApiKey,
modelName: model.service.modelName, modelName: model.service.modelName,
userId, userId,
chatId, chatId,
text: promptText + responseContent text: promptText + responseContent
}); });
} catch (err: any) { } catch (err: any) {
// console.log(err?.response); // console.log(err?.response);
if (step === 1) { if (step === 1) {

View File

@@ -12,7 +12,7 @@ export async function generateAbstract(next = false): Promise<any> {
const systemPrompt: ChatCompletionRequestMessage = { const systemPrompt: ChatCompletionRequestMessage = {
role: 'system', role: 'system',
content: `总结助手,我会向你发送一段长文本,请从文本中归纳总结5至15条信息,请直接输出总结内容,并按以下格式输出: 'A1:'\n'A2:'\n'A3:'\n` content: `总结助手,我会向你发送一段长文本,请从文本中归纳总结5至15条信息,如果是英文,请增加一条中文的总结,并按以下格式输出: A1:\nA2:\nA3:\n`
}; };
let dataItem: DataItemSchema | null = null; let dataItem: DataItemSchema | null = null;
@@ -80,36 +80,36 @@ export async function generateAbstract(next = false): Promise<any> {
const rawContent: string = abstractResponse?.data.choices[0].message?.content || ''; const rawContent: string = abstractResponse?.data.choices[0].message?.content || '';
// 从 content 中提取摘要内容 // 从 content 中提取摘要内容
const splitContents = splitText(rawContent); const splitContents = splitText(rawContent);
console.log(rawContent); // console.log(rawContent);
// 生成词向量 // 生成词向量
const vectorResponse = await Promise.allSettled( // const vectorResponse = await Promise.allSettled(
splitContents.map((item) => // splitContents.map((item) =>
chatAPI.createEmbedding( // chatAPI.createEmbedding(
{ // {
model: 'text-embedding-ada-002', // model: 'text-embedding-ada-002',
input: item.abstract // input: item.abstract
}, // },
{ // {
timeout: 120000, // timeout: 120000,
httpsAgent // httpsAgent
} // }
) // )
) // )
); // );
// 筛选成功的向量请求 // 筛选成功的向量请求
const vectorSuccessResponse = vectorResponse // const vectorSuccessResponse = vectorResponse
.map((item: any, i) => { // .map((item: any, i) => {
if (item.status !== 'fulfilled') { // if (item.status !== 'fulfilled') {
// 没有词向量的【摘要】不要 // // 没有词向量的【摘要】不要
console.log('获取词向量错误: ', item); // console.log('获取词向量错误: ', item);
return ''; // return '';
} // }
return { // return {
abstract: splitContents[i].abstract, // abstract: splitContents[i].abstract,
abstractVector: item?.value?.data?.data?.[0]?.embedding // abstractVector: item?.value?.data?.data?.[0]?.embedding
}; // };
}) // })
.filter((item) => item); // .filter((item) => item);
// 插入数据库,并修改状态 // 插入数据库,并修改状态
await DataItem.findByIdAndUpdate(dataItem._id, { await DataItem.findByIdAndUpdate(dataItem._id, {
@@ -117,28 +117,22 @@ export async function generateAbstract(next = false): Promise<any> {
$push: { $push: {
rawResponse: rawContent, rawResponse: rawContent,
result: { result: {
$each: vectorSuccessResponse $each: splitContents
} }
} }
}); });
// 计费
!userApiKey &&
vectorSuccessResponse.length > 0 &&
pushSplitDataBill({
userId: dataItem.userId,
type: 'abstract',
text:
systemPrompt.content +
dataItem.text +
rawContent +
rawContent.substring(0, Math.floor(dataItem.text.length / 10)) // 向量价格是 gpt35 的1/10
});
console.log( console.log(
`生成摘要成功time: ${(Date.now() - startTime) / 1000}s`, `生成摘要成功time: ${(Date.now() - startTime) / 1000}s`,
`摘要匹配数量: ${splitContents.length}`, `摘要匹配数量: ${splitContents.length}`
`有向量摘要数量:${vectorSuccessResponse.length}`
); );
// 计费
pushSplitDataBill({
isPay: !userApiKey && splitContents.length > 0,
userId: dataItem.userId,
type: 'abstract',
text: systemPrompt.content + dataItem.text + rawContent
});
} catch (error: any) { } catch (error: any) {
console.log('error: 生成摘要错误', dataItem?._id); console.log('error: 生成摘要错误', dataItem?._id);
console.log('response:', error); console.log('response:', error);

View File

@@ -12,7 +12,7 @@ export async function generateQA(next = false): Promise<any> {
const systemPrompt: ChatCompletionRequestMessage = { const systemPrompt: ChatCompletionRequestMessage = {
role: 'system', role: 'system',
content: `总结助手。我会向你发送一段长文本,请从中总结出5至15个问题和答案,答案请尽量详细,请按以下格式返回: "Q1:"\n"A1:"\n"Q2:"\n"A2:"\n` content: `总结助手。我会向你发送一段长文本,请从中总结出5至15个问题和答案,答案请尽量详细,请按以下格式返回: Q1:\nA1:\nQ2:\nA2:\n`
}; };
let dataItem: DataItemSchema | null = null; let dataItem: DataItemSchema | null = null;
@@ -58,61 +58,68 @@ export async function generateQA(next = false): Promise<any> {
const chatAPI = getOpenAIApi(userApiKey || systemKey); const chatAPI = getOpenAIApi(userApiKey || systemKey);
// 请求 chatgpt 获取回答 // 请求 chatgpt 获取回答
const response = await Promise.allSettled( const response = await Promise.allSettled(
[0, 0.5, 0.8].map((temperature) => [0.2, 0.8].map(
chatAPI.createChatCompletion( (temperature) =>
{ chatAPI
model: ChatModelNameEnum.GPT35, .createChatCompletion(
temperature: temperature,
n: 1,
messages: [
systemPrompt,
{ {
role: 'user', model: ChatModelNameEnum.GPT35,
content: dataItem?.text || '' temperature: temperature,
n: 1,
messages: [
systemPrompt,
{
role: 'user',
content: dataItem?.text || ''
}
]
},
{
timeout: 120000,
httpsAgent
} }
] )
}, .then((res) => ({
{ rawContent: res?.data.choices[0].message?.content || '',
timeout: 120000, result: splitText(res?.data.choices[0].message?.content || '')
httpsAgent })) // 从 content 中提取 QA
}
)
) )
); );
// 过滤出成功的响应 // 过滤出成功的响应
const successResponse = response.filter((item) => item.status === 'fulfilled'); const successResponse: {
// 提取响应内容 rawContent: string;
const rawContents: string[] = successResponse.map( result: { q: string; a: string }[];
(item: any) => item?.value?.data.choices[0].message?.content || '' }[] = response.filter((item) => item.status === 'fulfilled').map((item: any) => item.value);
);
// 从 content 中提取 QA const rawContents = successResponse.map((item) => item.rawContent);
const splitResponses = rawContents.map((content) => splitText(content)).flat(); const results = successResponse.map((item) => item.result).flat();
// 插入数据库,并修改状态 // 插入数据库,并修改状态
await DataItem.findByIdAndUpdate(dataItem._id, { await DataItem.findByIdAndUpdate(dataItem._id, {
status: 0, status: 0,
$push: { $push: {
rawResponse: { rawResponse: {
$each: rawContents $each: successResponse.map((item) => item.rawContent)
}, },
result: { result: {
$each: splitResponses $each: results
} }
} }
}); });
// 计费
!userApiKey &&
splitResponses.length > 0 &&
pushSplitDataBill({
userId: dataItem.userId,
type: 'QA',
text: systemPrompt.content + dataItem.text + rawContents.join('')
});
console.log( console.log(
'生成QA成功time:', '生成QA成功time:',
`${(Date.now() - startTime) / 1000}s`, `${(Date.now() - startTime) / 1000}s`,
'QA数量', 'QA数量',
splitResponses.length results.length
); );
// 计费
pushSplitDataBill({
isPay: !userApiKey && results.length > 0,
userId: dataItem.userId,
type: 'QA',
text: systemPrompt.content + dataItem.text + rawContents.join('')
});
} catch (error: any) { } catch (error: any) {
console.log('error: 生成QA错误', dataItem?._id); console.log('error: 生成QA错误', dataItem?._id);
console.log('response:', error?.response); console.log('response:', error?.response);

View File

@@ -5,55 +5,58 @@ import { formatPrice } from '@/utils/user';
import type { DataType } from '@/types/data'; import type { DataType } from '@/types/data';
export const pushChatBill = async ({ export const pushChatBill = async ({
isPay,
modelName, modelName,
userId, userId,
chatId, chatId,
text text
}: { }: {
isPay: boolean;
modelName: string; modelName: string;
userId: string; userId: string;
chatId: string; chatId: string;
text: string; text: string;
}) => { }) => {
await connectToDatabase();
let billId; let billId;
try { try {
// 获取模型单价格
const modelItem = modelList.find((item) => item.model === modelName);
const unitPrice = modelItem?.price || 5;
// 计算 token 数量 // 计算 token 数量
const tokens = encode(text); const tokens = encode(text);
// 计算价格
const price = unitPrice * tokens.length;
console.log('chat bill');
console.log('token len:', tokens.length);
console.log('text len: ', text.length); console.log('text len: ', text.length);
console.log('price: ', `${formatPrice(price)}`); console.log('token len:', tokens.length);
try { if (isPay) {
// 插入 Bill 记录 await connectToDatabase();
const res = await Bill.create({
userId,
type: 'chat',
modelName,
chatId,
textLen: text.length,
tokenLen: tokens.length,
price
});
billId = res._id;
// 账号扣费 // 获取模型单价格
await User.findByIdAndUpdate(userId, { const modelItem = modelList.find((item) => item.model === modelName);
$inc: { balance: -price } // 计算价格
}); const unitPrice = modelItem?.price || 5;
} catch (error) { const price = unitPrice * tokens.length;
console.log('创建账单失败:', error); console.log(`chat bill, price: ${formatPrice(price)}`);
billId && Bill.findByIdAndDelete(billId);
try {
// 插入 Bill 记录
const res = await Bill.create({
userId,
type: 'chat',
modelName,
chatId,
textLen: text.length,
tokenLen: tokens.length,
price
});
billId = res._id;
// 账号扣费
await User.findByIdAndUpdate(userId, {
$inc: { balance: -price }
});
} catch (error) {
console.log('创建账单失败:', error);
billId && Bill.findByIdAndDelete(billId);
}
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@@ -61,10 +64,12 @@ export const pushChatBill = async ({
}; };
export const pushSplitDataBill = async ({ export const pushSplitDataBill = async ({
isPay,
userId, userId,
text, text,
type type
}: { }: {
isPay: boolean;
userId: string; userId: string;
text: string; text: string;
type: DataType; type: DataType;
@@ -74,39 +79,41 @@ export const pushSplitDataBill = async ({
let billId; let billId;
try { try {
// 获取模型单价格, 都是用 gpt35 拆分
const modelItem = modelList.find((item) => item.model === ChatModelNameEnum.GPT35);
const unitPrice = modelItem?.price || 5;
// 计算 token 数量 // 计算 token 数量
const tokens = encode(text); const tokens = encode(text);
// 计算价格
const price = unitPrice * tokens.length;
console.log('splitData bill');
console.log('token len:', tokens.length);
console.log('text len: ', text.length); console.log('text len: ', text.length);
console.log('price: ', `${formatPrice(price)}`); console.log('token len:', tokens.length);
try { if (isPay) {
// 插入 Bill 记录 try {
const res = await Bill.create({ // 获取模型单价格, 都是用 gpt35 拆分
userId, const modelItem = modelList.find((item) => item.model === ChatModelNameEnum.GPT35);
type, const unitPrice = modelItem?.price || 5;
modelName: ChatModelNameEnum.GPT35, // 计算价格
textLen: text.length, const price = unitPrice * tokens.length;
tokenLen: tokens.length,
price
});
billId = res._id;
// 账号扣费 console.log(`splitData bill, price: ${formatPrice(price)}`);
await User.findByIdAndUpdate(userId, {
$inc: { balance: -price } // 插入 Bill 记录
}); const res = await Bill.create({
} catch (error) { userId,
console.log('创建账单失败:', error); type,
billId && Bill.findByIdAndDelete(billId); modelName: ChatModelNameEnum.GPT35,
textLen: text.length,
tokenLen: tokens.length,
price
});
billId = res._id;
// 账号扣费
await User.findByIdAndUpdate(userId, {
$inc: { balance: -price }
});
} catch (error) {
console.log('创建账单失败:', error);
billId && Bill.findByIdAndDelete(billId);
}
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

@@ -28,10 +28,10 @@ export const jsonRes = <T = any>(
} else if (openaiError[error?.response?.statusText]) { } else if (openaiError[error?.response?.statusText]) {
msg = openaiError[error.response.statusText]; msg = openaiError[error.response.statusText];
} }
error?.response && console.log('chat err:', error?.response);
console.log('error->'); console.log('error->');
console.log('code:', error.code); console.log('code:', error.code);
console.log('statusText:', error?.response?.statusText); console.log('statusText:', error?.response?.statusText);
console.log('data len:', error?.response?.config?.data.length);
console.log('msg:', msg); console.log('msg:', msg);
} }