perf: code and inform

This commit is contained in:
archer
2023-07-04 21:24:32 +08:00
parent 8635de866f
commit 2a45fe520b
20 changed files with 97 additions and 163 deletions

View File

@@ -7,7 +7,7 @@
| 计费项 | 价格: 元/ 1K tokens包含上下文| | 计费项 | 价格: 元/ 1K tokens包含上下文|
| --- | --- | | --- | --- |
| 知识库 - 索引 | 0.001 | | 知识库 - 索引 | 0.001 |
| chatgpt - 对话 | 0.025 | | chatgpt - 对话 | 0.015 |
| chatgpt16K - 对话 | 0.025 | | chatgpt16K - 对话 | 0.025 |
| gpt4 - 对话 | 0.45 | | gpt4 - 对话 | 0.45 |
| 文件拆分 | 0.025 | | 文件拆分 | 0.025 |

View File

@@ -19,7 +19,7 @@ FastGpt 项目完全开源,可随意私有化部署,去除平台风险忧虑
| 计费项 | 价格: 元/ 1K tokens包含上下文| | 计费项 | 价格: 元/ 1K tokens包含上下文|
| --- | --- | | --- | --- |
| 知识库 - 索引 | 0.001 | | 知识库 - 索引 | 0.001 |
| chatgpt - 对话 | 0.025 | | chatgpt - 对话 | 0.015 |
| chatgpt16K - 对话 | 0.025 | | chatgpt16K - 对话 | 0.025 |
| gpt4 - 对话 | 0.45 | | gpt4 - 对话 | 0.45 |
| 文件拆分 | 0.025 | | 文件拆分 | 0.025 |

View File

@@ -31,7 +31,7 @@ export const ChatModelMap = {
contextMaxToken: 4000, contextMaxToken: 4000,
systemMaxToken: 2400, systemMaxToken: 2400,
maxTemperature: 1.2, maxTemperature: 1.2,
price: 2.5 price: 1.5
}, },
[OpenAiChatEnum.GPT3516k]: { [OpenAiChatEnum.GPT3516k]: {
chatModel: OpenAiChatEnum.GPT3516k, chatModel: OpenAiChatEnum.GPT3516k,

View File

@@ -129,7 +129,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// 发出请求 // 发出请求
const { streamResponse, responseMessages, responseText, totalTokens } = const { streamResponse, responseMessages, responseText, totalTokens } =
await modelServiceToolMap[model.chat.chatModel].chatCompletion({ await modelServiceToolMap.chatCompletion({
model: model.chat.chatModel,
apiKey, apiKey,
temperature: +temperature, temperature: +temperature,
messages: completePrompts, messages: completePrompts,

View File

@@ -150,13 +150,15 @@ export async function appKbSearch({
} }
]; ];
const fixedSystemTokens = modelToolMap[model.chat.chatModel].countTokens({ const fixedSystemTokens = modelToolMap.countTokens({
model: model.chat.chatModel,
messages: [...userSystemPrompt, ...userLimitPrompt] messages: [...userSystemPrompt, ...userLimitPrompt]
}); });
// filter part quote by maxToken // filter part quote by maxToken
const sliceResult = modelToolMap[model.chat.chatModel] const sliceResult = modelToolMap
.tokenSlice({ .tokenSlice({
model: model.chat.chatModel,
maxToken: modelConstantsData.systemMaxToken - fixedSystemTokens, maxToken: modelConstantsData.systemMaxToken - fixedSystemTokens,
messages: filterSearch.map((item, i) => ({ messages: filterSearch.map((item, i) => ({
obj: ChatRoleEnum.System, obj: ChatRoleEnum.System,

View File

@@ -78,7 +78,8 @@ export async function pushDataToKb({
if (mode === TrainingModeEnum.qa) { if (mode === TrainingModeEnum.qa) {
// count token // count token
const token = modelToolMap[OpenAiChatEnum.GPT3516k].countTokens({ const token = modelToolMap.countTokens({
model: OpenAiChatEnum.GPT3516k,
messages: [{ obj: 'System', value: item.q }] messages: [{ obj: 'System', value: item.q }]
}); });
if (token > modeMaxToken[TrainingModeEnum.qa]) { if (token > modeMaxToken[TrainingModeEnum.qa]) {

View File

@@ -196,7 +196,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// start model api. responseText and totalTokens: valid only if stream = false // start model api. responseText and totalTokens: valid only if stream = false
const { streamResponse, responseMessages, responseText, totalTokens } = const { streamResponse, responseMessages, responseText, totalTokens } =
await modelServiceToolMap[model.chat.chatModel].chatCompletion({ await modelServiceToolMap.chatCompletion({
model: model.chat.chatModel,
apiKey: userOpenAiKey || apiKey, apiKey: userOpenAiKey || apiKey,
temperature: +temperature, temperature: +temperature,
maxToken: model.chat.maxToken, maxToken: model.chat.maxToken,

View File

@@ -9,6 +9,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT3516k]); chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT3516k]);
chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT35]); chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT35]);
chatModelList.push(ChatModelMap[OpenAiChatEnum.textada001]);
chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT4]); chatModelList.push(ChatModelMap[OpenAiChatEnum.GPT4]);
jsonRes(res, { jsonRes(res, {

View File

@@ -4,6 +4,7 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, Inform, User } from '@/service/mongo'; import { connectToDatabase, Inform, User } from '@/service/mongo';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { InformTypeEnum } from '@/constants/user'; import { InformTypeEnum } from '@/constants/user';
import { startSendInform } from '@/service/events/sendInform';
export type Props = { export type Props = {
type: `${InformTypeEnum}`; type: `${InformTypeEnum}`;
@@ -37,25 +38,26 @@ export async function sendInform({ type, title, content, userId }: Props) {
try { try {
if (userId) { if (userId) {
// skip it if have same inform within 5 minutes global.sendInformQueue.push(async () => {
const inform = await Inform.findOne({ // skip it if have same inform within 5 minutes
type, const inform = await Inform.findOne({
title, type,
content, title,
userId, content,
read: false, userId,
time: { $lte: new Date(Date.now() + 5 * 60 * 1000) } time: { $gte: new Date(Date.now() - 5 * 60 * 1000) }
});
if (inform) return;
await Inform.create({
type,
title,
content,
userId
});
}); });
startSendInform();
if (inform) return;
await Inform.create({
type,
title,
content,
userId
});
return; return;
} }

View File

@@ -114,7 +114,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
| 计费项 | 价格: 元/ 1K tokens(包含上下文)| | 计费项 | 价格: 元/ 1K tokens(包含上下文)|
| --- | --- | | --- | --- |
| 知识库 - 索引 | 0.001 | | 知识库 - 索引 | 0.001 |
| chatgpt - 对话 | 0.025 | | chatgpt - 对话 | 0.015 |
| chatgpt16K - 对话 | 0.025 | | chatgpt16K - 对话 | 0.025 |
| gpt4 - 对话 | 0.45 | | gpt4 - 对话 | 0.45 |
| 文件拆分 | 0.025 |`} | 文件拆分 | 0.025 |`}

View File

@@ -63,8 +63,9 @@ export async function generateQA(): Promise<any> {
// 请求 chatgpt 获取回答 // 请求 chatgpt 获取回答
const response = await Promise.all( const response = await Promise.all(
[data.q].map((text) => [data.q].map((text) =>
modelServiceToolMap[OpenAiChatEnum.GPT3516k] modelServiceToolMap
.chatCompletion({ .chatCompletion({
model: OpenAiChatEnum.GPT3516k,
apiKey: systemAuthKey, apiKey: systemAuthKey,
temperature: 0.8, temperature: 0.8,
messages: [ messages: [

View File

@@ -0,0 +1,16 @@
export const startSendInform = async () => {
if (global.sendInformQueue.length === 0 || global.sendInformQueueLen > 0) return;
global.sendInformQueueLen++;
try {
const fn = global.sendInformQueue[global.sendInformQueue.length - 1];
await fn();
global.sendInformQueue.pop();
global.sendInformQueueLen--;
startSendInform();
} catch (error) {
global.sendInformQueueLen--;
startSendInform();
}
};

View File

@@ -20,6 +20,8 @@ export async function connectToDatabase(): Promise<void> {
pgIvfflatProbe: 10, pgIvfflatProbe: 10,
sensitiveCheck: false sensitiveCheck: false
}; };
global.sendInformQueue = [];
global.sendInformQueueLen = 0;
// proxy obj // proxy obj
if (process.env.AXIOS_PROXY_HOST && process.env.AXIOS_PROXY_PORT) { if (process.env.AXIOS_PROXY_HOST && process.env.AXIOS_PROXY_PORT) {
global.httpsAgent = tunnel.httpsOverHttp({ global.httpsAgent = tunnel.httpsOverHttp({

View File

@@ -181,33 +181,13 @@ export const getApiKey = async ({
return Promise.reject(ERROR_ENUM.unAuthorization); return Promise.reject(ERROR_ENUM.unAuthorization);
} }
const keyMap = { const userOpenAiKey = user.openaiKey || '';
[OpenAiChatEnum.GPT35]: { const systemAuthKey = getSystemOpenAiKey();
userOpenAiKey: user.openaiKey || '',
systemAuthKey: getSystemOpenAiKey()
},
[OpenAiChatEnum.GPT3516k]: {
userOpenAiKey: user.openaiKey || '',
systemAuthKey: getSystemOpenAiKey()
},
[OpenAiChatEnum.GPT4]: {
userOpenAiKey: user.openaiKey || '',
systemAuthKey: getSystemOpenAiKey()
},
[OpenAiChatEnum.GPT432k]: {
userOpenAiKey: user.openaiKey || '',
systemAuthKey: getSystemOpenAiKey()
}
};
if (!keyMap[model]) {
return Promise.reject('App model is exists');
}
// 有自己的key // 有自己的key
if (!mustPay && keyMap[model].userOpenAiKey) { if (!mustPay && userOpenAiKey) {
return { return {
userOpenAiKey: keyMap[model].userOpenAiKey, userOpenAiKey,
systemAuthKey: '' systemAuthKey: ''
}; };
} }
@@ -219,7 +199,7 @@ export const getApiKey = async ({
return { return {
userOpenAiKey: '', userOpenAiKey: '',
systemAuthKey: keyMap[model].systemAuthKey systemAuthKey
}; };
}; };

View File

@@ -27,6 +27,7 @@ export type StreamResponseType = {
chatResponse: any; chatResponse: any;
prompts: ChatItemType[]; prompts: ChatItemType[];
res: NextApiResponse; res: NextApiResponse;
model: `${OpenAiChatEnum}`;
[key: string]: any; [key: string]: any;
}; };
export type StreamResponseReturnType = { export type StreamResponseReturnType = {
@@ -35,49 +36,9 @@ export type StreamResponseReturnType = {
finishMessages: ChatItemType[]; finishMessages: ChatItemType[];
}; };
export const modelServiceToolMap: Record< export const modelServiceToolMap = {
ChatModelType, chatCompletion: chatResponse,
{ streamResponse: openAiStreamResponse
chatCompletion: (data: ChatCompletionType) => Promise<ChatCompletionResponseType>;
streamResponse: (data: StreamResponseType) => Promise<StreamResponseReturnType>;
}
> = {
[OpenAiChatEnum.GPT35]: {
chatCompletion: (data: ChatCompletionType) =>
chatResponse({ model: OpenAiChatEnum.GPT35, ...data }),
streamResponse: (data: StreamResponseType) =>
openAiStreamResponse({
model: OpenAiChatEnum.GPT35,
...data
})
},
[OpenAiChatEnum.GPT3516k]: {
chatCompletion: (data: ChatCompletionType) =>
chatResponse({ model: OpenAiChatEnum.GPT3516k, ...data }),
streamResponse: (data: StreamResponseType) =>
openAiStreamResponse({
model: OpenAiChatEnum.GPT3516k,
...data
})
},
[OpenAiChatEnum.GPT4]: {
chatCompletion: (data: ChatCompletionType) =>
chatResponse({ model: OpenAiChatEnum.GPT4, ...data }),
streamResponse: (data: StreamResponseType) =>
openAiStreamResponse({
model: OpenAiChatEnum.GPT4,
...data
})
},
[OpenAiChatEnum.GPT432k]: {
chatCompletion: (data: ChatCompletionType) =>
chatResponse({ model: OpenAiChatEnum.GPT432k, ...data }),
streamResponse: (data: StreamResponseType) =>
openAiStreamResponse({
model: OpenAiChatEnum.GPT432k,
...data
})
}
}; };
/* delete invalid symbol */ /* delete invalid symbol */
@@ -124,7 +85,8 @@ export const ChatContextFilter = ({
} }
// 去掉 system 的 token // 去掉 system 的 token
maxTokens -= modelToolMap[model].countTokens({ maxTokens -= modelToolMap.countTokens({
model,
messages: systemPrompts messages: systemPrompts
}); });
@@ -135,7 +97,8 @@ export const ChatContextFilter = ({
for (let i = chatPrompts.length - 1; i >= 0; i--) { for (let i = chatPrompts.length - 1; i >= 0; i--) {
chats.unshift(chatPrompts[i]); chats.unshift(chatPrompts[i]);
const tokens = modelToolMap[model].countTokens({ const tokens = modelToolMap.countTokens({
model,
messages: chats messages: chats
}); });
@@ -164,13 +127,14 @@ export const resStreamResponse = async ({
res.setHeader('X-Accel-Buffering', 'no'); res.setHeader('X-Accel-Buffering', 'no');
res.setHeader('Cache-Control', 'no-cache, no-transform'); res.setHeader('Cache-Control', 'no-cache, no-transform');
const { responseContent, totalTokens, finishMessages } = await modelServiceToolMap[ const { responseContent, totalTokens, finishMessages } = await modelServiceToolMap.streamResponse(
model {
].streamResponse({ chatResponse,
chatResponse, prompts,
prompts, res,
res model
}); }
);
return { responseContent, totalTokens, finishMessages }; return { responseContent, totalTokens, finishMessages };
}; };
@@ -259,7 +223,8 @@ export const V2_StreamResponse = async ({
value: responseContent value: responseContent
}); });
const totalTokens = modelToolMap[model].countTokens({ const totalTokens = modelToolMap.countTokens({
model,
messages: finishMessages messages: finishMessages
}); });

View File

@@ -35,7 +35,8 @@ export const chatResponse = async ({
const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false }); const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false });
const chatAPI = getOpenAIApi(apiKey); const chatAPI = getOpenAIApi(apiKey);
const promptsToken = modelToolMap[model].countTokens({ const promptsToken = modelToolMap.countTokens({
model,
messages: filterMessages messages: filterMessages
}); });
@@ -116,7 +117,8 @@ export const openAiStreamResponse = async ({
value: responseContent value: responseContent
}); });
const totalTokens = modelToolMap[model].countTokens({ const totalTokens = modelToolMap.countTokens({
model,
messages: finishMessages messages: finishMessages
}); });

View File

@@ -21,7 +21,9 @@ declare global {
var QRCode: any; var QRCode: any;
var qaQueueLen: number; var qaQueueLen: number;
var vectorQueueLen: number; var vectorQueueLen: number;
var OpenAiEncMap: Record<string, Tiktoken>; var OpenAiEncMap: Tiktoken;
var sendInformQueue: (() => Promise<void>)[];
var sendInformQueueLen: number;
var systemEnv: { var systemEnv: {
vectorMaxProcess: number; vectorMaxProcess: number;
qaMaxProcess: number; qaMaxProcess: number;

View File

@@ -152,7 +152,7 @@ export const splitText_token = ({ text, maxLen }: { text: string; maxLen: number
const slideLen = Math.floor(maxLen * 0.3); const slideLen = Math.floor(maxLen * 0.3);
try { try {
const enc = getOpenAiEncMap()[OpenAiChatEnum.GPT35]; const enc = getOpenAiEncMap();
// filter empty text. encode sentence // filter empty text. encode sentence
const encodeText = enc.encode(text); const encodeText = enc.encode(text);

View File

@@ -4,32 +4,8 @@ import type { ChatItemType } from '@/types/chat';
import { countOpenAIToken, openAiSliceTextByToken } from './openai'; import { countOpenAIToken, openAiSliceTextByToken } from './openai';
import { gpt_chatItemTokenSlice } from '@/pages/api/openapi/text/gptMessagesSlice'; import { gpt_chatItemTokenSlice } from '@/pages/api/openapi/text/gptMessagesSlice';
export const modelToolMap: Record< export const modelToolMap = {
ChatModelType, countTokens: countOpenAIToken,
{ sliceText: openAiSliceTextByToken,
countTokens: (data: { messages: ChatItemType[] }) => number; tokenSlice: gpt_chatItemTokenSlice
sliceText: (data: { text: string; length: number }) => string;
tokenSlice: (data: { messages: ChatItemType[]; maxToken: number }) => ChatItemType[];
}
> = {
[OpenAiChatEnum.GPT35]: {
countTokens: ({ messages }) => countOpenAIToken({ model: OpenAiChatEnum.GPT35, messages }),
sliceText: (data) => openAiSliceTextByToken({ model: OpenAiChatEnum.GPT35, ...data }),
tokenSlice: (data) => gpt_chatItemTokenSlice({ model: OpenAiChatEnum.GPT35, ...data })
},
[OpenAiChatEnum.GPT3516k]: {
countTokens: ({ messages }) => countOpenAIToken({ model: OpenAiChatEnum.GPT3516k, messages }),
sliceText: (data) => openAiSliceTextByToken({ model: OpenAiChatEnum.GPT3516k, ...data }),
tokenSlice: (data) => gpt_chatItemTokenSlice({ model: OpenAiChatEnum.GPT3516k, ...data })
},
[OpenAiChatEnum.GPT4]: {
countTokens: ({ messages }) => countOpenAIToken({ model: OpenAiChatEnum.GPT4, messages }),
sliceText: (data) => openAiSliceTextByToken({ model: OpenAiChatEnum.GPT4, ...data }),
tokenSlice: (data) => gpt_chatItemTokenSlice({ model: OpenAiChatEnum.GPT4, ...data })
},
[OpenAiChatEnum.GPT432k]: {
countTokens: ({ messages }) => countOpenAIToken({ model: OpenAiChatEnum.GPT432k, messages }),
sliceText: (data) => openAiSliceTextByToken({ model: OpenAiChatEnum.GPT432k, ...data }),
tokenSlice: (data) => gpt_chatItemTokenSlice({ model: OpenAiChatEnum.GPT432k, ...data })
}
}; };

View File

@@ -4,7 +4,6 @@ import { ChatRoleEnum } from '@/constants/chat';
import { ChatCompletionRequestMessageRoleEnum } from 'openai'; import { ChatCompletionRequestMessageRoleEnum } from 'openai';
import { OpenAiChatEnum } from '@/constants/model'; import { OpenAiChatEnum } from '@/constants/model';
import axios from 'axios'; import axios from 'axios';
import dayjs from 'dayjs';
import type { MessageItemType } from '@/pages/api/openapi/v1/chat/completions'; import type { MessageItemType } from '@/pages/api/openapi/v1/chat/completions';
export const getOpenAiEncMap = () => { export const getOpenAiEncMap = () => {
@@ -14,28 +13,11 @@ export const getOpenAiEncMap = () => {
if (typeof global !== 'undefined' && global.OpenAiEncMap) { if (typeof global !== 'undefined' && global.OpenAiEncMap) {
return global.OpenAiEncMap; return global.OpenAiEncMap;
} }
const enc = { const enc = encoding_for_model('gpt-3.5-turbo', {
[OpenAiChatEnum.GPT35]: encoding_for_model('gpt-3.5-turbo', { '<|im_start|>': 100264,
'<|im_start|>': 100264, '<|im_end|>': 100265,
'<|im_end|>': 100265, '<|im_sep|>': 100266
'<|im_sep|>': 100266 });
}),
[OpenAiChatEnum.GPT3516k]: encoding_for_model('gpt-3.5-turbo', {
'<|im_start|>': 100264,
'<|im_end|>': 100265,
'<|im_sep|>': 100266
}),
[OpenAiChatEnum.GPT4]: encoding_for_model('gpt-4', {
'<|im_start|>': 100264,
'<|im_end|>': 100265,
'<|im_sep|>': 100266
}),
[OpenAiChatEnum.GPT432k]: encoding_for_model('gpt-4-32k', {
'<|im_start|>': 100264,
'<|im_end|>': 100265,
'<|im_sep|>': 100266
})
};
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.OpenAiEncMap = enc; window.OpenAiEncMap = enc;
@@ -78,7 +60,7 @@ export function countOpenAIToken({
const adaptMessages = adaptChatItem_openAI({ messages, reserveId: true }); const adaptMessages = adaptChatItem_openAI({ messages, reserveId: true });
const token = adaptMessages.reduce((sum, item) => { const token = adaptMessages.reduce((sum, item) => {
const text = `${item.role}\n${item.content}`; const text = `${item.role}\n${item.content}`;
const enc = getOpenAiEncMap()[model]; const enc = getOpenAiEncMap();
const encodeText = enc.encode(text); const encodeText = enc.encode(text);
const tokens = encodeText.length + diffVal; const tokens = encodeText.length + diffVal;
return sum + tokens; return sum + tokens;
@@ -96,7 +78,7 @@ export const openAiSliceTextByToken = ({
text: string; text: string;
length: number; length: number;
}) => { }) => {
const enc = getOpenAiEncMap()[model]; const enc = getOpenAiEncMap();
const encodeText = enc.encode(text); const encodeText = enc.encode(text);
const decoder = new TextDecoder(); const decoder = new TextDecoder();
return decoder.decode(enc.decode(encodeText.slice(0, length))); return decoder.decode(enc.decode(encodeText.slice(0, length)));