feat: app detail
This commit is contained in:
53
client/src/pages/api/app/data/tokenUsage.ts
Normal file
53
client/src/pages/api/app/data/tokenUsage.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
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 { ChatHistoryItemType } from '@/types/chat';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
/* get one app chat history content number. */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { appId, start, end } = req.body as { appId: string; start: Date; end: Date };
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const result = await Bill.aggregate([
|
||||
{
|
||||
$match: {
|
||||
appId: new Types.ObjectId(appId),
|
||||
userId: new Types.ObjectId(userId),
|
||||
time: { $gte: new Date(start) }
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
year: { $year: '$time' },
|
||||
month: { $month: '$time' },
|
||||
day: { $dayOfMonth: '$time' }
|
||||
},
|
||||
tokenLen: { $sum: '$tokenLen' } // 对tokenLen的值求和
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } },
|
||||
tokenLen: 1
|
||||
}
|
||||
},
|
||||
{ $sort: { date: 1 } }
|
||||
]);
|
||||
|
||||
jsonRes(res, {
|
||||
data: result
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 验证是否是该用户的 model
|
||||
// 验证是否是该用户的 app
|
||||
await authApp({
|
||||
appId,
|
||||
userId
|
||||
|
||||
@@ -6,22 +6,22 @@ import { Types } from 'mongoose';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { chatId, historyId } = req.query as {
|
||||
chatId: string;
|
||||
const { historyId, contentId } = req.query as {
|
||||
historyId: string;
|
||||
contentId: string;
|
||||
};
|
||||
await connectToDatabase();
|
||||
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
if (!chatId || !historyId) {
|
||||
if (!historyId || !contentId) {
|
||||
throw new Error('params is error');
|
||||
}
|
||||
|
||||
const history = await Chat.aggregate([
|
||||
{
|
||||
$match: {
|
||||
_id: new Types.ObjectId(chatId),
|
||||
_id: new Types.ObjectId(historyId),
|
||||
userId: new Types.ObjectId(userId)
|
||||
}
|
||||
},
|
||||
@@ -30,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
},
|
||||
{
|
||||
$match: {
|
||||
'content._id': new Types.ObjectId(historyId)
|
||||
'content._id': new Types.ObjectId(contentId)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,13 +7,13 @@ import { Types } from 'mongoose';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
let {
|
||||
chatId,
|
||||
historyId,
|
||||
contentId,
|
||||
quoteId,
|
||||
sourceText = ''
|
||||
} = req.query as {
|
||||
chatId: string;
|
||||
historyId: string;
|
||||
contentId: string;
|
||||
quoteId: string;
|
||||
sourceText: string;
|
||||
};
|
||||
@@ -21,15 +21,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
if (!chatId || !historyId || !quoteId) {
|
||||
if (!contentId || !historyId || !quoteId) {
|
||||
throw new Error('params is error');
|
||||
}
|
||||
|
||||
await Chat.updateOne(
|
||||
{
|
||||
_id: new Types.ObjectId(chatId),
|
||||
_id: new Types.ObjectId(historyId),
|
||||
userId: new Types.ObjectId(userId),
|
||||
'content._id': new Types.ObjectId(historyId)
|
||||
'content._id': new Types.ObjectId(contentId)
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
|
||||
@@ -53,7 +53,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } =
|
||||
await (async () => {
|
||||
if (historyId) {
|
||||
// auth chatId
|
||||
// auth historyId
|
||||
const chat = await Chat.findOne({
|
||||
_id: historyId,
|
||||
userId
|
||||
|
||||
@@ -53,14 +53,14 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
throw new Error('params is error');
|
||||
}
|
||||
|
||||
// auth model
|
||||
// auth app
|
||||
const { app } = await authApp({
|
||||
appId,
|
||||
userId
|
||||
});
|
||||
|
||||
const result = await appKbSearch({
|
||||
model: app,
|
||||
app,
|
||||
userId,
|
||||
fixedQuote: [],
|
||||
prompt: prompts[prompts.length - 1],
|
||||
@@ -81,21 +81,21 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
});
|
||||
|
||||
export async function appKbSearch({
|
||||
model,
|
||||
app,
|
||||
userId,
|
||||
fixedQuote = [],
|
||||
prompt,
|
||||
similarity = 0.8,
|
||||
limit = 5
|
||||
}: {
|
||||
model: AppSchema;
|
||||
app: AppSchema;
|
||||
userId: string;
|
||||
fixedQuote?: QuoteItemType[];
|
||||
prompt: ChatItemType;
|
||||
similarity: number;
|
||||
limit: number;
|
||||
}): Promise<Response> {
|
||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||
const modelConstantsData = ChatModelMap[app.chat.chatModel];
|
||||
|
||||
// get vector
|
||||
const promptVector = await openaiEmbedding({
|
||||
@@ -107,7 +107,7 @@ export async function appKbSearch({
|
||||
const res: any = await PgClient.query(
|
||||
`BEGIN;
|
||||
SET LOCAL ivfflat.probes = ${global.systemEnv.pgIvfflatProbe || 10};
|
||||
select id,q,a,source from modelData where kb_id IN (${model.chat.relatedKbs
|
||||
select id,q,a,source from modelData where kb_id IN (${app.chat.relatedKbs
|
||||
.map((item) => `'${item}'`)
|
||||
.join(',')}) AND vector <#> '[${promptVector[0]}]' < -${similarity} order by vector <#> '[${
|
||||
promptVector[0]
|
||||
@@ -133,32 +133,32 @@ export async function appKbSearch({
|
||||
});
|
||||
|
||||
// 计算固定提示词的 token 数量
|
||||
const userSystemPrompt = model.chat.systemPrompt // user system prompt
|
||||
const userSystemPrompt = app.chat.systemPrompt // user system prompt
|
||||
? [
|
||||
{
|
||||
obj: ChatRoleEnum.System,
|
||||
value: model.chat.systemPrompt
|
||||
value: app.chat.systemPrompt
|
||||
}
|
||||
]
|
||||
: [];
|
||||
const userLimitPrompt = [
|
||||
{
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: model.chat.limitPrompt
|
||||
? model.chat.limitPrompt
|
||||
: `知识库是关于 ${model.name} 的内容,参考知识库回答问题。与 "${model.name}" 无关内容,直接回复: "我不知道"。`
|
||||
value: app.chat.limitPrompt
|
||||
? app.chat.limitPrompt
|
||||
: `知识库是关于 ${app.name} 的内容,参考知识库回答问题。与 "${app.name}" 无关内容,直接回复: "我不知道"。`
|
||||
}
|
||||
];
|
||||
|
||||
const fixedSystemTokens = modelToolMap.countTokens({
|
||||
model: model.chat.chatModel,
|
||||
model: app.chat.chatModel,
|
||||
messages: [...userSystemPrompt, ...userLimitPrompt]
|
||||
});
|
||||
|
||||
// filter part quote by maxToken
|
||||
const sliceResult = modelToolMap
|
||||
.tokenSlice({
|
||||
model: model.chat.chatModel,
|
||||
model: app.chat.chatModel,
|
||||
maxToken: modelConstantsData.systemMaxToken - fixedSystemTokens,
|
||||
messages: filterSearch.map((item, i) => ({
|
||||
obj: ChatRoleEnum.System,
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function extract({ agents, history = [], userChatInput, description
|
||||
agents.forEach((item) => {
|
||||
properties[item.key] = {
|
||||
type: 'string',
|
||||
description: item.desc
|
||||
description: item.value
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import { Types } from 'mongoose';
|
||||
import { moduleFetch } from '@/service/api/request';
|
||||
import { AppModuleItemType, RunningModuleItemType } from '@/types/app';
|
||||
import { FlowInputItemTypeEnum } from '@/constants/flow';
|
||||
import { pushChatBill } from '@/service/events/pushBill';
|
||||
import { BillTypeEnum } from '@/constants/user';
|
||||
|
||||
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string };
|
||||
type FastGptWebChatProps = {
|
||||
@@ -168,6 +170,16 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
pushChatBill({
|
||||
isPay: true,
|
||||
chatModel: 'gpt-3.5-turbo',
|
||||
userId,
|
||||
appId,
|
||||
textLen: 1,
|
||||
tokens: 100,
|
||||
type: BillTypeEnum.chat
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (stream) {
|
||||
res.status(500);
|
||||
|
||||
@@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
kbId: id
|
||||
});
|
||||
|
||||
// delete related model
|
||||
// delete related app
|
||||
await App.updateMany(
|
||||
{
|
||||
userId
|
||||
|
||||
Reference in New Issue
Block a user