mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-27 00:17:31 +00:00
perf: bill logs
This commit is contained in:
@@ -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) {
|
||||||
// 直接结束流
|
// 直接结束流
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user