perf: completion chatId

This commit is contained in:
archer
2023-07-23 20:07:35 +08:00
parent b7d18e38d1
commit 67e10d6f2c
35 changed files with 447 additions and 385 deletions

View File

@@ -22,7 +22,7 @@ export const getChatHistory = (data: RequestPaging & { appId?: string }) =>
/** /**
* 删除一条历史记录 * 删除一条历史记录
*/ */
export const delChatHistoryById = (id: string) => GET(`/chat/removeHistory?id=${id}`); export const delChatHistoryById = (chatId: string) => DELETE(`/chat/removeHistory`, { chatId });
/** /**
* get history quotes * get history quotes

View File

@@ -78,6 +78,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);
} else if (item.event === sseResponseEventEnum.error) { } else if (item.event === sseResponseEventEnum.error) {
errMsg = getErrText(data, '流响应错误'); errMsg = getErrText(data, '流响应错误');
} }

View File

@@ -20,7 +20,6 @@ export interface InitChatResponse {
export interface InitShareChatResponse { export interface InitShareChatResponse {
userAvatar: string; userAvatar: string;
maxContext: number;
app: { app: {
variableModules?: VariableItemType[]; variableModules?: VariableItemType[];
welcomeText?: string; welcomeText?: string;

View File

@@ -19,7 +19,6 @@ import {
getErrText getErrText
} from '@/utils/tools'; } from '@/utils/tools';
import { Box, Card, Flex, Input, Textarea, Button, useTheme } from '@chakra-ui/react'; import { Box, Card, Flex, Input, Textarea, Button, useTheme } from '@chakra-ui/react';
import { useUserStore } from '@/store/user';
import { feConfigs } from '@/store/static'; import { feConfigs } from '@/store/static';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { EventNameEnum } from '../Markdown/constant'; import { EventNameEnum } from '../Markdown/constant';
@@ -118,6 +117,7 @@ const ChatBox = (
showEmptyIntro = false, showEmptyIntro = false,
chatId, chatId,
appAvatar, appAvatar,
userAvatar,
variableModules, variableModules,
welcomeText, welcomeText,
onUpdateVariable, onUpdateVariable,
@@ -126,7 +126,8 @@ const ChatBox = (
}: { }: {
showEmptyIntro?: boolean; showEmptyIntro?: boolean;
chatId?: string; chatId?: string;
appAvatar: string; appAvatar?: string;
userAvatar?: string;
variableModules?: VariableItemType[]; variableModules?: VariableItemType[];
welcomeText?: string; welcomeText?: string;
onUpdateVariable?: (e: Record<string, any>) => void; onUpdateVariable?: (e: Record<string, any>) => void;
@@ -142,7 +143,6 @@ const ChatBox = (
const router = useRouter(); const router = useRouter();
const { copyData } = useCopyData(); const { copyData } = useCopyData();
const { toast } = useToast(); const { toast } = useToast();
const { userInfo } = useUserStore();
const { isPc } = useGlobalStore(); const { isPc } = useGlobalStore();
const TextareaDom = useRef<HTMLTextAreaElement>(null); const TextareaDom = useRef<HTMLTextAreaElement>(null);
const controller = useRef(new AbortController()); const controller = useRef(new AbortController());
@@ -585,7 +585,7 @@ const ChatBox = (
)} )}
</Flex> </Flex>
</Box> </Box>
<ChatAvatar src={userInfo?.avatar} ml={['6px', 2]} /> <ChatAvatar src={userAvatar} ml={['6px', 2]} />
</> </>
)} )}
{item.obj === 'AI' && ( {item.obj === 'AI' && (

View File

@@ -0,0 +1,61 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import { connectToDatabase, TrainingData, User, promotionRecord, Chat } from '@/service/mongo';
import { PRICE_SCALE } from '@/constants/common';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await authUser({ req, authRoot: true });
await connectToDatabase();
const { limit = 1000 } = req.body as { limit: number };
let skip = 0;
const total = await Chat.countDocuments({
chatId: { $exists: false }
});
let promise = Promise.resolve();
console.log(total);
for (let i = 0; i < total; i += limit) {
const skipVal = skip;
skip += limit;
promise = promise
.then(() => init(limit, skipVal))
.then(() => {
console.log(skipVal);
});
}
await promise;
jsonRes(res, {});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}
async function init(limit: number, skip: number) {
// 遍历 app
const chats = await Chat.find(
{
chatId: { $exists: false }
},
'_id'
)
.limit(limit)
.skip(skip);
await Promise.all(
chats.map((chat) =>
Chat.findByIdAndUpdate(chat._id, {
chatId: String(chat._id),
source: 'online'
})
)
);
}

View File

@@ -1,47 +0,0 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import { connectToDatabase, TrainingData, User, promotionRecord } from '@/service/mongo';
import { PRICE_SCALE } from '@/constants/common';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await authUser({ req, authRoot: true });
await connectToDatabase();
// 计算剩余金额
const countResidue: { userId: string; totalAmount: number }[] = await promotionRecord.aggregate(
[
{
$group: {
_id: '$userId', // Group by userId
totalAmount: { $sum: '$amount' } // Calculate the sum of amount field
}
},
{
$project: {
_id: false, // Exclude _id field
userId: '$_id', // Include userId field
totalAmount: true // Include totalAmount field
}
}
]
);
await Promise.all(
countResidue.map((item) =>
User.findByIdAndUpdate(item.userId, {
$inc: { balance: item.totalAmount * PRICE_SCALE }
})
)
);
jsonRes(res, { data: countResidue });
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}

View File

@@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
// 凭证校验 // 凭证校验
const { userId } = await authUser({ req, authToken: true }); const { userId } = await authUser({ req, authToken: true });
const chatRecord = await Chat.findById(chatId); const chatRecord = await Chat.findOne({ chatId });
if (!chatRecord) { if (!chatRecord) {
throw new Error('找不到对话'); throw new Error('找不到对话');

View File

@@ -17,14 +17,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
userId, userId,
...(appId && { appId }) ...(appId && { appId })
}, },
'_id title top customTitle appId updateTime' 'chatId title top customTitle appId updateTime'
) )
.sort({ top: -1, updateTime: -1 }) .sort({ top: -1, updateTime: -1 })
.limit(20); .limit(20);
jsonRes<ChatHistoryItemType[]>(res, { jsonRes<ChatHistoryItemType[]>(res, {
data: data.map((item) => ({ data: data.map((item) => ({
_id: item._id, chatId: item.chatId,
updateTime: item.updateTime, updateTime: item.updateTime,
appId: item.appId, appId: item.appId,
customTitle: item.customTitle, customTitle: item.customTitle,

View File

@@ -3,7 +3,6 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo'; import { connectToDatabase, Chat } from '@/service/mongo';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { rawSearchKey } from '@/constants/chat';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
@@ -36,13 +35,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}, },
{ {
$project: { $project: {
[rawSearchKey]: `$content.${rawSearchKey}` // [rawSearchKey]: `$content.${rawSearchKey}`
} }
} }
]); ]);
jsonRes(res, { jsonRes(res, {
data: history[0]?.[rawSearchKey] || [] // data: history[0]?.[rawSearchKey] || []
}); });
} catch (err) { } catch (err) {
jsonRes(res, { jsonRes(res, {

View File

@@ -20,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await Chat.findOneAndUpdate( await Chat.findOneAndUpdate(
{ {
_id: chatId, chatId,
userId userId
}, },
{ {

View File

@@ -27,7 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await Chat.updateOne( await Chat.updateOne(
{ {
_id: new Types.ObjectId(chatId), chatId,
userId: new Types.ObjectId(userId), userId: new Types.ObjectId(userId),
'content._id': new Types.ObjectId(contentId) 'content._id': new Types.ObjectId(contentId)
}, },

View File

@@ -6,8 +6,7 @@ import { authUser } from '@/service/utils/auth';
import { ChatItemType } from '@/types/chat'; import { ChatItemType } from '@/types/chat';
import { authApp } from '@/service/utils/auth'; import { authApp } from '@/service/utils/auth';
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import type { AppSchema, ChatSchema } from '@/types/mongoSchema'; import type { ChatSchema } from '@/types/mongoSchema';
import { quoteLenKey, rawSearchKey } from '@/constants/chat';
import { getSpecialModule } from '@/components/ChatBox'; import { getSpecialModule } from '@/components/ChatBox';
/* 初始化我的聊天框,需要身份验证 */ /* 初始化我的聊天框,需要身份验证 */
@@ -20,72 +19,62 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
chatId: '' | string; chatId: '' | string;
}; };
if (!appId) {
return jsonRes(res, {
code: 501,
message: "You don't have an app yet"
});
}
await connectToDatabase(); await connectToDatabase();
// 没有 appId 时直接获取用户的第一个id // 校验使用权限
const app = await (async () => { const app = (
if (!appId) { await authApp({
const myModel = await App.findOne({ userId }); appId,
if (!myModel) { userId,
const { _id } = await App.create({ authUser: false,
name: '应用1', authOwner: false
userId })
}); ).app;
return (await App.findById(_id)) as AppSchema;
} else {
return myModel;
}
} else {
// 校验使用权限
const authRes = await authApp({
appId,
userId,
authUser: false,
authOwner: false
});
return authRes.app;
}
})();
appId = appId || app._id;
// 历史记录 // 历史记录
const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } = const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } =
await (async () => { await (async () => {
if (chatId) { if (chatId) {
// auth chatId // auth chatId
const chat = await Chat.findOne({ const [chat, history] = await Promise.all([
_id: chatId, Chat.findOne({
userId chatId,
}); userId
}),
Chat.aggregate([
{
$match: {
chatId,
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'
}
}
])
]);
if (!chat) { if (!chat) {
throw new Error('聊天框不存在'); throw new Error('聊天框不存在');
} }
// 获取 chat.content 数据
const history = await Chat.aggregate([
{
$match: {
_id: new mongoose.Types.ObjectId(chatId),
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',
[quoteLenKey]: { $size: { $ifNull: [`$content.${rawSearchKey}`, []] } }
}
}
]);
return { history, chat }; return { history, chat };
} }
return {}; return {};

View File

@@ -6,13 +6,13 @@ import { authUser } from '@/service/utils/auth';
/* 获取历史记录 */ /* 获取历史记录 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
const { id } = req.query; const { chatId } = req.query;
const { userId } = await authUser({ req, authToken: true }); const { userId } = await authUser({ req, authToken: true });
await connectToDatabase(); await connectToDatabase();
await Chat.findOneAndRemove({ await Chat.findOneAndRemove({
_id: id, chatId,
userId userId
}); });

View File

@@ -1,104 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { ChatItemType } from '@/types/chat';
import { connectToDatabase, Chat, App } from '@/service/mongo';
import { authApp } from '@/service/utils/auth';
import { authUser } from '@/service/utils/auth';
import { Types } from 'mongoose';
type Props = {
chatId?: string;
appId: string;
variables?: Record<string, any>;
prompts: [ChatItemType, ChatItemType];
};
/* 聊天内容存存储 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { chatId, appId, prompts } = req.body as Props;
if (!prompts) {
throw new Error('缺少参数');
}
const { userId } = await authUser({ req, authToken: true });
const response = await saveChat({
chatId,
appId,
prompts,
userId
});
jsonRes(res, {
data: response
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}
export async function saveChat({
newChatId,
chatId,
appId,
prompts,
variables,
userId
}: Props & { newChatId?: Types.ObjectId; userId: string }): Promise<{ newChatId: string }> {
await connectToDatabase();
const { app } = await authApp({ appId, userId, authOwner: false });
if (String(app.userId) === userId) {
await App.findByIdAndUpdate(appId, {
updateTime: new Date()
});
}
const [response] = await Promise.all([
...(chatId
? [
Chat.findByIdAndUpdate(chatId, {
$push: {
content: {
$each: prompts
}
},
variables,
title: prompts[0].value.slice(0, 20),
updateTime: new Date()
}).then(() => ({
newChatId: ''
}))
]
: [
Chat.create({
_id: newChatId,
userId,
appId,
variables,
content: prompts,
title: prompts[0].value.slice(0, 20)
}).then((res) => ({
newChatId: String(res._id)
}))
]),
// update app
...(String(app.userId) === userId
? [
App.findByIdAndUpdate(appId, {
updateTime: new Date()
})
]
: [])
]);
return {
// @ts-ignore
newChatId: response?.newChatId || ''
};
}

View File

@@ -9,7 +9,7 @@ const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
/* create a shareChat */ /* create a shareChat */
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
const { appId, name, maxContext } = req.body as ShareChatEditType & { const { appId, name } = req.body as ShareChatEditType & {
appId: string; appId: string;
}; };
@@ -27,8 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
shareId, shareId,
userId, userId,
appId, appId,
name, name
maxContext
}); });
jsonRes(res, { jsonRes(res, {

View File

@@ -3,10 +3,7 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, ShareChat, User } from '@/service/mongo'; import { connectToDatabase, ShareChat, User } from '@/service/mongo';
import type { InitShareChatResponse } from '@/api/response/chat'; import type { InitShareChatResponse } from '@/api/response/chat';
import { authApp } from '@/service/utils/auth'; import { authApp } from '@/service/utils/auth';
import { hashPassword } from '@/service/utils/tools';
import { HUMAN_ICON } from '@/constants/chat'; import { HUMAN_ICON } from '@/constants/chat';
import { FlowModuleTypeEnum } from '@/constants/flow';
import { SystemInputEnum } from '@/constants/app';
import { getSpecialModule } from '@/components/ChatBox'; import { getSpecialModule } from '@/components/ChatBox';
/* 初始化我的聊天框,需要身份验证 */ /* 初始化我的聊天框,需要身份验证 */
@@ -33,21 +30,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
} }
// 校验使用权限 // 校验使用权限
const { app } = await authApp({ const [{ app }, user] = await Promise.all([
appId: shareChat.appId, authApp({
userId: String(shareChat.userId), appId: shareChat.appId,
authOwner: false userId: String(shareChat.userId),
}); authOwner: false
}),
const user = await User.findById(shareChat.userId, 'avatar'); User.findById(shareChat.userId, 'avatar')
]);
jsonRes<InitShareChatResponse>(res, { jsonRes<InitShareChatResponse>(res, {
data: { data: {
userAvatar: user?.avatar || HUMAN_ICON, userAvatar: user?.avatar || HUMAN_ICON,
maxContext:
app.modules
?.find((item) => item.flowType === FlowModuleTypeEnum.historyNode)
?.inputs?.find((item) => item.key === 'maxContext')?.value || 0,
app: { app: {
...getSpecialModule(app.modules), ...getSpecialModule(app.modules),
name: app.name, name: app.name,

View File

@@ -28,7 +28,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
shareId: item.shareId, shareId: item.shareId,
name: item.name, name: item.name,
total: item.total, total: item.total,
maxContext: item.maxContext,
lastTime: item.lastTime lastTime: item.lastTime
})) }))
}); });

View File

@@ -3,7 +3,7 @@ import { connectToDatabase } from '@/service/mongo';
import { authUser, authApp, authShareChat } from '@/service/utils/auth'; import { authUser, authApp, authShareChat } from '@/service/utils/auth';
import { sseErrRes, jsonRes } from '@/service/response'; import { sseErrRes, jsonRes } from '@/service/response';
import { withNextCors } from '@/service/utils/tools'; import { withNextCors } from '@/service/utils/tools';
import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; import { ChatRoleEnum, ChatSourceEnum, sseResponseEventEnum } from '@/constants/chat';
import { import {
dispatchHistory, dispatchHistory,
dispatchChatInput, dispatchChatInput,
@@ -15,12 +15,11 @@ import {
import type { CreateChatCompletionRequest } from 'openai'; import type { CreateChatCompletionRequest } from 'openai';
import { gptMessage2ChatType } from '@/utils/adapt'; import { gptMessage2ChatType } from '@/utils/adapt';
import { getChatHistory } from './getHistory'; import { getChatHistory } from './getHistory';
import { saveChat } from '@/pages/api/chat/saveChat'; import { saveChat } from '@/service/utils/chat/saveChat';
import { sseResponse } from '@/service/utils/tools'; import { sseResponse } from '@/service/utils/tools';
import { type ChatCompletionRequestMessage } from 'openai'; import { type ChatCompletionRequestMessage } from 'openai';
import { TaskResponseKeyEnum } from '@/constants/chat'; import { TaskResponseKeyEnum } from '@/constants/chat';
import { FlowModuleTypeEnum, initModuleType } from '@/constants/flow'; import { FlowModuleTypeEnum, initModuleType } from '@/constants/flow';
import { Types } from 'mongoose';
import { AppModuleItemType, RunningModuleItemType } from '@/types/app'; import { AppModuleItemType, RunningModuleItemType } from '@/types/app';
import { pushTaskBill } from '@/service/events/pushBill'; import { pushTaskBill } from '@/service/events/pushBill';
import { BillSourceEnum } from '@/constants/user'; import { BillSourceEnum } from '@/constants/user';
@@ -101,16 +100,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
} }
// user question // user question
const prompt = prompts.pop(); const prompt = prompts.pop();
if (!prompt) { if (!prompt) {
throw new Error('Question is empty'); throw new Error('Question is empty');
} }
const newChatId = chatId === '' ? new Types.ObjectId() : undefined;
if (stream && newChatId) {
res.setHeader('newChatId', String(newChatId));
}
/* start process */ /* start process */
const { responseData, answerText } = await dispatchModules({ const { responseData, answerText } = await dispatchModules({
res, res,
@@ -122,29 +115,39 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}, },
stream stream
}); });
console.log(responseData, '===', answerText); // console.log(responseData, '===', answerText);
if (!answerText) { // if (!answerText) {
throw new Error('回复内容为空,可能模块编排出现问题'); // throw new Error('回复内容为空,可能模块编排出现问题');
} // }
// save chat // save chat
if (typeof chatId === 'string') { if (typeof chatId === 'string') {
await saveChat({ await saveChat({
chatId, chatId,
newChatId,
appId, appId,
userId,
variables, variables,
prompts: [ isOwner,
shareId,
source: (() => {
if (shareId) {
return ChatSourceEnum.share;
}
if (authType === 'apikey') {
return ChatSourceEnum.api;
}
return ChatSourceEnum.online;
})(),
content: [
prompt, prompt,
{ {
_id: messages[messages.length - 1]._id, _id: messages[messages.length - 1]._id,
obj: ChatRoleEnum.AI, obj: ChatRoleEnum.AI,
value: answerText, value: answerText,
...responseData responseData
} }
], ]
userId
}); });
} }

View File

@@ -43,7 +43,7 @@ export async function getChatHistory({
} }
const history = await Chat.aggregate([ const history = await Chat.aggregate([
{ $match: { _id: new Types.ObjectId(chatId), userId: new Types.ObjectId(userId) } }, { $match: { chatId, userId: new Types.ObjectId(userId) } },
{ {
$project: { $project: {
content: { content: {
@@ -54,10 +54,8 @@ export async function getChatHistory({
{ $unwind: '$content' }, { $unwind: '$content' },
{ {
$project: { $project: {
_id: '$content._id',
obj: '$content.obj', obj: '$content.obj',
value: '$content.value', value: '$content.value'
quote: '$content.quote'
} }
} }
]); ]);

View File

@@ -42,6 +42,66 @@ export async function getInitConfig() {
global.qaModels = res.QAModels; global.qaModels = res.QAModels;
global.vectorModels = res.VectorModels; global.vectorModels = res.VectorModels;
} catch (error) { } catch (error) {
setDefaultData();
return Promise.reject('get init config error'); return Promise.reject('get init config error');
} }
} }
export function setDefaultData() {
global.systemEnv = {
vectorMaxProcess: 15,
qaMaxProcess: 15,
pgIvfflatProbe: 20,
sensitiveCheck: false
};
global.feConfigs = {
show_emptyChat: true,
show_register: true,
show_appStore: true,
show_userDetail: true,
show_git: true,
systemTitle: 'FastAI',
authorText: 'Made by FastAI Team.'
};
global.chatModels = [
{
model: 'gpt-3.5-turbo',
name: 'FastAI-4k',
contextMaxToken: 4000,
systemMaxToken: 2400,
maxTemperature: 1.2,
price: 1.5
},
{
model: 'gpt-3.5-turbo-16k',
name: 'FastAI-16k',
contextMaxToken: 16000,
systemMaxToken: 8000,
maxTemperature: 1.2,
price: 3
},
{
model: 'gpt-4',
name: 'FastAI-Plus',
contextMaxToken: 8000,
systemMaxToken: 4000,
maxTemperature: 1.2,
price: 45
}
];
global.qaModels = [
{
model: 'gpt-3.5-turbo-16k',
name: 'FastAI-16k',
maxToken: 16000,
price: 3
}
];
global.vectorModels = [
{
model: 'text-embedding-ada-002',
name: 'Embedding-2',
price: 0.2
}
];
}

View File

@@ -441,7 +441,7 @@ const Settings = ({ appId }: { appId: string }) => {
}; };
const ChatTest = ({ appId }: { appId: string }) => { const ChatTest = ({ appId }: { appId: string }) => {
const { appDetail } = useUserStore(); const { appDetail, userInfo } = useUserStore();
const ChatBoxRef = useRef<ComponentRef>(null); const ChatBoxRef = useRef<ComponentRef>(null);
const [modules, setModules] = useState<AppModuleItemType[]>([]); const [modules, setModules] = useState<AppModuleItemType[]>([]);
@@ -509,6 +509,7 @@ const ChatTest = ({ appId }: { appId: string }) => {
<ChatBox <ChatBox
ref={ChatBoxRef} ref={ChatBoxRef}
appAvatar={appDetail.avatar} appAvatar={appDetail.avatar}
userAvatar={userInfo?.avatar}
{...getSpecialModule(modules)} {...getSpecialModule(modules)}
onStartChat={startChat} onStartChat={startChat}
onDelMessage={() => {}} onDelMessage={() => {}}

View File

@@ -13,6 +13,7 @@ import MyIcon from '@/components/Icon';
import { FlowModuleTypeEnum } from '@/constants/flow'; import { FlowModuleTypeEnum } from '@/constants/flow';
import { streamFetch } from '@/api/fetch'; import { streamFetch } from '@/api/fetch';
import MyTooltip from '@/components/MyTooltip'; import MyTooltip from '@/components/MyTooltip';
import { useUserStore } from '@/store/user';
import ChatBox, { import ChatBox, {
getSpecialModule, getSpecialModule,
type ComponentRef, type ComponentRef,
@@ -36,6 +37,7 @@ const ChatTest = (
ref: ForwardedRef<ChatTestComponentRef> ref: ForwardedRef<ChatTestComponentRef>
) => { ) => {
const ChatBoxRef = useRef<ComponentRef>(null); const ChatBoxRef = useRef<ComponentRef>(null);
const { userInfo } = useUserStore();
const isOpen = useMemo(() => modules && modules.length > 0, [modules]); const isOpen = useMemo(() => modules && modules.length > 0, [modules]);
const startChat = useCallback( const startChat = useCallback(
@@ -47,7 +49,7 @@ const ChatTest = (
const history = messages.slice(-historyMaxLen - 2, -2); const history = messages.slice(-historyMaxLen - 2, -2);
// 流请求,获取数据 // 流请求,获取数据
const { responseText, rawSearch } = await streamFetch({ const { responseText } = await streamFetch({
url: '/api/chat/chatTest', url: '/api/chat/chatTest',
data: { data: {
history, history,
@@ -61,7 +63,7 @@ const ChatTest = (
abortSignal: controller abortSignal: controller
}); });
return { responseText, rawSearch }; return { responseText };
}, },
[app._id, app.name, modules] [app._id, app.name, modules]
); );
@@ -113,6 +115,7 @@ const ChatTest = (
<ChatBox <ChatBox
ref={ChatBoxRef} ref={ChatBoxRef}
appAvatar={app.avatar} appAvatar={app.avatar}
userAvatar={userInfo?.avatar}
{...getSpecialModule(modules)} {...getSpecialModule(modules)}
onStartChat={startChat} onStartChat={startChat}
onDelMessage={() => {}} onDelMessage={() => {}}

View File

@@ -15,19 +15,24 @@ import { useQuery } from '@tanstack/react-query';
import { streamFetch } from '@/api/fetch'; import { streamFetch } from '@/api/fetch';
import { useChatStore } from '@/store/chat'; import { useChatStore } from '@/store/chat';
import { useLoading } from '@/hooks/useLoading'; import { useLoading } from '@/hooks/useLoading';
import { useToast } from '@/hooks/useToast';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
import type { ChatHistoryItemType } from '@/types/chat';
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox'; import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
import { ChatHistoryItemType } from '@/types/chat';
import PageContainer from '@/components/PageContainer'; import PageContainer from '@/components/PageContainer';
import SideBar from '@/components/SideBar'; import SideBar from '@/components/SideBar';
import ChatHistorySlider from './components/ChatHistorySlider'; import ChatHistorySlider from './components/ChatHistorySlider';
import SliderApps from './components/SliderApps'; import SliderApps from './components/SliderApps';
import ChatHeader from './components/ChatHeader'; import ChatHeader from './components/ChatHeader';
import { getErrText } from '@/utils/tools';
import { useUserStore } from '@/store/user';
const Chat = () => { const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
const router = useRouter(); const router = useRouter();
const { appId = '', chatId = '' } = router.query as { appId: string; chatId: string };
const theme = useTheme(); const theme = useTheme();
const { toast } = useToast();
const ChatBoxRef = useRef<ComponentRef>(null); const ChatBoxRef = useRef<ComponentRef>(null);
const forbidRefresh = useRef(false); const forbidRefresh = useRef(false);
@@ -44,6 +49,7 @@ const Chat = () => {
chatData, chatData,
setChatData setChatData
} = useChatStore(); } = useChatStore();
const { myApps, userInfo } = useUserStore();
const { isPc } = useGlobalStore(); const { isPc } = useGlobalStore();
const { Loading, setIsLoading } = useLoading(); const { Loading, setIsLoading } = useLoading();
@@ -52,12 +58,14 @@ const Chat = () => {
const startChat = useCallback( const startChat = useCallback(
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
const prompts = messages.slice(-2); const prompts = messages.slice(-2);
const { responseText, newChatId, rawSearch } = await streamFetch({ const completionChatId = chatId ? chatId : nanoid();
const { responseText } = await streamFetch({
data: { data: {
messages: prompts, messages: prompts,
variables, variables,
appId, appId,
chatId chatId: completionChatId
}, },
onMessage: generatingMessage, onMessage: generatingMessage,
abortSignal: controller abortSignal: controller
@@ -66,10 +74,10 @@ const Chat = () => {
const newTitle = prompts[0].content?.slice(0, 20) || '新对话'; const newTitle = prompts[0].content?.slice(0, 20) || '新对话';
// update history // update history
if (newChatId && !controller.signal.aborted) { if (completionChatId !== chatId && !controller.signal.aborted) {
forbidRefresh.current = true; forbidRefresh.current = true;
const newHistory: ChatHistoryItemType = { const newHistory: ChatHistoryItemType = {
_id: newChatId, chatId: completionChatId,
updateTime: new Date(), updateTime: new Date(),
title: newTitle, title: newTitle,
appId, appId,
@@ -78,12 +86,12 @@ const Chat = () => {
updateHistory(newHistory); updateHistory(newHistory);
router.replace({ router.replace({
query: { query: {
chatId: newChatId, chatId: completionChatId,
appId appId
} }
}); });
} else { } else {
const currentChat = history.find((item) => item._id === chatId); const currentChat = history.find((item) => item.chatId === chatId);
currentChat && currentChat &&
updateHistory({ updateHistory({
...currentChat, ...currentChat,
@@ -98,7 +106,7 @@ const Chat = () => {
history: ChatBoxRef.current?.getChatHistory() || state.history history: ChatBoxRef.current?.getChatHistory() || state.history
})); }));
return { responseText, rawSearch }; return { responseText };
}, },
[appId, chatId, history, router, setChatData, updateHistory] [appId, chatId, history, router, setChatData, updateHistory]
); );
@@ -153,38 +161,43 @@ const Chat = () => {
ChatBoxRef.current?.scrollToBottom('auto'); ChatBoxRef.current?.scrollToBottom('auto');
}, 200); }, 200);
} }
// empty appId request, return first app
if (res.appId !== appId) {
forbidRefresh.current = true;
router.replace({
query: {
appId: res.appId
}
});
}
} catch (e: any) { } catch (e: any) {
// reset all chat tore // reset all chat tore
setLastChatAppId(''); setLastChatAppId('');
setLastChatId(''); setLastChatId('');
router.replace('/chat'); toast({
title: getErrText(e, '初始化聊天失败'),
status: 'error'
});
if (e?.code === 501) {
router.replace('/app/list');
} else {
router.replace('/chat');
}
} }
setIsLoading(false); setIsLoading(false);
return null; return null;
}, },
[setIsLoading, setChatData, router, setLastChatAppId, setLastChatId] [setIsLoading, setChatData, router, setLastChatAppId, setLastChatId, toast]
); );
// 初始化聊天框 // 初始化聊天框
useQuery(['init', appId, chatId], () => { useQuery(['init', appId, chatId], () => {
// pc: redirect to latest model chat // pc: redirect to latest model chat
if (!appId && lastChatAppId) { if (!appId && lastChatAppId) {
router.replace({ return router.replace({
query: { query: {
appId: lastChatAppId, appId: lastChatAppId,
chatId: lastChatId chatId: lastChatId
} }
}); });
return null; }
if (!appId && myApps[0]) {
return router.replace({
query: {
appId: myApps[0]._id,
chatId: lastChatId
}
});
} }
// store id // store id
@@ -196,11 +209,15 @@ const Chat = () => {
return null; return null;
} }
return loadChatInfo({ if (appId) {
appId, return loadChatInfo({
chatId, appId,
loading: appId !== chatData.appId chatId,
}); loading: appId !== chatData.appId
});
}
return null;
}); });
useQuery(['loadHistory', appId], () => (appId ? loadHistory({ appId }) : null)); useQuery(['loadHistory', appId], () => (appId ? loadHistory({ appId }) : null));
@@ -233,7 +250,7 @@ const Chat = () => {
appAvatar={chatData.app.avatar} appAvatar={chatData.app.avatar}
activeChatId={chatId} activeChatId={chatId}
history={history.map((item) => ({ history={history.map((item) => ({
id: item._id, id: item.chatId,
title: item.title, title: item.title,
customTitle: item.customTitle, customTitle: item.customTitle,
top: item.top top: item.top
@@ -253,7 +270,7 @@ const Chat = () => {
onSetHistoryTop={async (e) => { onSetHistoryTop={async (e) => {
try { try {
await putChatHistory(e); await putChatHistory(e);
const historyItem = history.find((item) => item._id === e.chatId); const historyItem = history.find((item) => item.chatId === e.chatId);
if (!historyItem) return; if (!historyItem) return;
updateHistory({ updateHistory({
...historyItem, ...historyItem,
@@ -267,7 +284,7 @@ const Chat = () => {
chatId: e.chatId, chatId: e.chatId,
customTitle: e.title customTitle: e.title
}); });
const historyItem = history.find((item) => item._id === e.chatId); const historyItem = history.find((item) => item.chatId === e.chatId);
if (!historyItem) return; if (!historyItem) return;
updateHistory({ updateHistory({
...historyItem, ...historyItem,
@@ -300,6 +317,7 @@ const Chat = () => {
showEmptyIntro showEmptyIntro
chatId={chatId} chatId={chatId}
appAvatar={chatData.app.avatar} appAvatar={chatData.app.avatar}
userAvatar={userInfo?.avatar}
variableModules={chatData.app.variableModules} variableModules={chatData.app.variableModules}
welcomeText={chatData.app.welcomeText} welcomeText={chatData.app.welcomeText}
onUpdateVariable={(e) => {}} onUpdateVariable={(e) => {}}
@@ -315,4 +333,13 @@ const Chat = () => {
); );
}; };
export async function getServerSideProps(context: any) {
return {
props: {
appId: context?.query?.appId || '',
chatId: context?.query?.chatId || ''
}
};
}
export default Chat; export default Chat;

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef } from 'react'; import React, { useCallback, useMemo, useRef } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { initShareChatInfo } from '@/api/chat'; import { initShareChatInfo } from '@/api/chat';
import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react'; import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
@@ -10,14 +10,14 @@ import { useShareChatStore, defaultHistory } from '@/store/shareChat';
import SideBar from '@/components/SideBar'; import SideBar from '@/components/SideBar';
import { gptMessage2ChatType } from '@/utils/adapt'; import { gptMessage2ChatType } from '@/utils/adapt';
import { getErrText } from '@/utils/tools'; import { getErrText } from '@/utils/tools';
import dynamic from 'next/dynamic'; import { ChatSiteItemType } from '@/types/chat';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox'; import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
import PageContainer from '@/components/PageContainer'; import PageContainer from '@/components/PageContainer';
import ChatHeader from './components/ChatHeader'; import ChatHeader from './components/ChatHeader';
import ChatHistorySlider from './components/ChatHistorySlider';
const ChatHistorySlider = dynamic(() => import('./components/ChatHistorySlider'), {
ssr: false
});
const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) => { const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
const router = useRouter(); const router = useRouter();
@@ -36,44 +36,46 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
delOneShareHistoryByChatId, delOneShareHistoryByChatId,
delManyShareChatHistoryByShareId delManyShareChatHistoryByShareId
} = useShareChatStore(); } = useShareChatStore();
const history = useMemo(
() => shareChatHistory.filter((item) => item.shareId === shareId),
[shareChatHistory, shareId]
);
const startChat = useCallback( const startChat = useCallback(
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
const prompts = messages.slice(-shareChatData.maxContext - 2); const prompts = messages.slice(-2);
const completionChatId = chatId ? chatId : nanoid();
const { responseText } = await streamFetch({ const { responseText } = await streamFetch({
data: { data: {
history,
messages: prompts, messages: prompts,
variables, variables,
shareId shareId,
chatId: completionChatId
}, },
onMessage: generatingMessage, onMessage: generatingMessage,
abortSignal: controller abortSignal: controller
}); });
const result = { const result: ChatSiteItemType[] = gptMessage2ChatType(prompts).map((item) => ({
question: messages[messages.length - 2].content || '', ...item,
answer: responseText status: 'finish'
}; }));
result[1].value = responseText;
prompts[prompts.length - 1].content = responseText;
/* save chat */ /* save chat */
const { newChatId } = saveChatResponse({ saveChatResponse({
chatId, chatId: completionChatId,
prompts: gptMessage2ChatType(prompts).map((item) => ({ prompts: result,
...item,
status: 'finish'
})),
variables, variables,
shareId shareId
}); });
if (newChatId && !controller.signal.aborted) { if (completionChatId !== chatId && !controller.signal.aborted) {
router.replace({ router.replace({
query: { query: {
shareId, shareId,
chatId: newChatId chatId: completionChatId
} }
}); });
} }
@@ -81,14 +83,17 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
window.top?.postMessage( window.top?.postMessage(
{ {
type: 'shareChatFinish', type: 'shareChatFinish',
data: result data: {
question: result[0]?.value,
answer: result[1]?.value
}
}, },
'*' '*'
); );
return { responseText }; return { responseText };
}, },
[chatId, router, saveChatResponse, shareChatData.maxContext, shareId] [chatId, router, saveChatResponse, shareId]
); );
const loadAppInfo = useCallback( const loadAppInfo = useCallback(
@@ -96,7 +101,7 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
console.log(shareId, chatId); console.log(shareId, chatId);
if (!shareId) return null; if (!shareId) return null;
const history = shareChatHistory.find((item) => item._id === chatId) || defaultHistory; const history = shareChatHistory.find((item) => item.chatId === chatId) || defaultHistory;
ChatBoxRef.current?.resetHistory(history.chats); ChatBoxRef.current?.resetHistory(history.chats);
ChatBoxRef.current?.resetVariables(history.variables); ChatBoxRef.current?.resetVariables(history.variables);
@@ -157,11 +162,13 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
appName={shareChatData.app.name} appName={shareChatData.app.name}
appAvatar={shareChatData.app.avatar} appAvatar={shareChatData.app.avatar}
activeChatId={chatId} activeChatId={chatId}
history={shareChatHistory.map((item) => ({ history={history.map((item) => ({
id: item._id, id: item.chatId,
title: item.title title: item.title
}))} }))}
onChangeChat={(chatId) => { onChangeChat={(chatId) => {
console.log(chatId);
router.push({ router.push({
query: { query: {
chatId: chatId || '', chatId: chatId || '',
@@ -196,6 +203,7 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
<ChatBox <ChatBox
ref={ChatBoxRef} ref={ChatBoxRef}
appAvatar={shareChatData.app.avatar} appAvatar={shareChatData.app.avatar}
userAvatar={shareChatData.userAvatar}
variableModules={shareChatData.app.variableModules} variableModules={shareChatData.app.variableModules}
welcomeText={shareChatData.app.welcomeText} welcomeText={shareChatData.app.welcomeText}
onUpdateVariable={(e) => { onUpdateVariable={(e) => {

View File

@@ -4,13 +4,12 @@ import { useGlobalStore } from '@/store/global';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { clearCookie } from '@/utils/user'; import { clearCookie } from '@/utils/user';
import { useUserStore } from '@/store/user';
import { useConfirm } from '@/hooks/useConfirm';
import PageContainer from '@/components/PageContainer'; import PageContainer from '@/components/PageContainer';
import SideTabs from '@/components/SideTabs'; import SideTabs from '@/components/SideTabs';
import Tabs from '@/components/Tabs'; import Tabs from '@/components/Tabs';
import UserInfo from './components/Info'; import UserInfo from './components/Info';
import { useUserStore } from '@/store/user';
const BillTable = dynamic(() => import('./components/BillTable'), { const BillTable = dynamic(() => import('./components/BillTable'), {
ssr: false ssr: false
@@ -39,6 +38,10 @@ const NumberSetting = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{ icon: 'loginoutLight', label: '登出', id: TabEnum.loginout, Component: () => <></> } { icon: 'loginoutLight', label: '登出', id: TabEnum.loginout, Component: () => <></> }
]); ]);
const { openConfirm, ConfirmChild } = useConfirm({
content: '确认退出登录?'
});
const router = useRouter(); const router = useRouter();
const theme = useTheme(); const theme = useTheme();
const { isPc } = useGlobalStore(); const { isPc } = useGlobalStore();
@@ -47,9 +50,11 @@ const NumberSetting = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const setCurrentTab = useCallback( const setCurrentTab = useCallback(
(tab: string) => { (tab: string) => {
if (tab === TabEnum.loginout) { if (tab === TabEnum.loginout) {
clearCookie(); openConfirm(() => {
setUserInfo(null); clearCookie();
router.replace('/login'); setUserInfo(null);
router.replace('/login');
})();
} else { } else {
router.replace({ router.replace({
query: { query: {
@@ -58,7 +63,7 @@ const NumberSetting = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
}); });
} }
}, },
[router, setUserInfo] [openConfirm, router, setUserInfo]
); );
return ( return (
@@ -105,6 +110,7 @@ const NumberSetting = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{currentTab === TabEnum.inform && <InformTable />} {currentTab === TabEnum.inform && <InformTable />}
</Box> </Box>
</Flex> </Flex>
<ConfirmChild />
</PageContainer> </PageContainer>
); );
}; };

View File

@@ -1,9 +1,13 @@
import { Schema, model, models, Model } from 'mongoose'; import { Schema, model, models, Model } from 'mongoose';
import { ChatSchema as ChatType } from '@/types/mongoSchema'; import { ChatSchema as ChatType } from '@/types/mongoSchema';
import { ChatRoleMap } from '@/constants/chat'; import { ChatRoleMap, TaskResponseKeyEnum } from '@/constants/chat';
import { ChatSourceEnum, ChatSourceMap } from '@/constants/chat'; import { ChatSourceEnum, ChatSourceMap } from '@/constants/chat';
const ChatSchema = new Schema({ const ChatSchema = new Schema({
chatId: {
type: String,
require: true
},
userId: { userId: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
ref: 'user', ref: 'user',
@@ -33,14 +37,14 @@ const ChatSchema = new Schema({
type: Object, type: Object,
default: {} default: {}
}, },
// source: { source: {
// type: String, type: String,
// enum: Object.keys(ChatSourceMap), enum: Object.keys(ChatSourceMap),
// required: true required: true
// }, },
// shareId: { shareId: {
// type: String type: String
// }, },
content: { content: {
type: [ type: [
{ {
@@ -53,14 +57,22 @@ const ChatSchema = new Schema({
type: String, type: String,
default: '' default: ''
}, },
rawSearch: { [TaskResponseKeyEnum.responseData]: {
type: [ type: [
{ {
id: String, moduleName: String,
q: String, price: String,
a: String, model: String,
kb_id: String, tokens: Number,
source: String question: String,
answer: String,
temperature: Number,
maxToken: Number,
finishMessages: Array,
similarity: Number,
limit: Number,
cqList: Array,
cqResult: String
} }
], ],
default: [] default: []

View File

@@ -24,10 +24,6 @@ const ShareChatSchema = new Schema({
type: Number, type: Number,
default: 0 default: 0
}, },
maxContext: {
type: Number,
default: 20
},
lastTime: { lastTime: {
type: Date type: Date
} }

View File

@@ -113,7 +113,7 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')}
const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false }); const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false });
const chatAPI = getOpenAIApi(); const chatAPI = getOpenAIApi();
// console.log(adaptMessages); console.log(adaptMessages);
/* count response max token */ /* count response max token */
const promptsToken = modelToolMap.countTokens({ const promptsToken = modelToolMap.countTokens({
@@ -128,8 +128,8 @@ ${quoteQA.map((item, i) => `${i + 1}. [${item.q}\n${item.a}]`).join('\n')}
temperature: Number(temperature || 0), temperature: Number(temperature || 0),
max_tokens: maxToken, max_tokens: maxToken,
messages: adaptMessages, messages: adaptMessages,
frequency_penalty: 0.5, // 越大,重复内容越少 // frequency_penalty: 0.5, // 越大,重复内容越少
presence_penalty: -0.5, // 越大,越容易出现新内容 // presence_penalty: -0.5, // 越大,越容易出现新内容
stream stream
}, },
{ {

View File

@@ -0,0 +1,65 @@
import { ChatItemType } from '@/types/chat';
import { Chat, App } from '@/service/mongo';
import { ChatSourceEnum } from '@/constants/chat';
type Props = {
chatId: string;
appId: string;
userId: string;
variables?: Record<string, any>;
isOwner: boolean;
source: `${ChatSourceEnum}`;
shareId?: string;
content: [ChatItemType, ChatItemType];
};
export async function saveChat({
chatId,
appId,
userId,
variables,
isOwner,
source,
shareId,
content
}: Props) {
const chatHistory = await Chat.findOne(
{
chatId,
userId
},
'_id'
);
if (chatHistory) {
await Chat.findOneAndUpdate(
{ chatId },
{
$push: {
content: {
$each: content
}
},
title: content[0].value.slice(0, 20),
updateTime: new Date()
}
);
} else {
await Chat.create({
chatId,
userId,
appId,
variables,
title: content[0].value.slice(0, 20),
source,
shareId,
content: content
});
}
if (isOwner && source === ChatSourceEnum.online) {
App.findByIdAndUpdate(appId, {
updateTime: new Date()
});
}
}

View File

@@ -65,12 +65,12 @@ export const useChatStore = create<State>()(
}, },
async delHistory(chatId) { async delHistory(chatId) {
set((state) => { set((state) => {
state.history = state.history.filter((item) => item._id !== chatId); state.history = state.history.filter((item) => item.chatId !== chatId);
}); });
await delChatHistoryById(chatId); await delChatHistoryById(chatId);
}, },
updateHistory(history) { updateHistory(history) {
const index = get().history.findIndex((item) => item._id === history._id); const index = get().history.findIndex((item) => item.chatId === history.chatId);
set((state) => { set((state) => {
const newHistory = (() => { const newHistory = (() => {
if (index > -1) { if (index > -1) {

View File

@@ -4,8 +4,6 @@ import { immer } from 'zustand/middleware/immer';
import type { ChatSiteItemType, ShareChatHistoryItemType, ShareChatType } from '@/types/chat'; import type { ChatSiteItemType, ShareChatHistoryItemType, ShareChatType } from '@/types/chat';
import { HUMAN_ICON } from '@/constants/chat'; import { HUMAN_ICON } from '@/constants/chat';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
type State = { type State = {
shareChatData: ShareChatType; shareChatData: ShareChatType;
@@ -16,21 +14,20 @@ type State = {
prompts: ChatSiteItemType[]; prompts: ChatSiteItemType[];
variables: Record<string, any>; variables: Record<string, any>;
shareId: string; shareId: string;
}) => { newChatId: string }; }) => void;
delOneShareHistoryByChatId: (chatId: string) => void; delOneShareHistoryByChatId: (chatId: string) => void;
delShareChatHistoryItemById: (e: { chatId: string; index: number }) => void; delShareChatHistoryItemById: (e: { chatId: string; index: number }) => void;
delManyShareChatHistoryByShareId: (shareId?: string) => void; delManyShareChatHistoryByShareId: (shareId?: string) => void;
}; };
export const defaultHistory: ShareChatHistoryItemType = { export const defaultHistory: ShareChatHistoryItemType = {
_id: `${Date.now()}`, chatId: `${Date.now()}`,
updateTime: new Date(), updateTime: new Date(),
title: '新对话', title: '新对话',
shareId: '', shareId: '',
chats: [] chats: []
}; };
const defaultShareChatData: ShareChatType = { const defaultShareChatData: ShareChatType = {
maxContext: 5,
userAvatar: HUMAN_ICON, userAvatar: HUMAN_ICON,
app: { app: {
name: '', name: '',
@@ -57,32 +54,30 @@ export const useShareChatStore = create<State>()(
state.shareChatData = val; state.shareChatData = val;
// update history // update history
state.shareChatHistory = state.shareChatHistory.map((item) => state.shareChatHistory = state.shareChatHistory.map((item) =>
item._id === val.history._id ? val.history : item item.chatId === val.history.chatId ? val.history : item
); );
}); });
}, },
shareChatHistory: [], shareChatHistory: [],
saveChatResponse({ chatId, prompts, variables, shareId }) { saveChatResponse({ chatId, prompts, variables, shareId }) {
const history = get().shareChatHistory.find((item) => item._id === chatId); const chatHistory = get().shareChatHistory.find((item) => item.chatId === chatId);
const newChatId = history ? '' : nanoid();
const historyList = (() => { const historyList = (() => {
if (history) { if (chatHistory) {
return get().shareChatHistory.map((item) => return get().shareChatHistory.map((item) =>
item._id === chatId item.chatId === chatId
? { ? {
...item, ...item,
title: prompts[prompts.length - 2]?.value, title: prompts[prompts.length - 2]?.value,
updateTime: new Date(), updateTime: new Date(),
chats: prompts, chats: chatHistory.chats.concat(prompts).slice(-50),
variables variables
} }
: item : item
); );
} }
return get().shareChatHistory.concat({ return get().shareChatHistory.concat({
_id: newChatId, chatId,
shareId, shareId,
title: prompts[prompts.length - 2]?.value, title: prompts[prompts.length - 2]?.value,
updateTime: new Date(), updateTime: new Date(),
@@ -97,21 +92,19 @@ export const useShareChatStore = create<State>()(
set((state) => { set((state) => {
state.shareChatHistory = historyList.slice(0, 100); state.shareChatHistory = historyList.slice(0, 100);
}); });
return {
newChatId
};
}, },
delOneShareHistoryByChatId(chatId: string) { delOneShareHistoryByChatId(chatId: string) {
set((state) => { set((state) => {
state.shareChatHistory = state.shareChatHistory.filter((item) => item._id !== chatId); state.shareChatHistory = state.shareChatHistory.filter(
(item) => item.chatId !== chatId
);
}); });
}, },
delShareChatHistoryItemById({ chatId, index }) { delShareChatHistoryItemById({ chatId, index }) {
set((state) => { set((state) => {
// update history store // update history store
const newHistoryList = state.shareChatHistory.map((item) => const newHistoryList = state.shareChatHistory.map((item) =>
item._id === chatId item.chatId === chatId
? { ? {
...item, ...item,
chats: [...item.chats.slice(0, index), ...item.chats.slice(index + 1)] chats: [...item.chats.slice(0, index), ...item.chats.slice(index + 1)]

View File

@@ -37,7 +37,6 @@ export interface ShareAppItem {
export type ShareChatEditType = { export type ShareChatEditType = {
name: string; name: string;
maxContext: number;
}; };
/* agent */ /* agent */

View File

@@ -1,4 +1,4 @@
import { ChatRoleEnum, rawSearchKey } from '@/constants/chat'; import { ChatRoleEnum } from '@/constants/chat';
import type { InitChatResponse, InitShareChatResponse } from '@/api/response/chat'; import type { InitChatResponse, InitShareChatResponse } from '@/api/response/chat';
import { TaskResponseKeyEnum } from '@/constants/chat'; import { TaskResponseKeyEnum } from '@/constants/chat';
import { ClassifyQuestionAgentItemType } from './app'; import { ClassifyQuestionAgentItemType } from './app';
@@ -9,9 +9,7 @@ export type ChatItemType = {
_id?: string; _id?: string;
obj: `${ChatRoleEnum}`; obj: `${ChatRoleEnum}`;
value: string; value: string;
[rawSearchKey]?: QuoteItemType[]; [TaskResponseKeyEnum.responseData]?: ChatHistoryItemResType[];
quoteLen?: number;
[key: string]: any;
}; };
export type ChatSiteItemType = { export type ChatSiteItemType = {
@@ -19,7 +17,7 @@ export type ChatSiteItemType = {
} & ChatItemType; } & ChatItemType;
export type HistoryItemType = { export type HistoryItemType = {
_id: string; chatId: string;
updateTime: Date; updateTime: Date;
customTitle?: string; customTitle?: string;
title: string; title: string;

View File

@@ -18,7 +18,7 @@ export type FeConfigsType = {
show_register?: boolean; show_register?: boolean;
show_appStore?: boolean; show_appStore?: boolean;
show_userDetail?: boolean; show_userDetail?: boolean;
show_git?: false; show_git?: boolean;
systemTitle?: string; systemTitle?: string;
authorText?: string; authorText?: string;
}; };

View File

@@ -4,6 +4,7 @@ import type { DataType } from './data';
import { BillSourceEnum, InformTypeEnum } from '@/constants/user'; import { BillSourceEnum, InformTypeEnum } from '@/constants/user';
import { TrainingModeEnum } from '@/constants/plugin'; import { TrainingModeEnum } from '@/constants/plugin';
import type { AppModuleItemType } from './app'; import type { AppModuleItemType } from './app';
import { ChatSourceEnum } from '@/constants/chat';
export interface UserModelSchema { export interface UserModelSchema {
_id: string; _id: string;
@@ -78,6 +79,7 @@ export interface TrainingDataSchema {
export interface ChatSchema { export interface ChatSchema {
_id: string; _id: string;
chatId: string;
userId: string; userId: string;
appId: string; appId: string;
updateTime: Date; updateTime: Date;
@@ -85,6 +87,8 @@ export interface ChatSchema {
customTitle: string; customTitle: string;
top: boolean; top: boolean;
variables: Record<string, any>; variables: Record<string, any>;
source: `${ChatSourceEnum}`;
shareId?: string;
content: ChatItemType[]; content: ChatItemType[];
} }
@@ -138,7 +142,6 @@ export interface ShareChatSchema {
appId: string; appId: string;
name: string; name: string;
total: number; total: number;
maxContext: number;
lastTime: Date; lastTime: Date;
} }