mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-06 15:36:21 +00:00
chatbox ui
This commit is contained in:
@@ -6,14 +6,15 @@ import { sseResponseEventEnum } from '@/constants/chat';
|
||||
import { sseResponse } from '@/service/utils/tools';
|
||||
import { type ChatCompletionRequestMessage } from 'openai';
|
||||
import { AppModuleItemType } from '@/types/app';
|
||||
import { dispatchModules } from '../openapi/v1/chat/completions2';
|
||||
import { dispatchModules } from '../openapi/v1/chat/completions';
|
||||
import { gptMessage2ChatType } from '@/utils/adapt';
|
||||
|
||||
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string };
|
||||
export type Props = {
|
||||
history: MessageItemType[];
|
||||
prompt: string;
|
||||
modules: AppModuleItemType[];
|
||||
variable: Record<string, any>;
|
||||
variables: Record<string, any>;
|
||||
};
|
||||
export type ChatResponseType = {
|
||||
newChatId: string;
|
||||
@@ -29,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
res.end();
|
||||
});
|
||||
|
||||
let { modules = [], history = [], prompt, variable = {} } = req.body as Props;
|
||||
let { modules = [], history = [], prompt, variables = {} } = req.body as Props;
|
||||
|
||||
try {
|
||||
if (!history || !modules || !prompt) {
|
||||
@@ -48,9 +49,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { responseData } = await dispatchModules({
|
||||
res,
|
||||
modules: modules,
|
||||
variable,
|
||||
variables,
|
||||
params: {
|
||||
history,
|
||||
history: gptMessage2ChatType(history),
|
||||
userChatInput: prompt
|
||||
},
|
||||
stream: true
|
||||
|
@@ -5,12 +5,10 @@ import { authUser } from '@/service/utils/auth';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { chatId, contentId } = req.query as {
|
||||
chatId: string;
|
||||
contentId: string;
|
||||
};
|
||||
const { historyId, contentId } = req.query as { historyId: string; contentId: string };
|
||||
console.log(historyId, contentId);
|
||||
|
||||
if (!chatId || !contentId) {
|
||||
if (!historyId || !contentId) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
@@ -19,7 +17,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
const chatRecord = await Chat.findById(chatId);
|
||||
const chatRecord = await Chat.findById(historyId);
|
||||
|
||||
if (!chatRecord) {
|
||||
throw new Error('找不到对话');
|
||||
@@ -28,7 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
// 删除一条数据库记录
|
||||
await Chat.updateOne(
|
||||
{
|
||||
_id: chatId,
|
||||
_id: historyId,
|
||||
userId
|
||||
},
|
||||
{ $pull: { content: { _id: contentId } } }
|
||||
|
@@ -2,31 +2,32 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import type { HistoryItemType } from '@/types/chat';
|
||||
import type { ChatHistoryItemType } from '@/types/chat';
|
||||
|
||||
/* 获取历史记录 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { appId } = req.body as { appId?: string };
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const data = await Chat.find(
|
||||
{
|
||||
userId
|
||||
userId,
|
||||
...(appId && { appId })
|
||||
},
|
||||
'_id title top customTitle modelId updateTime latestChat'
|
||||
'_id title top customTitle appId updateTime'
|
||||
)
|
||||
.sort({ top: -1, updateTime: -1 })
|
||||
.limit(20);
|
||||
|
||||
jsonRes<HistoryItemType[]>(res, {
|
||||
jsonRes<ChatHistoryItemType[]>(res, {
|
||||
data: data.map((item) => ({
|
||||
_id: item._id,
|
||||
updateTime: item.updateTime,
|
||||
modelId: item.modelId,
|
||||
appId: item.appId,
|
||||
title: item.customTitle || item.title,
|
||||
latestChat: item.latestChat,
|
||||
top: item.top
|
||||
}))
|
||||
});
|
||||
|
@@ -4,7 +4,7 @@ import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
|
||||
export type Props = {
|
||||
chatId: '' | string;
|
||||
historyId: string;
|
||||
customTitle?: string;
|
||||
top?: boolean;
|
||||
};
|
||||
@@ -12,7 +12,7 @@ export type Props = {
|
||||
/* 更新聊天标题 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { chatId, customTitle, top } = req.body as Props;
|
||||
const { historyId, customTitle, top } = req.body as Props;
|
||||
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
@@ -20,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
await Chat.findOneAndUpdate(
|
||||
{
|
||||
_id: chatId,
|
||||
_id: historyId,
|
||||
userId
|
||||
},
|
||||
{
|
||||
|
@@ -6,23 +6,25 @@ import { authUser } from '@/service/utils/auth';
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
import { authApp } from '@/service/utils/auth';
|
||||
import mongoose from 'mongoose';
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
import type { AppSchema, ChatSchema } from '@/types/mongoSchema';
|
||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||
import { SystemInputEnum } from '@/constants/app';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
let { modelId, chatId } = req.query as {
|
||||
modelId: '' | string;
|
||||
chatId: '' | string;
|
||||
let { appId, historyId } = req.query as {
|
||||
appId: '' | string;
|
||||
historyId: '' | string;
|
||||
};
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 没有 modelId 时,直接获取用户的第一个id
|
||||
// 没有 appId 时,直接获取用户的第一个id
|
||||
const app = await (async () => {
|
||||
if (!modelId) {
|
||||
if (!appId) {
|
||||
const myModel = await App.findOne({ userId });
|
||||
if (!myModel) {
|
||||
const { _id } = await App.create({
|
||||
@@ -36,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
} else {
|
||||
// 校验使用权限
|
||||
const authRes = await authApp({
|
||||
appId: modelId,
|
||||
appId,
|
||||
userId,
|
||||
authUser: false,
|
||||
authOwner: false
|
||||
@@ -45,63 +47,71 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
}
|
||||
})();
|
||||
|
||||
modelId = modelId || app._id;
|
||||
appId = appId || app._id;
|
||||
|
||||
// 历史记录
|
||||
let history: ChatItemType[] = [];
|
||||
|
||||
if (chatId) {
|
||||
// auth chatId
|
||||
const chat = await Chat.countDocuments({
|
||||
_id: chatId,
|
||||
userId
|
||||
});
|
||||
if (chat === 0) {
|
||||
throw new Error('聊天框不存在');
|
||||
}
|
||||
// 获取 chat.content 数据
|
||||
history = await Chat.aggregate([
|
||||
{
|
||||
$match: {
|
||||
_id: new mongoose.Types.ObjectId(chatId),
|
||||
userId: new mongoose.Types.ObjectId(userId)
|
||||
const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } =
|
||||
await (async () => {
|
||||
if (historyId) {
|
||||
// auth chatId
|
||||
const chat = await Chat.findOne({
|
||||
_id: historyId,
|
||||
userId
|
||||
});
|
||||
if (!chat) {
|
||||
throw new Error('聊天框不存在');
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
content: {
|
||||
$slice: ['$content', -50] // 返回 content 数组的最后50个元素
|
||||
// 获取 chat.content 数据
|
||||
const history = await Chat.aggregate([
|
||||
{
|
||||
$match: {
|
||||
_id: new mongoose.Types.ObjectId(historyId),
|
||||
userId: new mongoose.Types.ObjectId(userId)
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
content: {
|
||||
$slice: ['$content', -50] // 返回 content 数组的最后50个元素
|
||||
}
|
||||
}
|
||||
},
|
||||
{ $unwind: '$content' },
|
||||
{
|
||||
$project: {
|
||||
_id: '$content._id',
|
||||
obj: '$content.obj',
|
||||
value: '$content.value',
|
||||
systemPrompt: '$content.systemPrompt',
|
||||
quoteLen: { $size: { $ifNull: ['$content.quote', []] } }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{ $unwind: '$content' },
|
||||
{
|
||||
$project: {
|
||||
_id: '$content._id',
|
||||
obj: '$content.obj',
|
||||
value: '$content.value',
|
||||
systemPrompt: '$content.systemPrompt',
|
||||
quoteLen: { $size: { $ifNull: ['$content.quote', []] } }
|
||||
}
|
||||
]);
|
||||
return { history, chat };
|
||||
}
|
||||
]);
|
||||
}
|
||||
return {};
|
||||
})();
|
||||
|
||||
const isOwner = String(app.userId) === userId;
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
data: {
|
||||
chatId: chatId || '',
|
||||
modelId: modelId,
|
||||
model: {
|
||||
historyId,
|
||||
appId,
|
||||
app: {
|
||||
variableModules: app.modules
|
||||
.find((item) => item.flowType === FlowModuleTypeEnum.userGuide)
|
||||
?.inputs?.find((item) => item.key === SystemInputEnum.variables)?.value,
|
||||
welcomeText: app.modules
|
||||
.find((item) => item.flowType === FlowModuleTypeEnum.userGuide)
|
||||
?.inputs?.find((item) => item.key === SystemInputEnum.welcomeText)?.value,
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro,
|
||||
canUse: app.share.isShare || isOwner
|
||||
},
|
||||
chatModel: app.chat.chatModel,
|
||||
systemPrompt: isOwner ? app.chat.systemPrompt : '',
|
||||
limitPrompt: isOwner ? app.chat.limitPrompt : '',
|
||||
title: chat?.title || '新对话',
|
||||
variables: chat?.variables || {},
|
||||
history
|
||||
}
|
||||
});
|
||||
|
@@ -7,15 +7,16 @@ import { authUser } from '@/service/utils/auth';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
type Props = {
|
||||
chatId?: string;
|
||||
modelId: string;
|
||||
historyId?: string;
|
||||
appId: string;
|
||||
variables?: Record<string, any>;
|
||||
prompts: [ChatItemType, ChatItemType];
|
||||
};
|
||||
|
||||
/* 聊天内容存存储 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { chatId, modelId, prompts } = req.body as Props;
|
||||
const { historyId, appId, prompts } = req.body as Props;
|
||||
|
||||
if (!prompts) {
|
||||
throw new Error('缺少参数');
|
||||
@@ -24,8 +25,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
const response = await saveChat({
|
||||
chatId,
|
||||
modelId,
|
||||
historyId,
|
||||
appId,
|
||||
prompts,
|
||||
userId
|
||||
});
|
||||
@@ -42,14 +43,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
}
|
||||
|
||||
export async function saveChat({
|
||||
newChatId,
|
||||
chatId,
|
||||
modelId,
|
||||
newHistoryId,
|
||||
historyId,
|
||||
appId,
|
||||
prompts,
|
||||
variables,
|
||||
userId
|
||||
}: Props & { newChatId?: Types.ObjectId; userId: string }): Promise<{ newChatId: string }> {
|
||||
}: Props & { newHistoryId?: Types.ObjectId; userId: string }): Promise<{ newHistoryId: string }> {
|
||||
await connectToDatabase();
|
||||
const { app } = await authApp({ appId: modelId, userId, authOwner: false });
|
||||
const { app } = await authApp({ appId, userId, authOwner: false });
|
||||
|
||||
const content = prompts.map((item) => ({
|
||||
_id: item._id,
|
||||
@@ -60,43 +62,45 @@ export async function saveChat({
|
||||
}));
|
||||
|
||||
if (String(app.userId) === userId) {
|
||||
await App.findByIdAndUpdate(modelId, {
|
||||
await App.findByIdAndUpdate(appId, {
|
||||
updateTime: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
const [response] = await Promise.all([
|
||||
...(chatId
|
||||
...(historyId
|
||||
? [
|
||||
Chat.findByIdAndUpdate(chatId, {
|
||||
Chat.findByIdAndUpdate(historyId, {
|
||||
$push: {
|
||||
content: {
|
||||
$each: content
|
||||
}
|
||||
},
|
||||
variables,
|
||||
title: content[0].value.slice(0, 20),
|
||||
latestChat: content[1].value,
|
||||
updateTime: new Date()
|
||||
}).then(() => ({
|
||||
newChatId: ''
|
||||
newHistoryId: ''
|
||||
}))
|
||||
]
|
||||
: [
|
||||
Chat.create({
|
||||
_id: newChatId,
|
||||
_id: newHistoryId,
|
||||
userId,
|
||||
modelId,
|
||||
appId,
|
||||
variables,
|
||||
content,
|
||||
title: content[0].value.slice(0, 20),
|
||||
latestChat: content[1].value
|
||||
}).then((res) => ({
|
||||
newChatId: String(res._id)
|
||||
newHistoryId: String(res._id)
|
||||
}))
|
||||
]),
|
||||
// update app
|
||||
...(String(app.userId) === userId
|
||||
? [
|
||||
App.findByIdAndUpdate(modelId, {
|
||||
App.findByIdAndUpdate(appId, {
|
||||
updateTime: new Date()
|
||||
})
|
||||
]
|
||||
@@ -105,6 +109,6 @@ export async function saveChat({
|
||||
|
||||
return {
|
||||
// @ts-ignore
|
||||
newChatId: response?.newChatId || ''
|
||||
newHistoryId: response?.newHistoryId || ''
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user