Commit 1ffe1be5 by archer

perf: response store

parent ba965320
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
"model": "gpt-3.5-turbo", "model": "gpt-3.5-turbo",
"name": "FastAI-4k", "name": "FastAI-4k",
"contextMaxToken": 4000, "contextMaxToken": 4000,
"systemMaxToken": 2400, "quoteMaxToken": 2000,
"maxTemperature": 1.2, "maxTemperature": 1.2,
"price": 1.5 "price": 1.5
}, },
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
"model": "gpt-3.5-turbo-16k", "model": "gpt-3.5-turbo-16k",
"name": "FastAI-16k", "name": "FastAI-16k",
"contextMaxToken": 16000, "contextMaxToken": 16000,
"systemMaxToken": 8000, "quoteMaxToken": 8000,
"maxTemperature": 1.2, "maxTemperature": 1.2,
"price": 3 "price": 3
}, },
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
"model": "gpt-4", "model": "gpt-4",
"name": "FastAI-Plus", "name": "FastAI-Plus",
"contextMaxToken": 8000, "contextMaxToken": 8000,
"systemMaxToken": 4000, "quoteMaxToken": 4000,
"maxTemperature": 1.2, "maxTemperature": 1.2,
"price": 45 "price": 45
} }
......
import { sseResponseEventEnum } from '@/constants/chat'; import { sseResponseEventEnum, TaskResponseKeyEnum } from '@/constants/chat';
import { getErrText } from '@/utils/tools'; import { getErrText } from '@/utils/tools';
import { parseStreamChunk } from '@/utils/adapt'; import { parseStreamChunk } from '@/utils/adapt';
import { QuoteItemType } from '@/types/chat'; import type { ChatHistoryItemResType } from '@/types/chat';
interface StreamFetchProps { interface StreamFetchProps {
url?: string; url?: string;
...@@ -17,8 +17,7 @@ export const streamFetch = ({ ...@@ -17,8 +17,7 @@ export const streamFetch = ({
}: StreamFetchProps) => }: StreamFetchProps) =>
new Promise<{ new Promise<{
responseText: string; responseText: string;
errMsg: string; [TaskResponseKeyEnum.responseData]: ChatHistoryItemResType[];
newChatId: string | null;
}>(async (resolve, reject) => { }>(async (resolve, reject) => {
try { try {
const response = await window.fetch(url, { const response = await window.fetch(url, {
...@@ -42,7 +41,7 @@ export const streamFetch = ({ ...@@ -42,7 +41,7 @@ export const streamFetch = ({
// response data // response data
let responseText = ''; let responseText = '';
let errMsg = ''; let errMsg = '';
const newChatId = response.headers.get('newChatId'); let responseData: ChatHistoryItemResType[] = [];
const read = async () => { const read = async () => {
try { try {
...@@ -51,8 +50,7 @@ export const streamFetch = ({ ...@@ -51,8 +50,7 @@ export const streamFetch = ({
if (response.status === 200 && !errMsg) { if (response.status === 200 && !errMsg) {
return resolve({ return resolve({
responseText, responseText,
errMsg, responseData
newChatId
}); });
} else { } else {
return reject({ return reject({
...@@ -78,7 +76,7 @@ export const streamFetch = ({ ...@@ -78,7 +76,7 @@ export const streamFetch = ({
onMessage(answer); onMessage(answer);
responseText += answer; responseText += answer;
} else if (item.event === sseResponseEventEnum.appStreamResponse) { } else if (item.event === sseResponseEventEnum.appStreamResponse) {
console.log(data); responseData = data;
} else if (item.event === sseResponseEventEnum.error) { } else if (item.event === sseResponseEventEnum.error) {
errMsg = getErrText(data, '流响应错误'); errMsg = getErrText(data, '流响应错误');
} }
...@@ -88,8 +86,7 @@ export const streamFetch = ({ ...@@ -88,8 +86,7 @@ export const streamFetch = ({
if (err?.message === 'The user aborted a request.') { if (err?.message === 'The user aborted a request.') {
return resolve({ return resolve({
responseText, responseText,
errMsg, responseData
newChatId
}); });
} }
reject(getErrText(err, '请求异常')); reject(getErrText(err, '请求异常'));
......
import React from 'react';
const ResponseDetailModal = () => {
return <div>ResponseDetailModal</div>;
};
export default ResponseDetailModal;
...@@ -9,7 +9,12 @@ import React, { ...@@ -9,7 +9,12 @@ import React, {
useEffect useEffect
} from 'react'; } from 'react';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import { ChatItemType, ChatSiteItemType, ExportChatType } from '@/types/chat'; import {
ChatHistoryItemResType,
ChatItemType,
ChatSiteItemType,
ExportChatType
} from '@/types/chat';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { import {
useCopyData, useCopyData,
...@@ -35,6 +40,7 @@ import { useRouter } from 'next/router'; ...@@ -35,6 +40,7 @@ import { useRouter } from 'next/router';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import { QuoteItemType } from '@/types/chat'; import { QuoteItemType } from '@/types/chat';
import { FlowModuleTypeEnum } from '@/constants/flow'; import { FlowModuleTypeEnum } from '@/constants/flow';
import { TaskResponseKeyEnum } from '@/constants/chat';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
const QuoteModal = dynamic(() => import('./QuoteModal')); const QuoteModal = dynamic(() => import('./QuoteModal'));
...@@ -131,9 +137,10 @@ const ChatBox = ( ...@@ -131,9 +137,10 @@ const ChatBox = (
variableModules?: VariableItemType[]; variableModules?: VariableItemType[];
welcomeText?: string; welcomeText?: string;
onUpdateVariable?: (e: Record<string, any>) => void; onUpdateVariable?: (e: Record<string, any>) => void;
onStartChat: ( onStartChat: (e: StartChatFnProps) => Promise<{
e: StartChatFnProps responseText: string;
) => Promise<{ responseText?: string; rawSearch?: QuoteItemType[] }>; [TaskResponseKeyEnum.responseData]: ChatHistoryItemResType[];
}>;
onDelMessage?: (e: { contentId?: string; index: number }) => void; onDelMessage?: (e: { contentId?: string; index: number }) => void;
}, },
ref: ForwardedRef<ComponentRef> ref: ForwardedRef<ComponentRef>
...@@ -294,7 +301,7 @@ const ChatBox = ( ...@@ -294,7 +301,7 @@ const ChatBox = (
const messages = adaptChatItem_openAI({ messages: newChatList, reserveId: true }); const messages = adaptChatItem_openAI({ messages: newChatList, reserveId: true });
const { rawSearch } = await onStartChat({ const { responseData } = await onStartChat({
messages, messages,
controller: abortSignal, controller: abortSignal,
generatingMessage, generatingMessage,
...@@ -308,7 +315,7 @@ const ChatBox = ( ...@@ -308,7 +315,7 @@ const ChatBox = (
return { return {
...item, ...item,
status: 'finish', status: 'finish',
rawSearch responseData
}; };
}) })
); );
......
...@@ -51,5 +51,11 @@ export const ChatSourceMap = { ...@@ -51,5 +51,11 @@ export const ChatSourceMap = {
} }
}; };
export enum ChatModuleEnum {
'AIChat' = 'AI Chat',
'KBSearch' = 'KB Search',
'CQ' = 'Classify Question'
}
export const HUMAN_ICON = `https://fastgpt.run/icon/human.png`; export const HUMAN_ICON = `https://fastgpt.run/icon/human.png`;
export const LOGO_ICON = `https://fastgpt.run/icon/logo.png`; export const LOGO_ICON = `https://fastgpt.run/icon/logo.png`;
...@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
// 删除一条数据库记录 // 删除一条数据库记录
await Chat.updateOne( await Chat.updateOne(
{ {
_id: chatId, chatId,
userId userId
}, },
{ $pull: { content: { _id: contentId } } } { $pull: { content: { _id: contentId } } }
......
...@@ -8,6 +8,7 @@ import { authApp } from '@/service/utils/auth'; ...@@ -8,6 +8,7 @@ import { authApp } from '@/service/utils/auth';
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import type { ChatSchema } from '@/types/mongoSchema'; import type { ChatSchema } from '@/types/mongoSchema';
import { getSpecialModule } from '@/components/ChatBox'; import { getSpecialModule } from '@/components/ChatBox';
import { TaskResponseKeyEnum } from '@/constants/chat';
/* 初始化我的聊天框,需要身份验证 */ /* 初始化我的聊天框,需要身份验证 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
...@@ -67,7 +68,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -67,7 +68,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
$project: { $project: {
_id: '$content._id', _id: '$content._id',
obj: '$content.obj', obj: '$content.obj',
value: '$content.value' value: '$content.value',
[TaskResponseKeyEnum.responseData]: `$content.${TaskResponseKeyEnum.responseData}`
} }
} }
]) ])
......
...@@ -46,7 +46,7 @@ export function gpt_chatItemTokenSlice({ ...@@ -46,7 +46,7 @@ export function gpt_chatItemTokenSlice({
maxToken maxToken
}: { }: {
messages: ChatItemType[]; messages: ChatItemType[];
model?: ModelType; model?: string;
maxToken: number; maxToken: number;
}) { }) {
let result: ChatItemType[] = []; let result: ChatItemType[] = [];
......
...@@ -68,7 +68,7 @@ export function setDefaultData() { ...@@ -68,7 +68,7 @@ export function setDefaultData() {
model: 'gpt-3.5-turbo', model: 'gpt-3.5-turbo',
name: 'FastAI-4k', name: 'FastAI-4k',
contextMaxToken: 4000, contextMaxToken: 4000,
systemMaxToken: 2400, quoteMaxToken: 2400,
maxTemperature: 1.2, maxTemperature: 1.2,
price: 1.5 price: 1.5
}, },
...@@ -76,7 +76,7 @@ export function setDefaultData() { ...@@ -76,7 +76,7 @@ export function setDefaultData() {
model: 'gpt-3.5-turbo-16k', model: 'gpt-3.5-turbo-16k',
name: 'FastAI-16k', name: 'FastAI-16k',
contextMaxToken: 16000, contextMaxToken: 16000,
systemMaxToken: 8000, quoteMaxToken: 8000,
maxTemperature: 1.2, maxTemperature: 1.2,
price: 3 price: 3
}, },
...@@ -84,7 +84,7 @@ export function setDefaultData() { ...@@ -84,7 +84,7 @@ export function setDefaultData() {
model: 'gpt-4', model: 'gpt-4',
name: 'FastAI-Plus', name: 'FastAI-Plus',
contextMaxToken: 8000, contextMaxToken: 8000,
systemMaxToken: 4000, quoteMaxToken: 4000,
maxTemperature: 1.2, maxTemperature: 1.2,
price: 45 price: 45
} }
......
...@@ -454,7 +454,7 @@ const ChatTest = ({ appId }: { appId: string }) => { ...@@ -454,7 +454,7 @@ const ChatTest = ({ appId }: { appId: string }) => {
const history = messages.slice(-historyMaxLen - 2, -2); const history = messages.slice(-historyMaxLen - 2, -2);
// 流请求,获取数据 // 流请求,获取数据
const { responseText } = await streamFetch({ const { responseText, responseData } = await streamFetch({
url: '/api/chat/chatTest', url: '/api/chat/chatTest',
data: { data: {
history, history,
...@@ -468,7 +468,7 @@ const ChatTest = ({ appId }: { appId: string }) => { ...@@ -468,7 +468,7 @@ const ChatTest = ({ appId }: { appId: string }) => {
abortSignal: controller abortSignal: controller
}); });
return { responseText }; return { responseText, responseData };
}, },
[modules, appId, appDetail.name] [modules, appId, appDetail.name]
); );
......
...@@ -49,7 +49,7 @@ const ChatTest = ( ...@@ -49,7 +49,7 @@ const ChatTest = (
const history = messages.slice(-historyMaxLen - 2, -2); const history = messages.slice(-historyMaxLen - 2, -2);
// 流请求,获取数据 // 流请求,获取数据
const { responseText } = await streamFetch({ const { responseText, responseData } = await streamFetch({
url: '/api/chat/chatTest', url: '/api/chat/chatTest',
data: { data: {
history, history,
...@@ -63,7 +63,7 @@ const ChatTest = ( ...@@ -63,7 +63,7 @@ const ChatTest = (
abortSignal: controller abortSignal: controller
}); });
return { responseText }; return { responseText, responseData };
}, },
[app._id, app.name, modules] [app._id, app.name, modules]
); );
......
...@@ -60,7 +60,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => { ...@@ -60,7 +60,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
const prompts = messages.slice(-2); const prompts = messages.slice(-2);
const completionChatId = chatId ? chatId : nanoid(); const completionChatId = chatId ? chatId : nanoid();
const { responseText } = await streamFetch({ const { responseText, responseData } = await streamFetch({
data: { data: {
messages: prompts, messages: prompts,
variables, variables,
...@@ -106,7 +106,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => { ...@@ -106,7 +106,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
history: ChatBoxRef.current?.getChatHistory() || state.history history: ChatBoxRef.current?.getChatHistory() || state.history
})); }));
return { responseText }; return { responseText, responseData };
}, },
[appId, chatId, history, router, setChatData, updateHistory] [appId, chatId, history, router, setChatData, updateHistory]
); );
......
...@@ -46,7 +46,7 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) => ...@@ -46,7 +46,7 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
const prompts = messages.slice(-2); const prompts = messages.slice(-2);
const completionChatId = chatId ? chatId : nanoid(); const completionChatId = chatId ? chatId : nanoid();
const { responseText } = await streamFetch({ const { responseText, responseData } = await streamFetch({
data: { data: {
messages: prompts, messages: prompts,
variables, variables,
...@@ -91,7 +91,7 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) => ...@@ -91,7 +91,7 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
'*' '*'
); );
return { responseText }; return { responseText, responseData };
}, },
[chatId, router, saveChatResponse, shareId] [chatId, router, saveChatResponse, shareId]
); );
......
...@@ -68,7 +68,8 @@ const ChatSchema = new Schema({ ...@@ -68,7 +68,8 @@ const ChatSchema = new Schema({
answer: String, answer: String,
temperature: Number, temperature: Number,
maxToken: Number, maxToken: Number,
finishMessages: Array, quoteList: Array,
completeMessages: Array,
similarity: Number, similarity: Number,
limit: Number, limit: Number,
cqList: Array, cqList: Array,
......
import { adaptChatItem_openAI } from '@/utils/plugin/openai'; import { adaptChatItem_openAI } from '@/utils/plugin/openai';
import { ChatContextFilter } from '@/service/utils/chat/index'; import { ChatContextFilter } from '@/service/utils/chat/index';
import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat'; import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
import { ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat'; import { ChatModuleEnum, ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat';
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai'; import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
import type { ClassifyQuestionAgentItemType } from '@/types/app'; import type { ClassifyQuestionAgentItemType } from '@/types/app';
import { countModelPrice } from '@/service/events/pushBill'; import { countModelPrice } from '@/service/events/pushBill';
...@@ -17,7 +17,6 @@ export type CQResponse = { ...@@ -17,7 +17,6 @@ export type CQResponse = {
[key: string]: any; [key: string]: any;
}; };
const moduleName = 'Classify Question';
const agentModel = 'gpt-3.5-turbo'; const agentModel = 'gpt-3.5-turbo';
const agentFunName = 'agent_user_question'; const agentFunName = 'agent_user_question';
const maxTokens = 2000; const maxTokens = 2000;
...@@ -88,7 +87,7 @@ export const dispatchClassifyQuestion = async (props: Record<string, any>): Prom ...@@ -88,7 +87,7 @@ export const dispatchClassifyQuestion = async (props: Record<string, any>): Prom
return { return {
[result.key]: 1, [result.key]: 1,
[TaskResponseKeyEnum.responseData]: { [TaskResponseKeyEnum.responseData]: {
moduleName, moduleName: ChatModuleEnum.CQ,
price: countModelPrice({ model: agentModel, tokens }), price: countModelPrice({ model: agentModel, tokens }),
model: agentModel, model: agentModel,
tokens, tokens,
......
...@@ -6,12 +6,13 @@ import { modelToolMap } from '@/utils/plugin'; ...@@ -6,12 +6,13 @@ import { modelToolMap } from '@/utils/plugin';
import { ChatContextFilter } from '@/service/utils/chat/index'; import { ChatContextFilter } from '@/service/utils/chat/index';
import type { ChatItemType, QuoteItemType } from '@/types/chat'; import type { ChatItemType, QuoteItemType } from '@/types/chat';
import type { ChatHistoryItemResType } from '@/types/chat'; import type { ChatHistoryItemResType } from '@/types/chat';
import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; import { ChatModuleEnum, ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat';
import { parseStreamChunk, textAdaptGptResponse } from '@/utils/adapt'; import { parseStreamChunk, textAdaptGptResponse } from '@/utils/adapt';
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai'; import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
import { TaskResponseKeyEnum } from '@/constants/chat'; import { TaskResponseKeyEnum } from '@/constants/chat';
import { getChatModel } from '@/service/utils/data'; import { getChatModel } from '@/service/utils/data';
import { countModelPrice } from '@/service/events/pushBill'; import { countModelPrice } from '@/service/events/pushBill';
import { ChatModelItemType } from '@/types/model';
export type ChatProps = { export type ChatProps = {
res: NextApiResponse; res: NextApiResponse;
...@@ -30,8 +31,6 @@ export type ChatResponse = { ...@@ -30,8 +31,6 @@ export type ChatResponse = {
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType; [TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
}; };
const moduleName = 'AI Chat';
/* request openai chat */ /* request openai chat */
export const dispatchChatCompletion = async (props: Record<string, any>): Promise<ChatResponse> => { export const dispatchChatCompletion = async (props: Record<string, any>): Promise<ChatResponse> => {
let { let {
...@@ -54,80 +53,36 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis ...@@ -54,80 +53,36 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
return Promise.reject('The chat model is undefined, you need to select a chat model.'); return Promise.reject('The chat model is undefined, you need to select a chat model.');
} }
// FastGpt temperature range: 1~10 const { filterQuoteQA, quotePrompt } = filterQuote({
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2); quoteQA,
model: modelConstantsData
const limitText = (() => { });
if (limitPrompt) return limitPrompt;
if (quoteQA.length > 0 && !limitPrompt) {
return '根据知识库内容回答问题,仅回复知识库提供的内容,不要对知识库内容做补充说明。';
}
return '';
})();
const quotePrompt =
quoteQA.length > 0
? `下面是知识库内容:
${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')}
`
: '';
const messages: ChatItemType[] = [
...(quotePrompt
? [
{
obj: ChatRoleEnum.System,
value: quotePrompt
}
]
: []),
...(systemPrompt
? [
{
obj: ChatRoleEnum.System,
value: systemPrompt
}
]
: []),
...history,
...(limitText
? [
{
obj: ChatRoleEnum.System,
value: limitText
}
]
: []),
{
obj: ChatRoleEnum.Human,
value: userChatInput
}
];
const modelTokenLimit = getChatModel(model)?.contextMaxToken || 4000;
const filterMessages = ChatContextFilter({ const { messages, filterMessages } = getChatMessages({
model, model: modelConstantsData,
prompts: messages, history,
maxTokens: Math.ceil(modelTokenLimit - 300) // filter token. not response maxToken quotePrompt,
userChatInput,
systemPrompt,
limitPrompt
}); });
const { max_tokens } = getMaxTokens({
model: modelConstantsData,
maxToken,
filterMessages
});
// console.log(messages);
const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false }); // FastGpt temperature range: 1~10
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
const chatAPI = getOpenAIApi(); const chatAPI = getOpenAIApi();
console.log(adaptMessages);
/* count response max token */
const promptsToken = modelToolMap.countTokens({
model,
messages: filterMessages
});
maxToken = maxToken + promptsToken > modelTokenLimit ? modelTokenLimit - promptsToken : maxToken;
const response = await chatAPI.createChatCompletion( const response = await chatAPI.createChatCompletion(
{ {
model, model,
temperature: Number(temperature || 0), temperature: Number(temperature || 0),
max_tokens: maxToken, max_tokens,
messages: adaptMessages, messages,
// frequency_penalty: 0.5, // 越大,重复内容越少 // frequency_penalty: 0.5, // 越大,重复内容越少
// presence_penalty: -0.5, // 越大,越容易出现新内容 // presence_penalty: -0.5, // 越大,越容易出现新内容
stream stream
...@@ -139,30 +94,30 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')} ...@@ -139,30 +94,30 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')}
} }
); );
const { answerText, totalTokens, finishMessages } = await (async () => { const { answerText, totalTokens, completeMessages } = await (async () => {
if (stream) { if (stream) {
// sse response // sse response
const { answer } = await streamResponse({ res, response }); const { answer } = await streamResponse({ res, response });
// count tokens // count tokens
const finishMessages = filterMessages.concat({ const completeMessages = filterMessages.concat({
obj: ChatRoleEnum.AI, obj: ChatRoleEnum.AI,
value: answer value: answer
}); });
const totalTokens = countOpenAIToken({ const totalTokens = countOpenAIToken({
messages: finishMessages messages: completeMessages
}); });
return { return {
answerText: answer, answerText: answer,
totalTokens, totalTokens,
finishMessages completeMessages
}; };
} else { } else {
const answer = stream ? '' : response.data.choices?.[0].message?.content || ''; const answer = stream ? '' : response.data.choices?.[0].message?.content || '';
const totalTokens = stream ? 0 : response.data.usage?.total_tokens || 0; const totalTokens = stream ? 0 : response.data.usage?.total_tokens || 0;
const finishMessages = filterMessages.concat({ const completeMessages = filterMessages.concat({
obj: ChatRoleEnum.AI, obj: ChatRoleEnum.AI,
value: answer value: answer
}); });
...@@ -170,7 +125,7 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')} ...@@ -170,7 +125,7 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')}
return { return {
answerText: answer, answerText: answer,
totalTokens, totalTokens,
finishMessages completeMessages
}; };
} }
})(); })();
...@@ -178,18 +133,140 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')} ...@@ -178,18 +133,140 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')}
return { return {
[TaskResponseKeyEnum.answerText]: answerText, [TaskResponseKeyEnum.answerText]: answerText,
[TaskResponseKeyEnum.responseData]: { [TaskResponseKeyEnum.responseData]: {
moduleName, moduleName: ChatModuleEnum.AIChat,
price: countModelPrice({ model, tokens: totalTokens }), price: countModelPrice({ model, tokens: totalTokens }),
model: modelConstantsData.name, model: modelConstantsData.name,
tokens: totalTokens, tokens: totalTokens,
question: userChatInput, question: userChatInput,
answer: answerText, answer: answerText,
maxToken, maxToken,
finishMessages quoteList: filterQuoteQA,
completeMessages
} }
}; };
}; };
function filterQuote({
quoteQA = [],
model
}: {
quoteQA: ChatProps['quoteQA'];
model: ChatModelItemType;
}) {
const sliceResult = modelToolMap.tokenSlice({
model: model.model,
maxToken: model.quoteMaxToken,
messages: quoteQA.map((item, i) => ({
obj: ChatRoleEnum.System,
value: `${i + 1}. [${item.q}\n${item.a}]`
}))
});
// slice filterSearch
const filterQuoteQA = quoteQA.slice(0, sliceResult.length);
const quotePrompt =
filterQuoteQA.length > 0
? `下面是知识库内容:
${filterQuoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')}
`
: '';
return {
filterQuoteQA,
quotePrompt
};
}
function getChatMessages({
quotePrompt,
history = [],
systemPrompt,
limitPrompt,
userChatInput,
model
}: {
quotePrompt: string;
history: ChatProps['history'];
systemPrompt: string;
limitPrompt: string;
userChatInput: string;
model: ChatModelItemType;
}) {
const limitText = (() => {
if (limitPrompt) return limitPrompt;
if (quotePrompt && !limitPrompt) {
return '根据知识库内容回答问题,仅回复知识库提供的内容,不要对知识库内容做补充说明。';
}
return '';
})();
const messages: ChatItemType[] = [
...(quotePrompt
? [
{
obj: ChatRoleEnum.System,
value: quotePrompt
}
]
: []),
...(systemPrompt
? [
{
obj: ChatRoleEnum.System,
value: systemPrompt
}
]
: []),
...history,
...(limitText
? [
{
obj: ChatRoleEnum.System,
value: limitText
}
]
: []),
{
obj: ChatRoleEnum.Human,
value: userChatInput
}
];
const filterMessages = ChatContextFilter({
model: model.model,
prompts: messages,
maxTokens: Math.ceil(model.contextMaxToken - 300) // filter token. not response maxToken
});
const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false });
return {
messages: adaptMessages,
filterMessages
};
}
function getMaxTokens({
maxToken,
model,
filterMessages = []
}: {
maxToken: number;
model: ChatModelItemType;
filterMessages: ChatProps['history'];
}) {
const tokensLimit = model.contextMaxToken;
/* count response max token */
const promptsToken = modelToolMap.countTokens({
model: model.model,
messages: filterMessages
});
maxToken = maxToken + promptsToken > tokensLimit ? tokensLimit - promptsToken : maxToken;
return {
max_tokens: maxToken
};
}
async function streamResponse({ res, response }: { res: NextApiResponse; response: any }) { async function streamResponse({ res, response }: { res: NextApiResponse; response: any }) {
let answer = ''; let answer = '';
let error: any = null; let error: any = null;
......
import { PgClient } from '@/service/pg'; import { PgClient } from '@/service/pg';
import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat'; import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
import { TaskResponseKeyEnum } from '@/constants/chat'; import { ChatModuleEnum, TaskResponseKeyEnum } from '@/constants/chat';
import { getVector } from '@/pages/api/openapi/plugin/vector'; import { getVector } from '@/pages/api/openapi/plugin/vector';
import { countModelPrice } from '@/service/events/pushBill'; import { countModelPrice } from '@/service/events/pushBill';
import type { SelectedKbType } from '@/types/plugin'; import type { SelectedKbType } from '@/types/plugin';
...@@ -20,8 +20,6 @@ export type KBSearchResponse = { ...@@ -20,8 +20,6 @@ export type KBSearchResponse = {
quoteQA: QuoteItemType[]; quoteQA: QuoteItemType[];
}; };
const moduleName = 'KB Search';
export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSearchResponse> { export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSearchResponse> {
const { const {
kbList = [], kbList = [],
...@@ -65,7 +63,7 @@ export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSe ...@@ -65,7 +63,7 @@ export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSe
unEmpty: searchRes.length > 0 ? true : undefined, unEmpty: searchRes.length > 0 ? true : undefined,
quoteQA: searchRes, quoteQA: searchRes,
responseData: { responseData: {
moduleName, moduleName: ChatModuleEnum.KBSearch,
price: countModelPrice({ model: vectorModel.model, tokens: tokenLen }), price: countModelPrice({ model: vectorModel.model, tokens: tokenLen }),
model: vectorModel.name, model: vectorModel.name,
tokens: tokenLen, tokens: tokenLen,
......
import { ChatItemType } from '@/types/chat'; import { ChatItemType } from '@/types/chat';
import { modelToolMap } from '@/utils/plugin'; import { modelToolMap } from '@/utils/plugin';
import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; import { ChatRoleEnum } from '@/constants/chat';
import { sseResponse } from '../tools';
import { OpenAiChatEnum } from '@/constants/model'; import { OpenAiChatEnum } from '@/constants/model';
import type { NextApiResponse } from 'next'; import type { NextApiResponse } from 'next';
...@@ -18,18 +17,6 @@ export type StreamResponseType = { ...@@ -18,18 +17,6 @@ export type StreamResponseType = {
model: `${OpenAiChatEnum}`; model: `${OpenAiChatEnum}`;
[key: string]: any; [key: string]: any;
}; };
export type StreamResponseReturnType = {
responseContent: string;
totalTokens: number;
finishMessages: ChatItemType[];
};
/* delete invalid symbol */
const simplifyStr = (str = '') =>
str
.replace(/\n+/g, '\n') // 连续空行
.replace(/[^\S\r\n]+/g, ' ') // 连续空白内容
.trim();
/* slice chat context by tokens */ /* slice chat context by tokens */
export const ChatContextFilter = ({ export const ChatContextFilter = ({
......
...@@ -56,7 +56,8 @@ export type ChatHistoryItemResType = { ...@@ -56,7 +56,8 @@ export type ChatHistoryItemResType = {
question?: string; question?: string;
temperature?: number; temperature?: number;
maxToken?: number; maxToken?: number;
finishMessages?: ChatItemType[]; quoteList?: QuoteItemType[];
completeMessages?: ChatItemType[];
// kb search // kb search
similarity?: number; similarity?: number;
......
...@@ -2,7 +2,7 @@ export type ChatModelItemType = { ...@@ -2,7 +2,7 @@ export type ChatModelItemType = {
model: string; model: string;
name: string; name: string;
contextMaxToken: number; contextMaxToken: number;
systemMaxToken: number; quoteMaxToken: number;
maxTemperature: number; maxTemperature: number;
price: number; price: number;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment