mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-28 09:03:53 +00:00
4.6.4-alpha (#569)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { Flex, BoxProps, useDisclosure, Image, useTheme, Box } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Box, useTheme, Flex, Image } from '@chakra-ui/react';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { moduleTemplatesFlat } from '@/web/core/modules/template/system';
|
||||
import Tabs from '../Tabs';
|
||||
|
@@ -12,7 +12,7 @@ import Script from 'next/script';
|
||||
import { throttle } from 'lodash';
|
||||
import type { ExportChatType } from '@/types/chat.d';
|
||||
import type { ChatItemType, ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { useAudioPlay } from '@/web/common/utils/voice';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
@@ -80,7 +80,7 @@ export type StartChatFnProps = {
|
||||
};
|
||||
|
||||
export type ComponentRef = {
|
||||
getChatHistory: () => ChatSiteItemType[];
|
||||
getChatHistories: () => ChatSiteItemType[];
|
||||
resetVariables: (data?: Record<string, any>) => void;
|
||||
resetHistory: (history: ChatSiteItemType[]) => void;
|
||||
scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
|
||||
@@ -134,7 +134,7 @@ const ChatBox = (
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { isPc } = useSystemStore();
|
||||
const { isPc, setLoading } = useSystemStore();
|
||||
const TextareaDom = useRef<HTMLTextAreaElement>(null);
|
||||
const chatController = useRef(new AbortController());
|
||||
const questionGuideController = useRef(new AbortController());
|
||||
@@ -415,15 +415,20 @@ const ChatBox = (
|
||||
async (index: number) => {
|
||||
if (!onDelMessage) return;
|
||||
const delHistory = chatHistory.slice(index);
|
||||
setChatHistory((state) => (index === 0 ? [] : state.slice(0, index)));
|
||||
|
||||
await Promise.all(
|
||||
delHistory.map((item, i) => onDelMessage({ contentId: item.dataId, index: index + i }))
|
||||
);
|
||||
setLoading(true);
|
||||
|
||||
sendPrompt(variables, delHistory[0].value, chatHistory.slice(0, index));
|
||||
try {
|
||||
await Promise.all(
|
||||
delHistory.map((item, i) => onDelMessage({ contentId: item.dataId, index: index + i }))
|
||||
);
|
||||
setChatHistory((state) => (index === 0 ? [] : state.slice(0, index)));
|
||||
|
||||
sendPrompt(variables, delHistory[0].value, chatHistory.slice(0, index));
|
||||
} catch (error) {}
|
||||
setLoading(false);
|
||||
},
|
||||
[chatHistory, onDelMessage, sendPrompt, variables]
|
||||
[chatHistory, onDelMessage, sendPrompt, setLoading, variables]
|
||||
);
|
||||
// delete one message
|
||||
const delOneMessage = useCallback(
|
||||
@@ -439,7 +444,7 @@ const ChatBox = (
|
||||
|
||||
// output data
|
||||
useImperativeHandle(ref, () => ({
|
||||
getChatHistory: () => chatHistory,
|
||||
getChatHistories: () => chatHistory,
|
||||
resetVariables(e) {
|
||||
const defaultVal: Record<string, any> = {};
|
||||
variableModules?.forEach((item) => {
|
||||
|
@@ -86,7 +86,7 @@ const DatasetParamsModal = ({
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
value={getValues(ModuleInputKeyEnum.datasetSimilarity) || 0.5}
|
||||
value={getValues(ModuleInputKeyEnum.datasetSimilarity) ?? 0.5}
|
||||
onChange={(val) => {
|
||||
setValue(ModuleInputKeyEnum.datasetSimilarity, val);
|
||||
setRefresh(!refresh);
|
||||
@@ -107,7 +107,7 @@ const DatasetParamsModal = ({
|
||||
]}
|
||||
min={1}
|
||||
max={30}
|
||||
value={getValues(ModuleInputKeyEnum.datasetLimit) || 5}
|
||||
value={getValues(ModuleInputKeyEnum.datasetLimit) ?? 5}
|
||||
onChange={(val) => {
|
||||
setValue(ModuleInputKeyEnum.datasetLimit, val);
|
||||
setRefresh(!refresh);
|
||||
|
68
projects/app/src/global/core/chat/api.d.ts
vendored
68
projects/app/src/global/core/chat/api.d.ts
vendored
@@ -1,7 +1,75 @@
|
||||
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
|
||||
import { ModuleItemType } from '../module/type';
|
||||
import { AdminFbkType, ChatItemType, moduleDispatchResType } from '@fastgpt/global/core/chat/type';
|
||||
|
||||
export type GetChatSpeechProps = {
|
||||
ttsConfig: AppTTSConfigType;
|
||||
input: string;
|
||||
shareId?: string;
|
||||
};
|
||||
|
||||
/* ---------- chat ----------- */
|
||||
export type InitChatProps = {
|
||||
appId?: string;
|
||||
chatId?: string;
|
||||
};
|
||||
export type InitOutLinkChatProps = {
|
||||
chatId?: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
export type InitChatResponse = {
|
||||
chatId?: string;
|
||||
appId: string;
|
||||
userAvatar?: string;
|
||||
title: string;
|
||||
variables: Record<string, any>;
|
||||
history: ChatItemType[];
|
||||
app: {
|
||||
userGuideModule?: ModuleItemType;
|
||||
chatModels?: string[];
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
canUse?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
/* ---------- history ----------- */
|
||||
export type getHistoriesProps = {
|
||||
appId?: string;
|
||||
// share chat
|
||||
shareId?: string;
|
||||
outLinkUid?: string; // authToken/uid
|
||||
};
|
||||
|
||||
export type UpdateHistoryProps = {
|
||||
chatId: string;
|
||||
customTitle?: string;
|
||||
top?: boolean;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
|
||||
export type DelHistoryProps = {
|
||||
chatId: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
export type ClearHistoriesProps = {
|
||||
appId?: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
|
||||
/* -------- chat item ---------- */
|
||||
export type DeleteChatItemProps = {
|
||||
chatId: string;
|
||||
contentId: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
|
||||
export type AdminUpdateFeedbackParams = AdminFbkType & {
|
||||
chatItemId: string;
|
||||
};
|
||||
|
15
projects/app/src/global/core/chat/constants.ts
Normal file
15
projects/app/src/global/core/chat/constants.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { InitChatResponse } from './api';
|
||||
|
||||
export const defaultChatData: InitChatResponse = {
|
||||
chatId: '',
|
||||
appId: '',
|
||||
app: {
|
||||
name: 'Loading',
|
||||
avatar: '/icon/logo.svg',
|
||||
intro: '',
|
||||
canUse: false
|
||||
},
|
||||
title: '新对话',
|
||||
variables: {},
|
||||
history: []
|
||||
};
|
@@ -33,9 +33,8 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
"""
|
||||
回答要求:
|
||||
1. 优先使用知识库内容回答问题。
|
||||
2. 你可以回答我不知道。
|
||||
3. 不要提及你是从知识库获取的知识。
|
||||
4. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
||||
2. 不要提及你是从知识库获取的知识。
|
||||
3. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
||||
我的问题是:"{{question}}"`
|
||||
},
|
||||
{
|
||||
@@ -47,9 +46,8 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
"""
|
||||
回答要求:
|
||||
1. 优先使用知识库内容回答问题,其中 instruction 是相关介绍,output 是预期回答或补充。
|
||||
2. 你可以回答我不知道。
|
||||
3. 不要提及你是从知识库获取的知识。
|
||||
4. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
||||
2. 不要提及你是从知识库获取的知识。
|
||||
3. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
||||
我的问题是:"{{question}}"`
|
||||
},
|
||||
{
|
||||
|
@@ -54,7 +54,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
|
||||
onSuccess(res) {
|
||||
if (!res) return;
|
||||
toast({
|
||||
title: '充值成功',
|
||||
title: res,
|
||||
status: 'success'
|
||||
});
|
||||
router.reload();
|
||||
|
@@ -1,32 +1,14 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { userId } = await authCert({ req, authToken: true });
|
||||
await unlockTask(userId);
|
||||
await authCert({ req, authToken: true });
|
||||
startQueue();
|
||||
} catch (error) {}
|
||||
jsonRes(res);
|
||||
}
|
||||
|
||||
async function unlockTask(userId: string) {
|
||||
try {
|
||||
await MongoDatasetTraining.updateMany(
|
||||
{
|
||||
userId
|
||||
},
|
||||
{
|
||||
lockTime: new Date('2000/1/1')
|
||||
}
|
||||
);
|
||||
|
||||
startQueue();
|
||||
} catch (error) {
|
||||
unlockTask(userId);
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -20,6 +21,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
await authApp({ req, authToken: true, appId, per: 'owner' });
|
||||
|
||||
// 删除对应的聊天
|
||||
await MongoChatItem.deleteMany({
|
||||
appId
|
||||
});
|
||||
await MongoChat.deleteMany({
|
||||
appId
|
||||
});
|
||||
|
@@ -381,7 +381,7 @@ function datasetTemplate({
|
||||
key: 'similarity',
|
||||
value: 0.4,
|
||||
type: FlowNodeInputTypeEnum.slider,
|
||||
label: '相似度',
|
||||
label: '相关度',
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
|
@@ -289,7 +289,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
key: 'similarity',
|
||||
value: formData.dataset.similarity,
|
||||
type: FlowNodeInputTypeEnum.slider,
|
||||
label: '相似度',
|
||||
label: '相关度',
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
|
@@ -8,8 +8,9 @@ import { pushChatBill } from '@/service/support/wallet/bill/push';
|
||||
import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { authUser } from '@/service/support/permission/auth/user';
|
||||
import { dispatchModules } from '@/service/moduleDispatch';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
|
||||
|
||||
export type Props = {
|
||||
history: ChatItemType[];
|
||||
@@ -40,15 +41,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
}
|
||||
|
||||
/* user auth */
|
||||
const [{ teamId, tmbId }, { user }] = await Promise.all([
|
||||
const [_, { teamId, tmbId }] = await Promise.all([
|
||||
authApp({ req, authToken: true, appId, per: 'r' }),
|
||||
authUser({
|
||||
authCert({
|
||||
req,
|
||||
authToken: true,
|
||||
minBalance: 0
|
||||
authToken: true
|
||||
})
|
||||
]);
|
||||
|
||||
// auth balance
|
||||
const user = await getUserAndAuthBalance({
|
||||
tmbId,
|
||||
minBalance: 0
|
||||
});
|
||||
|
||||
/* start process */
|
||||
const { responseData } = await dispatchModules({
|
||||
res,
|
||||
|
58
projects/app/src/pages/api/core/chat/clearHistories.ts
Normal file
58
projects/app/src/pages/api/core/chat/clearHistories.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { ClearHistoriesProps } from '@/global/core/chat/api';
|
||||
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
/* clear chat history */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId, shareId, outLinkUid } = req.query as ClearHistoriesProps;
|
||||
|
||||
const match = await (async () => {
|
||||
if (shareId && outLinkUid) {
|
||||
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||
|
||||
return {
|
||||
shareId,
|
||||
outLinkUid: uid
|
||||
};
|
||||
}
|
||||
if (appId) {
|
||||
const { tmbId } = await authCert({ req, authToken: true });
|
||||
|
||||
return {
|
||||
appId,
|
||||
tmbId,
|
||||
source: ChatSourceEnum.online
|
||||
};
|
||||
}
|
||||
|
||||
return Promise.reject('Param are error');
|
||||
})();
|
||||
console.log(match);
|
||||
|
||||
// find chatIds
|
||||
const list = await MongoChat.find(match, 'chatId').lean();
|
||||
const idList = list.map((item) => item.chatId);
|
||||
|
||||
await MongoChatItem.deleteMany({
|
||||
chatId: { $in: idList }
|
||||
});
|
||||
await MongoChat.deleteMany({
|
||||
chatId: { $in: idList }
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
38
projects/app/src/pages/api/core/chat/delHistory.ts
Normal file
38
projects/app/src/pages/api/core/chat/delHistory.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { DelHistoryProps } from '@/global/core/chat/api';
|
||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
|
||||
/* clear chat history */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { chatId, shareId, outLinkUid } = req.query as DelHistoryProps;
|
||||
|
||||
await autChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await MongoChatItem.deleteMany({
|
||||
chatId
|
||||
});
|
||||
await MongoChat.findOneAndRemove({
|
||||
chatId
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
type Props = {
|
||||
chatId?: string;
|
||||
appId?: string;
|
||||
};
|
||||
|
||||
/* clear chat history */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { chatId, appId } = req.query as Props;
|
||||
|
||||
const { tmbId } = await authCert({ req, authToken: true });
|
||||
|
||||
if (chatId) {
|
||||
await MongoChatItem.deleteMany({
|
||||
chatId,
|
||||
tmbId
|
||||
});
|
||||
await MongoChat.findOneAndRemove({
|
||||
chatId,
|
||||
tmbId
|
||||
});
|
||||
}
|
||||
if (appId) {
|
||||
const chats = await MongoChat.find({
|
||||
appId,
|
||||
tmbId,
|
||||
source: ChatSourceEnum.online
|
||||
}).select('_id');
|
||||
const chatIds = chats.map((chat) => chat._id);
|
||||
await MongoChatItem.deleteMany({
|
||||
chatId: { $in: chatIds }
|
||||
});
|
||||
await MongoChat.deleteMany({
|
||||
_id: { $in: chatIds }
|
||||
});
|
||||
}
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import type { AdminUpdateFeedbackParams } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { AdminUpdateFeedbackParams } from '@/global/core/chat/api.d';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
|
62
projects/app/src/pages/api/core/chat/getHistories.ts
Normal file
62
projects/app/src/pages/api/core/chat/getHistories.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getHistoriesProps } from '@/global/core/chat/api';
|
||||
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId, shareId, outLinkUid } = req.body as getHistoriesProps;
|
||||
|
||||
const limit = shareId && outLinkUid ? 20 : 30;
|
||||
|
||||
const match = await (async () => {
|
||||
if (shareId && outLinkUid) {
|
||||
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||
|
||||
return {
|
||||
shareId,
|
||||
outLinkUid: uid,
|
||||
source: ChatSourceEnum.share,
|
||||
updateTime: {
|
||||
$gte: new Date(new Date().setDate(new Date().getDate() - 30))
|
||||
}
|
||||
};
|
||||
}
|
||||
if (appId) {
|
||||
const { tmbId } = await authCert({ req, authToken: true });
|
||||
return {
|
||||
appId,
|
||||
tmbId,
|
||||
source: ChatSourceEnum.online
|
||||
};
|
||||
}
|
||||
return Promise.reject('Params are error');
|
||||
})();
|
||||
|
||||
const data = await MongoChat.find(match, 'chatId title top customTitle appId updateTime')
|
||||
.sort({ top: -1, updateTime: -1 })
|
||||
.limit(limit);
|
||||
|
||||
jsonRes<ChatHistoryItemType[]>(res, {
|
||||
data: data.map((item) => ({
|
||||
chatId: item.chatId,
|
||||
updateTime: item.updateTime,
|
||||
appId: item.appId,
|
||||
customTitle: item.customTitle,
|
||||
title: item.title,
|
||||
top: item.top
|
||||
}))
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,24 +1,20 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import type { InitChatResponse } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
||||
import { authChat } from '@fastgpt/service/support/permission/auth/chat';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
let { appId, chatId } = req.query as {
|
||||
appId: string;
|
||||
chatId: '' | string;
|
||||
};
|
||||
let { appId, chatId } = req.query as InitChatProps;
|
||||
|
||||
if (!appId) {
|
||||
return jsonRes(res, {
|
||||
@@ -27,57 +23,44 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
}
|
||||
|
||||
// 校验使用权限
|
||||
const [{ app }, autChatResult] = await Promise.all([
|
||||
// auth app permission
|
||||
const [{ app, tmbId }, chat] = await Promise.all([
|
||||
authApp({
|
||||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
per: 'r'
|
||||
}),
|
||||
chatId
|
||||
? authChat({
|
||||
req,
|
||||
authToken: true,
|
||||
chatId,
|
||||
per: 'r'
|
||||
})
|
||||
: undefined
|
||||
chatId ? MongoChat.findOne({ chatId }) : undefined
|
||||
]);
|
||||
|
||||
// get app and history
|
||||
const { history = [] }: { history?: ChatItemType[] } = await (async () => {
|
||||
if (chatId) {
|
||||
// auth chatId
|
||||
const history = await MongoChatItem.find(
|
||||
{
|
||||
chatId
|
||||
},
|
||||
`dataId obj value adminFeedback userFeedback ${ModuleOutputKeyEnum.responseData}`
|
||||
)
|
||||
.sort({ _id: -1 })
|
||||
.limit(30);
|
||||
// auth chat permission
|
||||
if (!app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
|
||||
throw new Error(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
history.reverse();
|
||||
return { history };
|
||||
}
|
||||
return {};
|
||||
})();
|
||||
// get app and history
|
||||
const { history } = await getChatItems({
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value adminFeedback userFeedback ${ModuleOutputKeyEnum.responseData}`
|
||||
});
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
data: {
|
||||
chatId,
|
||||
appId,
|
||||
title: chat?.title || '新对话',
|
||||
userAvatar: undefined,
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(app.modules),
|
||||
chatModels: getChatModelNameListByModules(app.modules),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro
|
||||
},
|
||||
title: autChatResult?.chat?.title || '新对话',
|
||||
variables: autChatResult?.chat?.variables || {},
|
||||
history
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
|
@@ -0,0 +1,42 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
/* clear chat history */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { outLinkUid, chatIds } = req.body as {
|
||||
outLinkUid: string;
|
||||
chatIds: string[];
|
||||
};
|
||||
|
||||
if (!outLinkUid) {
|
||||
throw new Error('shareId or outLinkUid is required');
|
||||
}
|
||||
|
||||
const sliceIds = chatIds.slice(0, 50);
|
||||
|
||||
await MongoChat.updateMany(
|
||||
{
|
||||
chatId: { $in: sliceIds },
|
||||
source: ChatSourceEnum.share,
|
||||
outLinkUid: { $exists: false }
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
outLinkUid
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -2,14 +2,22 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { authChat } from '@fastgpt/service/support/permission/auth/chat';
|
||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
import type { DeleteChatItemProps } from '@/global/core/chat/api.d';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { chatId, contentId } = req.query as { chatId: string; contentId: string };
|
||||
const { chatId, contentId, shareId, outLinkUid } = req.query as DeleteChatItemProps;
|
||||
|
||||
await authChat({ req, authToken: true, chatId, per: 'w' });
|
||||
await autChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await MongoChatItem.deleteOne({
|
||||
dataId: contentId,
|
||||
|
@@ -1,43 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
|
||||
/* 获取历史记录 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId } = req.body as { appId: string };
|
||||
const { tmbId } = await authApp({ req, authToken: true, appId, per: 'r' });
|
||||
|
||||
const data = await MongoChat.find(
|
||||
{
|
||||
appId,
|
||||
tmbId,
|
||||
source: ChatSourceEnum.online
|
||||
},
|
||||
'chatId title top customTitle appId updateTime'
|
||||
)
|
||||
.sort({ top: -1, updateTime: -1 })
|
||||
.limit(20);
|
||||
|
||||
jsonRes<ChatHistoryItemType[]>(res, {
|
||||
data: data.map((item) => ({
|
||||
chatId: item.chatId,
|
||||
updateTime: item.updateTime,
|
||||
appId: item.appId,
|
||||
customTitle: item.customTitle,
|
||||
title: item.title,
|
||||
top: item.top
|
||||
}))
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
85
projects/app/src/pages/api/core/chat/outLink/init.ts
Normal file
85
projects/app/src/pages/api/core/chat/outLink/init.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { InitChatResponse, InitOutLinkChatProps } from '@/global/core/chat/api.d';
|
||||
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { selectShareResponse } from '@/utils/service/core/chat';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
let { chatId, shareId, outLinkUid } = req.query as InitOutLinkChatProps;
|
||||
|
||||
// auth link permission
|
||||
const { shareChat, uid, appId } = await authOutLink({ shareId, outLinkUid });
|
||||
|
||||
// auth app permission
|
||||
const [tmb, chat, app] = await Promise.all([
|
||||
MongoTeamMember.findById(shareChat.tmbId, '_id userId').populate('userId', 'avatar').lean(),
|
||||
MongoChat.findOne({ chatId, shareId }).lean(),
|
||||
MongoApp.findById(appId).lean()
|
||||
]);
|
||||
|
||||
if (!app) {
|
||||
throw new Error(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
// auth chat permission
|
||||
if (chat && chat.outLinkUid !== uid) {
|
||||
throw new Error(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
const { history } = await getChatItems({
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value userFeedback ${
|
||||
shareChat.responseDetail ? `adminFeedback ${ModuleOutputKeyEnum.responseData}` : ''
|
||||
} `
|
||||
});
|
||||
|
||||
// pick share response field
|
||||
history.forEach((item) => {
|
||||
item.responseData = selectShareResponse({ responseData: item.responseData });
|
||||
});
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
data: {
|
||||
chatId,
|
||||
appId: app._id,
|
||||
title: chat?.title || '新对话',
|
||||
//@ts-ignore
|
||||
userAvatar: tmb?.userId?.avatar,
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(app.modules),
|
||||
chatModels: getChatModelNameListByModules(app.modules),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
responseLimit: '10mb'
|
||||
}
|
||||
};
|
@@ -1,17 +1,24 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { UpdateHistoryProps } from '@fastgpt/global/core/chat/api.d';
|
||||
import { UpdateHistoryProps } from '@/global/core/chat/api.d';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { authChat } from '@fastgpt/service/support/permission/auth/chat';
|
||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
|
||||
/* 更新聊天标题 */
|
||||
/* update chat top, custom title */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { chatId, customTitle, top } = req.body as UpdateHistoryProps;
|
||||
const { chatId, shareId, outLinkUid, customTitle, top } = req.body as UpdateHistoryProps;
|
||||
|
||||
await authChat({ req, authToken: true, chatId });
|
||||
await autChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
await MongoChat.findOneAndUpdate(
|
||||
{ chatId },
|
@@ -8,7 +8,7 @@ import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { id, parentId, name, avatar, tags, permission, agentModel, websiteConfig, status } =
|
||||
const { id, parentId, name, avatar, intro, permission, agentModel, websiteConfig, status } =
|
||||
req.body as DatasetUpdateBody;
|
||||
|
||||
if (!id) {
|
||||
@@ -26,11 +26,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
...(parentId !== undefined && { parentId: parentId || null }),
|
||||
...(name && { name }),
|
||||
...(avatar && { avatar }),
|
||||
...(tags && { tags }),
|
||||
...(permission && { permission }),
|
||||
...(agentModel && { agentModel: agentModel.model }),
|
||||
...(websiteConfig && { websiteConfig }),
|
||||
...(status && { status })
|
||||
...(status && { status }),
|
||||
...(intro && { intro })
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -1,51 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||
import type { InitShareChatResponse } from '@fastgpt/global/support/outLink/api.d';
|
||||
import { HUMAN_ICON } from '@fastgpt/global/core/chat/constants';
|
||||
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { authShareChatInit } from '@/service/support/outLink/auth';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
||||
import { authOutLinkValid } from '@fastgpt/service/support/permission/auth/outLink';
|
||||
|
||||
/* init share chat window */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
let { shareId, authToken } = req.query as {
|
||||
shareId: string;
|
||||
authToken?: string;
|
||||
};
|
||||
|
||||
// get shareChat
|
||||
const { app, shareChat } = await authOutLinkValid({ shareId });
|
||||
|
||||
// 校验使用权限
|
||||
const [user] = await Promise.all([
|
||||
MongoUser.findById(shareChat.userId, 'avatar'),
|
||||
authShareChatInit({
|
||||
authToken,
|
||||
tokenUrl: shareChat.limit?.hookUrl
|
||||
})
|
||||
]);
|
||||
|
||||
jsonRes<InitShareChatResponse>(res, {
|
||||
data: {
|
||||
userAvatar: user?.avatar || HUMAN_ICON,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(app.modules),
|
||||
chatModels: getChatModelNameListByModules(app.modules),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||
import { createJWT, setCookie } from '@fastgpt/service/support/permission/controller';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getUserDetail } from '@/service/support/user/controller';
|
||||
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
||||
import type { PostLoginProps } from '@fastgpt/global/support/user/api.d';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getUserDetail } from '@/service/support/user/controller';
|
||||
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authCert, authCertAndShareId } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { getUploadModel } from '@fastgpt/service/common/file/upload/multer';
|
||||
import fs from 'fs';
|
||||
|
@@ -10,11 +10,11 @@ import { dispatchModules } from '@/service/moduleDispatch';
|
||||
import type { ChatCompletionCreateParams } from '@fastgpt/global/core/ai/type.d';
|
||||
import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d';
|
||||
import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt';
|
||||
import { getChatHistory } from './getHistory';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { saveChat } from '@/service/utils/chat/saveChat';
|
||||
import { responseWrite } from '@fastgpt/service/common/response';
|
||||
import { pushChatBill } from '@/service/support/wallet/bill/push';
|
||||
import { authOutLinkChat } from '@/service/support/permission/auth/outLink';
|
||||
import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink';
|
||||
import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
|
||||
import requestIp from 'request-ip';
|
||||
import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
|
||||
@@ -22,9 +22,10 @@ import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/too
|
||||
import { selectShareResponse } from '@/utils/service/core/chat';
|
||||
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getUserAndAuthBalance } from '@/service/support/permission/auth/user';
|
||||
import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
|
||||
type FastGptWebChatProps = {
|
||||
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
|
||||
@@ -32,7 +33,7 @@ type FastGptWebChatProps = {
|
||||
};
|
||||
type FastGptShareChatProps = {
|
||||
shareId?: string;
|
||||
authToken?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
export type Props = ChatCompletionCreateParams &
|
||||
FastGptWebChatProps &
|
||||
@@ -56,11 +57,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
res.end();
|
||||
});
|
||||
|
||||
let {
|
||||
const {
|
||||
chatId,
|
||||
appId,
|
||||
shareId,
|
||||
authToken,
|
||||
outLinkUid,
|
||||
stream = false,
|
||||
detail = false,
|
||||
messages = [],
|
||||
@@ -93,22 +94,29 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
throw new Error('Question is empty');
|
||||
}
|
||||
|
||||
/* auth app permission */
|
||||
const { user, app, responseDetail, authType, apikey, canWrite } = await (async () => {
|
||||
if (shareId) {
|
||||
const { user, app, authType, responseDetail } = await authOutLinkChat({
|
||||
/* auth app permission */
|
||||
const { user, app, responseDetail, authType, apikey, canWrite, uid } = await (async () => {
|
||||
if (shareId && outLinkUid) {
|
||||
const { user, appId, authType, responseDetail, uid } = await authOutLinkChatStart({
|
||||
shareId,
|
||||
ip: requestIp.getClientIp(req),
|
||||
authToken,
|
||||
outLinkUid,
|
||||
question: question.value
|
||||
});
|
||||
const app = await MongoApp.findById(appId);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject('app is empty');
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
app,
|
||||
responseDetail,
|
||||
apikey: '',
|
||||
authType,
|
||||
canWrite: false
|
||||
canWrite: false,
|
||||
uid
|
||||
};
|
||||
}
|
||||
|
||||
@@ -146,11 +154,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
};
|
||||
}
|
||||
|
||||
// token auth
|
||||
if (!appId) {
|
||||
return Promise.reject('appId is empty');
|
||||
}
|
||||
|
||||
// token
|
||||
const { app, canWrite } = await authApp({
|
||||
req,
|
||||
authToken: true,
|
||||
@@ -168,12 +175,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
};
|
||||
})();
|
||||
|
||||
// auth app, get history
|
||||
const { history } = await getChatHistory({ chatId, tmbId: user.team.tmbId });
|
||||
// auth chat permission
|
||||
await autChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
const isAppOwner = !shareId && String(user.team.tmbId) === String(app.tmbId);
|
||||
|
||||
/* format prompts */
|
||||
// get and concat history
|
||||
const { history } = await getChatItems({ chatId, limit: 30, field: `dataId obj value` });
|
||||
const concatHistory = history.concat(chatMessages);
|
||||
|
||||
/* start flow controller */
|
||||
@@ -202,8 +216,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
teamId: user.team.teamId,
|
||||
tmbId: user.team.tmbId,
|
||||
variables,
|
||||
updateUseTime: isAppOwner, // owner update use time
|
||||
updateUseTime: !shareId && String(user.team.tmbId) === String(app.tmbId), // owner update use time
|
||||
shareId,
|
||||
outLinkUid: uid,
|
||||
source: (() => {
|
||||
if (shareId) {
|
||||
return ChatSourceEnum.share;
|
||||
@@ -281,7 +296,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
});
|
||||
|
||||
if (shareId) {
|
||||
pushResult2Remote({ authToken, shareId, responseData });
|
||||
pushResult2Remote({ outLinkUid, shareId, responseData });
|
||||
updateOutLinkUsage({
|
||||
shareId,
|
||||
total
|
||||
|
@@ -1,73 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { Types } from '@fastgpt/service/common/mongo';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
|
||||
export type Props = {
|
||||
appId?: string;
|
||||
chatId?: string;
|
||||
limit?: number;
|
||||
};
|
||||
export type Response = { history: ChatItemType[] };
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { tmbId } = await authCert({ req, authToken: true });
|
||||
const { chatId, limit } = req.body as Props;
|
||||
|
||||
jsonRes<Response>(res, {
|
||||
data: await getChatHistory({
|
||||
chatId,
|
||||
tmbId,
|
||||
limit
|
||||
})
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function getChatHistory({
|
||||
chatId,
|
||||
tmbId,
|
||||
limit = 30
|
||||
}: Props & { tmbId: string }): Promise<Response> {
|
||||
if (!chatId) {
|
||||
return { history: [] };
|
||||
}
|
||||
|
||||
const history = await MongoChatItem.aggregate([
|
||||
{
|
||||
$match: {
|
||||
chatId,
|
||||
tmbId: new Types.ObjectId(tmbId)
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
_id: -1
|
||||
}
|
||||
},
|
||||
{
|
||||
$limit: limit
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
dataId: 1,
|
||||
obj: 1,
|
||||
value: 1
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
history.reverse();
|
||||
|
||||
return { history };
|
||||
}
|
@@ -23,7 +23,7 @@ import { AppLogsListItemType } from '@/types/app';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import ChatBox, { type ComponentRef } from '@/components/ChatBox';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getInitChatSiteInfo } from '@/web/core/chat/api';
|
||||
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||
import Tag from '@/components/Tag';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
|
||||
@@ -199,7 +199,7 @@ function DetailLogsModal({
|
||||
|
||||
const { data: chat } = useQuery(
|
||||
['getChatDetail', chatId],
|
||||
() => getInitChatSiteInfo({ appId, chatId }),
|
||||
() => getInitChatInfo({ appId, chatId }),
|
||||
{
|
||||
onSuccess(res) {
|
||||
const history = res.history.map((item) => ({
|
||||
|
@@ -104,7 +104,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
>
|
||||
<Flex mb={4} alignItems={'center'}>
|
||||
<Avatar src={appDetail.avatar} w={'34px'} borderRadius={'lg'} />
|
||||
<Box ml={2} fontWeight={'bold'}>
|
||||
<Box ml={2} fontWeight={'bold'} fontSize={'sm'}>
|
||||
{appDetail.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
@@ -55,7 +55,7 @@ const ChatHistorySlider = ({
|
||||
history: HistoryItemType[];
|
||||
activeChatId: string;
|
||||
onChangeChat: (chatId?: string) => void;
|
||||
onDelHistory: (chatId: string) => void;
|
||||
onDelHistory: (e: { chatId: string }) => void;
|
||||
onClearHistory: () => void;
|
||||
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
|
||||
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
|
||||
@@ -261,7 +261,7 @@ const ChatHistorySlider = ({
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelHistory(item.id);
|
||||
onDelHistory({ chatId: item.id });
|
||||
if (item.id === activeChatId) {
|
||||
onChangeChat();
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getInitChatSiteInfo, delChatRecordById, putChatHistory } from '@/web/core/chat/api';
|
||||
import { getInitChatInfo, putChatHistory } from '@/web/core/chat/api';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -34,6 +34,7 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
const router = useRouter();
|
||||
@@ -49,13 +50,15 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
setLastChatAppId,
|
||||
lastChatId,
|
||||
setLastChatId,
|
||||
history,
|
||||
loadHistory,
|
||||
histories,
|
||||
loadHistories,
|
||||
pushHistory,
|
||||
updateHistory,
|
||||
delHistory,
|
||||
clearHistory,
|
||||
delOneHistory,
|
||||
clearHistories,
|
||||
chatData,
|
||||
setChatData
|
||||
setChatData,
|
||||
delOneHistoryItem
|
||||
} = useChatStore();
|
||||
const { myApps, loadMyApps } = useAppStore();
|
||||
const { userInfo } = useUserStore();
|
||||
@@ -85,7 +88,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
prompts[1]?.value?.slice(0, 20) ||
|
||||
'新对话';
|
||||
|
||||
// update history
|
||||
// new chat
|
||||
if (completionChatId !== chatId) {
|
||||
const newHistory: ChatHistoryItemType = {
|
||||
chatId: completionChatId,
|
||||
@@ -94,7 +97,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
appId,
|
||||
top: false
|
||||
};
|
||||
updateHistory(newHistory);
|
||||
pushHistory(newHistory);
|
||||
if (controller.signal.reason !== 'leave') {
|
||||
forbidRefresh.current = true;
|
||||
router.replace({
|
||||
@@ -105,7 +108,8 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const currentChat = history.find((item) => item.chatId === chatId);
|
||||
// update chat
|
||||
const currentChat = histories.find((item) => item.chatId === chatId);
|
||||
currentChat &&
|
||||
updateHistory({
|
||||
...currentChat,
|
||||
@@ -117,30 +121,12 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
setChatData((state) => ({
|
||||
...state,
|
||||
title: newTitle,
|
||||
history: ChatBoxRef.current?.getChatHistory() || state.history
|
||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||
}));
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
},
|
||||
[appId, chatId, history, router, setChatData, updateHistory]
|
||||
);
|
||||
|
||||
// del one chat content
|
||||
const delOneHistoryItem = useCallback(
|
||||
async ({ contentId, index }: { contentId?: string; index: number }) => {
|
||||
if (!chatId || !contentId) return;
|
||||
|
||||
try {
|
||||
setChatData((state) => ({
|
||||
...state,
|
||||
history: state.history.filter((_, i) => i !== index)
|
||||
}));
|
||||
await delChatRecordById({ chatId, contentId });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
},
|
||||
[chatId, setChatData]
|
||||
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
||||
);
|
||||
|
||||
// get chat app info
|
||||
@@ -156,10 +142,10 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
}) => {
|
||||
try {
|
||||
loading && setIsLoading(true);
|
||||
const res = await getInitChatSiteInfo({ appId, chatId });
|
||||
const res = await getInitChatInfo({ appId, chatId });
|
||||
const history = res.history.map((item) => ({
|
||||
...item,
|
||||
status: 'finish' as any
|
||||
status: ChatStatusEnum.finish
|
||||
}));
|
||||
|
||||
setChatData({
|
||||
@@ -185,8 +171,13 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
});
|
||||
if (e?.code === 501) {
|
||||
router.replace('/app/list');
|
||||
} else {
|
||||
router.replace('/chat');
|
||||
} else if (chatId) {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
setIsLoading(false);
|
||||
@@ -250,7 +241,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
});
|
||||
});
|
||||
|
||||
useQuery(['loadHistory', appId], () => (appId ? loadHistory({ appId }) : null));
|
||||
useQuery(['loadHistories', appId], () => (appId ? loadHistories({ appId }) : null));
|
||||
|
||||
return (
|
||||
<Flex h={'100%'}>
|
||||
@@ -289,7 +280,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
appAvatar={chatData.app.avatar}
|
||||
activeChatId={chatId}
|
||||
onClose={onCloseSlider}
|
||||
history={history.map((item, i) => ({
|
||||
history={histories.map((item, i) => ({
|
||||
id: item.chatId,
|
||||
title: item.title,
|
||||
customTitle: item.customTitle,
|
||||
@@ -306,39 +297,24 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
onCloseSlider();
|
||||
}
|
||||
}}
|
||||
onDelHistory={delHistory}
|
||||
onDelHistory={delOneHistory}
|
||||
onClearHistory={() => {
|
||||
clearHistory(appId);
|
||||
clearHistories({ appId });
|
||||
router.replace({
|
||||
query: {
|
||||
appId
|
||||
}
|
||||
});
|
||||
}}
|
||||
onSetHistoryTop={async (e) => {
|
||||
try {
|
||||
await putChatHistory(e);
|
||||
const historyItem = history.find((item) => item.chatId === e.chatId);
|
||||
if (!historyItem) return;
|
||||
updateHistory({
|
||||
...historyItem,
|
||||
top: e.top
|
||||
});
|
||||
} catch (error) {}
|
||||
onSetHistoryTop={(e) => {
|
||||
updateHistory(e);
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
try {
|
||||
putChatHistory({
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title
|
||||
});
|
||||
const historyItem = history.find((item) => item.chatId === e.chatId);
|
||||
if (!historyItem) return;
|
||||
updateHistory({
|
||||
...historyItem,
|
||||
customTitle: e.title
|
||||
});
|
||||
} catch (error) {}
|
||||
updateHistory({
|
||||
chatId: e.chatId,
|
||||
title: e.title,
|
||||
customTitle: e.title
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -372,7 +348,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
feedbackType={'user'}
|
||||
onUpdateVariable={(e) => {}}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={delOneHistoryItem}
|
||||
onDelMessage={(e) => delOneHistoryItem({ ...e, chatId })}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
@@ -1,17 +1,16 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { initShareChatInfo } from '@/web/support/outLink/api';
|
||||
import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
import { useShareChatStore, defaultHistory } from '@/web/core/chat/storeShareChat';
|
||||
import { useShareChatStore } from '@/web/core/chat/storeShareChat';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import { gptMessage2ChatType } from '@/utils/adapt';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import type { ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ChatHistoryItemType, ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||
|
||||
@@ -21,6 +20,13 @@ import ChatHeader from './components/ChatHeader';
|
||||
import ChatHistorySlider from './components/ChatHistorySlider';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||
import { POST } from '@/web/common/api/request';
|
||||
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||
|
||||
const OutLink = ({
|
||||
shareId,
|
||||
@@ -33,6 +39,7 @@ const OutLink = ({
|
||||
showHistory: '0' | '1';
|
||||
authToken?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||
@@ -43,18 +50,23 @@ const OutLink = ({
|
||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||
|
||||
const {
|
||||
shareChatData,
|
||||
setShareChatData,
|
||||
shareChatHistory,
|
||||
saveChatResponse,
|
||||
delShareChatHistoryItemById,
|
||||
delOneShareHistoryByChatId,
|
||||
delManyShareChatHistoryByShareId
|
||||
localUId,
|
||||
shareChatHistory, // abandon
|
||||
clearLocalHistory // abandon
|
||||
} = useShareChatStore();
|
||||
const history = useMemo(
|
||||
() => shareChatHistory.filter((item) => item.shareId === shareId),
|
||||
[shareChatHistory, shareId]
|
||||
);
|
||||
const {
|
||||
histories,
|
||||
loadHistories,
|
||||
pushHistory,
|
||||
updateHistory,
|
||||
delOneHistory,
|
||||
chatData,
|
||||
setChatData,
|
||||
delOneHistoryItem,
|
||||
clearHistories
|
||||
} = useChatStore();
|
||||
const appId = chatData.appId;
|
||||
const outLinkUid: string = authToken || localUId;
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||
@@ -67,12 +79,55 @@ const OutLink = ({
|
||||
variables,
|
||||
shareId,
|
||||
chatId: completionChatId,
|
||||
authToken
|
||||
outLinkUid
|
||||
},
|
||||
onMessage: generatingMessage,
|
||||
abortSignal: controller
|
||||
});
|
||||
|
||||
const newTitle =
|
||||
chatContentReplaceBlock(prompts[0].content).slice(0, 20) ||
|
||||
prompts[1]?.value?.slice(0, 20) ||
|
||||
'新对话';
|
||||
|
||||
// new chat
|
||||
if (completionChatId !== chatId) {
|
||||
const newHistory: ChatHistoryItemType = {
|
||||
chatId: completionChatId,
|
||||
updateTime: new Date(),
|
||||
title: newTitle,
|
||||
appId,
|
||||
top: false
|
||||
};
|
||||
pushHistory(newHistory);
|
||||
if (controller.signal.reason !== 'leave') {
|
||||
forbidRefresh.current = true;
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: completionChatId
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// update chat
|
||||
const currentChat = histories.find((item) => item.chatId === chatId);
|
||||
currentChat &&
|
||||
updateHistory({
|
||||
...currentChat,
|
||||
updateTime: new Date(),
|
||||
title: newTitle
|
||||
});
|
||||
}
|
||||
|
||||
// update chat window
|
||||
setChatData((state) => ({
|
||||
...state,
|
||||
title: newTitle,
|
||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||
}));
|
||||
|
||||
/* post message to report result */
|
||||
const result: ChatSiteItemType[] = gptMessage2ChatType(prompts).map((item) => ({
|
||||
...item,
|
||||
status: 'finish'
|
||||
@@ -80,24 +135,6 @@ const OutLink = ({
|
||||
result[1].value = responseText;
|
||||
result[1].responseData = responseData;
|
||||
|
||||
/* save chat */
|
||||
saveChatResponse({
|
||||
chatId: completionChatId,
|
||||
prompts: result,
|
||||
variables,
|
||||
shareId
|
||||
});
|
||||
|
||||
if (completionChatId !== chatId && controller.signal.reason !== 'leave') {
|
||||
forbidRefresh.current = true;
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: completionChatId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.top?.postMessage(
|
||||
{
|
||||
type: 'shareChatFinish',
|
||||
@@ -111,61 +148,80 @@ const OutLink = ({
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
},
|
||||
[authToken, chatId, router, saveChatResponse, shareId]
|
||||
[chatId, shareId, outLinkUid, setChatData, appId, updateHistory, router, histories]
|
||||
);
|
||||
|
||||
const loadAppInfo = useCallback(
|
||||
async (shareId: string, chatId: string, authToken?: string) => {
|
||||
const loadChatInfo = useCallback(
|
||||
async (shareId: string, chatId: string) => {
|
||||
if (!shareId) return null;
|
||||
const history = shareChatHistory.find((item) => item.chatId === chatId) || defaultHistory;
|
||||
|
||||
ChatBoxRef.current?.resetHistory(history.chats);
|
||||
ChatBoxRef.current?.resetVariables(history.variables);
|
||||
|
||||
try {
|
||||
const chatData = await (async () => {
|
||||
if (shareChatData.app.name === '') {
|
||||
return initShareChatInfo({
|
||||
shareId,
|
||||
authToken
|
||||
});
|
||||
}
|
||||
return shareChatData;
|
||||
})();
|
||||
const res = await getInitOutLinkChatInfo({
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid: authToken || localUId
|
||||
});
|
||||
const history = res.history.map((item) => ({
|
||||
...item,
|
||||
status: ChatStatusEnum.finish
|
||||
}));
|
||||
|
||||
setShareChatData({
|
||||
...chatData,
|
||||
setChatData({
|
||||
...res,
|
||||
history
|
||||
});
|
||||
|
||||
ChatBoxRef.current?.resetHistory(history);
|
||||
ChatBoxRef.current?.resetVariables(res.variables);
|
||||
|
||||
if (res.history.length > 0) {
|
||||
setTimeout(() => {
|
||||
ChatBoxRef.current?.scrollToBottom('auto');
|
||||
}, 500);
|
||||
}
|
||||
} catch (e: any) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: getErrText(e, '获取应用失败')
|
||||
title: getErrText(e, t('core.shareChat.Init Error'))
|
||||
});
|
||||
if (e?.code === 501) {
|
||||
delManyShareChatHistoryByShareId(shareId);
|
||||
if (chatId) {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
if (e?.statusText === OutLinkErrEnum.linkUnInvalid) {
|
||||
router.replace('/');
|
||||
}
|
||||
}
|
||||
|
||||
if (history.chats.length > 0) {
|
||||
setTimeout(() => {
|
||||
ChatBoxRef.current?.scrollToBottom('auto');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
return history;
|
||||
return null;
|
||||
},
|
||||
[delManyShareChatHistoryByShareId, setShareChatData, shareChatData, shareChatHistory, toast]
|
||||
[authToken, localUId, router, setChatData, t, toast]
|
||||
);
|
||||
|
||||
useQuery(['init', shareId, chatId, authToken], () => {
|
||||
useQuery(['init', shareId, chatId], () => {
|
||||
if (forbidRefresh.current) {
|
||||
forbidRefresh.current = false;
|
||||
return null;
|
||||
}
|
||||
return loadAppInfo(shareId, chatId, authToken);
|
||||
|
||||
return loadChatInfo(shareId, chatId);
|
||||
});
|
||||
|
||||
// load histories
|
||||
useQuery(['loadHistories', outLinkUid, shareId], () => {
|
||||
if (shareId && outLinkUid) {
|
||||
return loadHistories({
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// check is embed
|
||||
useEffect(() => {
|
||||
if (window !== top) {
|
||||
window.top?.postMessage({ type: 'shareChatReady' }, '*');
|
||||
@@ -173,10 +229,32 @@ const OutLink = ({
|
||||
setIdEmbed(window !== top);
|
||||
}, []);
|
||||
|
||||
// todo:4.6.4 init: update local chat history, add outLinkUid
|
||||
useEffect(() => {
|
||||
const activeHistory = shareChatHistory.filter((item) => !item.delete);
|
||||
if (!localUId || !shareId || activeHistory.length === 0) return;
|
||||
(async () => {
|
||||
try {
|
||||
await POST('/core/chat/initLocalShareHistoryV464', {
|
||||
shareId,
|
||||
outLinkUid: localUId,
|
||||
chatIds: shareChatHistory.map((item) => item.chatId)
|
||||
});
|
||||
clearLocalHistory();
|
||||
// router.reload();
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error, t('core.shareChat.Init Error'))
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [clearLocalHistory, localUId, router, shareChatHistory, shareId, t, toast]);
|
||||
|
||||
return (
|
||||
<PageContainer {...(isEmbed ? { p: '0 !important', borderRadius: '0' } : {})}>
|
||||
<Head>
|
||||
<title>{shareChatData.app.name}</title>
|
||||
<title>{chatData.app.name}</title>
|
||||
</Head>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
||||
{showHistory === '1'
|
||||
@@ -199,12 +277,14 @@ const OutLink = ({
|
||||
);
|
||||
})(
|
||||
<ChatHistorySlider
|
||||
appName={shareChatData.app.name}
|
||||
appAvatar={shareChatData.app.avatar}
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
activeChatId={chatId}
|
||||
history={history.map((item) => ({
|
||||
history={histories.map((item) => ({
|
||||
id: item.chatId,
|
||||
title: item.title
|
||||
title: item.title,
|
||||
customTitle: item.customTitle,
|
||||
top: item.top
|
||||
}))}
|
||||
onClose={onCloseSlider}
|
||||
onChangeChat={(chatId) => {
|
||||
@@ -218,9 +298,9 @@ const OutLink = ({
|
||||
onCloseSlider();
|
||||
}
|
||||
}}
|
||||
onDelHistory={delOneShareHistoryByChatId}
|
||||
onDelHistory={({ chatId }) => delOneHistory({ chatId, shareId, outLinkUid })}
|
||||
onClearHistory={() => {
|
||||
delManyShareChatHistoryByShareId(shareId);
|
||||
clearHistories({ shareId, outLinkUid });
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
@@ -228,6 +308,16 @@ const OutLink = ({
|
||||
}
|
||||
});
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
updateHistory(e);
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
updateHistory({
|
||||
chatId: e.chatId,
|
||||
title: e.title,
|
||||
customTitle: e.title
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
: null}
|
||||
@@ -242,36 +332,24 @@ const OutLink = ({
|
||||
>
|
||||
{/* header */}
|
||||
<ChatHeader
|
||||
appAvatar={shareChatData.app.avatar}
|
||||
appName={shareChatData.app.name}
|
||||
history={shareChatData.history.chats}
|
||||
appAvatar={chatData.app.avatar}
|
||||
appName={chatData.app.name}
|
||||
history={chatData.history}
|
||||
onOpenSlider={onOpenSlider}
|
||||
/>
|
||||
{/* chat box */}
|
||||
<Box flex={1}>
|
||||
<ChatBox
|
||||
active={!!shareChatData.app.name}
|
||||
active={!!chatData.app.name}
|
||||
ref={ChatBoxRef}
|
||||
appAvatar={shareChatData.app.avatar}
|
||||
userAvatar={shareChatData.userAvatar}
|
||||
userGuideModule={shareChatData.app?.userGuideModule}
|
||||
showFileSelector={checkChatSupportSelectFileByChatModels(
|
||||
shareChatData.app.chatModels
|
||||
)}
|
||||
appAvatar={chatData.app.avatar}
|
||||
userAvatar={chatData.userAvatar}
|
||||
userGuideModule={chatData.app?.userGuideModule}
|
||||
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
||||
feedbackType={'user'}
|
||||
onUpdateVariable={(e) => {
|
||||
setShareChatData((state) => ({
|
||||
...state,
|
||||
history: {
|
||||
...state.history,
|
||||
variables: e
|
||||
}
|
||||
}));
|
||||
}}
|
||||
onUpdateVariable={(e) => {}}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={({ contentId, index }) =>
|
||||
delShareChatHistoryItemById({ chatId, contentId, index })
|
||||
}
|
||||
onDelMessage={(e) => delOneHistoryItem({ ...e, chatId })}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
@@ -189,15 +189,6 @@ export async function searchDatasetData(props: SearchProps) {
|
||||
})
|
||||
).filter((item) => item.score > similarity);
|
||||
|
||||
// (It's possible that rerank failed) concat rerank results and search results
|
||||
set = new Set<string>(reRankResults.map((item) => item.id));
|
||||
embeddingRecallResults.forEach((item) => {
|
||||
if (!set.has(item.id) && item.score >= similarity) {
|
||||
reRankResults.push(item);
|
||||
set.add(item.id);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
searchRes: reRankResults.slice(0, limit),
|
||||
tokenLen
|
||||
@@ -382,7 +373,7 @@ export async function reRankSearchResult({
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
return [];
|
||||
return data;
|
||||
}
|
||||
}
|
||||
// ------------------ search end ------------------
|
||||
|
@@ -3,7 +3,7 @@ import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { RunningModuleItemType } from '@/types/app';
|
||||
import { ModuleDispatchProps } from '@/types/core/chat/type';
|
||||
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import { UserType } from '@fastgpt/global/support/user/type';
|
||||
|
@@ -48,7 +48,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
answerText,
|
||||
responseData: {
|
||||
moduleLogo: plugin.avatar,
|
||||
price: responseData.reduce((sum, item) => sum + item.price, 0),
|
||||
price: responseData.reduce((sum, item) => sum + (item.price || 0), 0),
|
||||
runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0),
|
||||
pluginOutput: output?.pluginOutput
|
||||
},
|
||||
|
@@ -1,14 +0,0 @@
|
||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import type {
|
||||
AuthLinkLimitProps,
|
||||
AuthShareChatInitProps
|
||||
} from '@fastgpt/global/support/outLink/api.d';
|
||||
|
||||
export function authOutLinkLimit(data: AuthLinkLimitProps) {
|
||||
return POST('/support/outLink/authLimit', data);
|
||||
}
|
||||
|
||||
export function authShareChatInit(data: AuthShareChatInitProps) {
|
||||
if (!global.feConfigs?.isPlus) return;
|
||||
return POST('/support/outLink/authShareChatInit', data);
|
||||
}
|
59
projects/app/src/service/support/permission/auth/chat.ts
Normal file
59
projects/app/src/service/support/permission/auth/chat.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ChatSchema } from '@fastgpt/global/core/chat/type';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { AuthModeType } from '@fastgpt/service/support/permission/type';
|
||||
import { authOutLink } from './outLink';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
/*
|
||||
outLink: Must be the owner
|
||||
token: team owner and chat owner have all permissions
|
||||
*/
|
||||
export async function autChatCrud({
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
chatId?: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
}): Promise<{
|
||||
chat?: ChatSchema;
|
||||
isOutLink: boolean;
|
||||
uid?: string;
|
||||
}> {
|
||||
const isOutLink = Boolean(shareId && outLinkUid);
|
||||
if (!chatId) return { isOutLink, uid: outLinkUid };
|
||||
|
||||
const chat = await MongoChat.findOne({ chatId }).lean();
|
||||
|
||||
if (!chat) return { isOutLink, uid: outLinkUid };
|
||||
|
||||
const { uid } = await (async () => {
|
||||
// outLink Auth
|
||||
if (shareId && outLinkUid) {
|
||||
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||
|
||||
// auth outLinkUid
|
||||
if (chat.shareId === shareId && chat.outLinkUid === uid) {
|
||||
return { uid };
|
||||
}
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
// req auth
|
||||
const { tmbId, role } = await authUserRole(props);
|
||||
if (role === TeamMemberRoleEnum.owner) return { uid: outLinkUid };
|
||||
if (String(tmbId) === String(chat.tmbId)) return { uid: outLinkUid };
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
})();
|
||||
|
||||
return {
|
||||
chat,
|
||||
isOutLink,
|
||||
uid
|
||||
};
|
||||
}
|
@@ -1,31 +1,74 @@
|
||||
import { authOutLinkLimit } from '@/service/support/outLink/auth';
|
||||
import { AuthLinkChatProps } from '@fastgpt/global/support/outLink/api.d';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { getUserAndAuthBalance } from './user';
|
||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import type {
|
||||
AuthOutLinkChatProps,
|
||||
AuthOutLinkLimitProps,
|
||||
AuthOutLinkInitProps,
|
||||
AuthOutLinkResponse
|
||||
} from '@fastgpt/global/support/outLink/api.d';
|
||||
import { authOutLinkValid } from '@fastgpt/service/support/permission/auth/outLink';
|
||||
import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||
|
||||
export async function authOutLinkChat({
|
||||
export function authOutLinkInit(data: AuthOutLinkInitProps): Promise<AuthOutLinkResponse> {
|
||||
if (!global.feConfigs?.isPlus) return Promise.resolve({ uid: data.outLinkUid });
|
||||
return POST<AuthOutLinkResponse>('/support/outLink/authInit', data);
|
||||
}
|
||||
export function authOutLinkChatLimit(data: AuthOutLinkLimitProps): Promise<AuthOutLinkResponse> {
|
||||
if (!global.feConfigs?.isPlus) return Promise.resolve({ uid: data.outLinkUid });
|
||||
return POST<AuthOutLinkResponse>('/support/outLink/authChatStart', data);
|
||||
}
|
||||
|
||||
export const authOutLink = async ({
|
||||
shareId,
|
||||
outLinkUid
|
||||
}: {
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
}): Promise<{
|
||||
uid: string;
|
||||
appId: string;
|
||||
shareChat: OutLinkSchema;
|
||||
}> => {
|
||||
if (!outLinkUid) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
const result = await authOutLinkValid({ shareId });
|
||||
|
||||
const { uid } = await authOutLinkInit({
|
||||
outLinkUid,
|
||||
tokenUrl: result.shareChat.limit?.hookUrl
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
uid
|
||||
};
|
||||
};
|
||||
|
||||
export async function authOutLinkChatStart({
|
||||
shareId,
|
||||
ip,
|
||||
authToken,
|
||||
outLinkUid,
|
||||
question
|
||||
}: AuthLinkChatProps & {
|
||||
}: AuthOutLinkChatProps & {
|
||||
shareId: string;
|
||||
}) {
|
||||
// get outLink
|
||||
const { shareChat, app } = await authOutLinkValid({ shareId });
|
||||
// get outLink and app
|
||||
const { shareChat, appId } = await authOutLinkValid({ shareId });
|
||||
|
||||
const [user] = await Promise.all([
|
||||
// check balance and chat limit
|
||||
const [user, { uid }] = await Promise.all([
|
||||
getUserAndAuthBalance({ tmbId: shareChat.tmbId, minBalance: 0 }),
|
||||
global.feConfigs?.isPlus
|
||||
? authOutLinkLimit({ outLink: shareChat, ip, authToken, question })
|
||||
: undefined
|
||||
authOutLinkChatLimit({ outLink: shareChat, ip, outLinkUid, question })
|
||||
]);
|
||||
|
||||
return {
|
||||
authType: AuthUserTypeEnum.token,
|
||||
responseDetail: shareChat.responseDetail,
|
||||
user,
|
||||
app
|
||||
appId,
|
||||
uid
|
||||
};
|
||||
}
|
||||
|
@@ -1,46 +0,0 @@
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { parseHeaderCert } from '@fastgpt/service/support/permission/controller';
|
||||
import { AuthModeType } from '@fastgpt/service/support/permission/type';
|
||||
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
|
||||
import { UserType } from '@fastgpt/global/support/user/type';
|
||||
import { getUserDetail } from '@/service/support/user/controller';
|
||||
|
||||
export async function getUserAndAuthBalance({
|
||||
tmbId,
|
||||
minBalance
|
||||
}: {
|
||||
tmbId: string;
|
||||
minBalance?: number;
|
||||
}) {
|
||||
const user = await getUserDetail({ tmbId });
|
||||
|
||||
if (!user) {
|
||||
return Promise.reject(UserErrEnum.unAuthUser);
|
||||
}
|
||||
if (minBalance !== undefined && global.feConfigs.isPlus && user.team.balance < minBalance) {
|
||||
return Promise.reject(UserErrEnum.balanceNotEnough);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/* get user */
|
||||
export async function authUser({
|
||||
minBalance,
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
minBalance?: number;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
user: UserType;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
|
||||
return {
|
||||
...result,
|
||||
user: await getUserAndAuthBalance({ tmbId: result.tmbId, minBalance }),
|
||||
isOwner: true,
|
||||
canWrite: true
|
||||
};
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||
import { UserType } from '@fastgpt/global/support/user/type';
|
||||
import {
|
||||
getTeamInfoByTmbId,
|
||||
getUserDefaultTeam
|
||||
} from '@fastgpt/service/support/user/team/controller';
|
||||
|
||||
export async function getUserDetail({
|
||||
tmbId,
|
||||
userId
|
||||
}: {
|
||||
tmbId?: string;
|
||||
userId?: string;
|
||||
}): Promise<UserType> {
|
||||
const team = await (async () => {
|
||||
if (tmbId) {
|
||||
return getTeamInfoByTmbId({ tmbId });
|
||||
}
|
||||
if (userId) {
|
||||
return getUserDefaultTeam({ userId });
|
||||
}
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
})();
|
||||
const user = await MongoUser.findById(team.userId);
|
||||
|
||||
if (!user) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
return {
|
||||
_id: user._id,
|
||||
username: user.username,
|
||||
avatar: user.avatar,
|
||||
balance: user.balance,
|
||||
timezone: user.timezone,
|
||||
promotionRate: user.promotionRate,
|
||||
openaiAccount: user.openaiAccount,
|
||||
team
|
||||
};
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import { BillSourceEnum, PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { getAudioSpeechModel, getQAModel } from '@/service/core/ai/model';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
|
||||
import { addLog } from '@fastgpt/service/common/mongo/controller';
|
||||
import type { ConcatBillProps, CreateBillProps } from '@fastgpt/global/support/wallet/bill/api.d';
|
||||
@@ -41,7 +41,7 @@ export const pushChatBill = ({
|
||||
source: `${BillSourceEnum}`;
|
||||
response: ChatHistoryItemResType[];
|
||||
}) => {
|
||||
const total = response.reduce((sum, item) => sum + item.price, 0);
|
||||
const total = response.reduce((sum, item) => sum + (item.price || 0), 0);
|
||||
|
||||
createBill({
|
||||
teamId,
|
||||
|
@@ -15,6 +15,7 @@ type Props = {
|
||||
updateUseTime: boolean;
|
||||
source: `${ChatSourceEnum}`;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
content: [ChatItemType, ChatItemType];
|
||||
};
|
||||
|
||||
@@ -27,10 +28,11 @@ export async function saveChat({
|
||||
updateUseTime,
|
||||
source,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
content
|
||||
}: Props) {
|
||||
try {
|
||||
const chatHistory = await MongoChat.findOne(
|
||||
const chat = await MongoChat.findOne(
|
||||
{
|
||||
chatId,
|
||||
teamId,
|
||||
@@ -57,7 +59,7 @@ export async function saveChat({
|
||||
content[1]?.value?.slice(0, 20) ||
|
||||
'Chat';
|
||||
|
||||
if (chatHistory) {
|
||||
if (chat) {
|
||||
promise.push(
|
||||
MongoChat.updateOne(
|
||||
{ chatId },
|
||||
@@ -77,7 +79,8 @@ export async function saveChat({
|
||||
variables,
|
||||
title,
|
||||
source,
|
||||
shareId
|
||||
shareId,
|
||||
outLinkUid
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@@ -1,11 +1,16 @@
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
|
||||
export function selectShareResponse({ responseData }: { responseData: ChatHistoryItemResType[] }) {
|
||||
export function selectShareResponse({
|
||||
responseData = []
|
||||
}: {
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
}) {
|
||||
const filedList = [
|
||||
'moduleType',
|
||||
'moduleName',
|
||||
'moduleLogo',
|
||||
'runningTime',
|
||||
'historyPreview',
|
||||
'quoteList',
|
||||
'question'
|
||||
];
|
||||
@@ -17,6 +22,6 @@ export function selectShareResponse({ responseData }: { responseData: ChatHistor
|
||||
obj[key] = item[key];
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
return obj as ChatHistoryItemResType;
|
||||
});
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { parseStreamChunk, SSEParseData } from '@/utils/sse';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { StartChatFnProps } from '@/components/ChatBox';
|
||||
import { getToken } from '@/web/support/user/auth';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
|
@@ -346,7 +346,7 @@ export const appTemplates: (AppItemType & {
|
||||
{
|
||||
key: 'similarity',
|
||||
type: 'slider',
|
||||
label: '相似度',
|
||||
label: '相关度',
|
||||
value: 0.4,
|
||||
min: 0,
|
||||
max: 1,
|
||||
@@ -1398,7 +1398,7 @@ export const appTemplates: (AppItemType & {
|
||||
{
|
||||
key: 'similarity',
|
||||
type: 'slider',
|
||||
label: '相似度',
|
||||
label: '相关度',
|
||||
value: 0.76,
|
||||
min: 0,
|
||||
max: 1,
|
||||
|
@@ -1,42 +1,53 @@
|
||||
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { InitChatResponse } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { RequestPaging } from '@/types';
|
||||
import { UpdateHistoryProps } from '@fastgpt/global/core/chat/api.d';
|
||||
import type { AdminUpdateFeedbackParams } from '@fastgpt/global/core/chat/api.d';
|
||||
import { GetChatSpeechProps } from '@/global/core/chat/api.d';
|
||||
import type {
|
||||
InitChatProps,
|
||||
InitChatResponse,
|
||||
InitOutLinkChatProps,
|
||||
getHistoriesProps
|
||||
} from '@/global/core/chat/api.d';
|
||||
import type {
|
||||
AdminUpdateFeedbackParams,
|
||||
ClearHistoriesProps,
|
||||
DelHistoryProps,
|
||||
DeleteChatItemProps,
|
||||
UpdateHistoryProps
|
||||
} from '@/global/core/chat/api.d';
|
||||
|
||||
/**
|
||||
* 获取初始化聊天内容
|
||||
*/
|
||||
export const getInitChatSiteInfo = (data: { appId: string; chatId?: string }) =>
|
||||
export const getInitChatInfo = (data: InitChatProps) =>
|
||||
GET<InitChatResponse>(`/core/chat/init`, data);
|
||||
export const getInitOutLinkChatInfo = (data: InitOutLinkChatProps) =>
|
||||
GET<InitChatResponse>(`/core/chat/outLink/init`, data);
|
||||
|
||||
/**
|
||||
* 获取历史记录
|
||||
* get current window history(appid or shareId)
|
||||
*/
|
||||
export const getChatHistory = (data: RequestPaging & { appId: string }) =>
|
||||
POST<ChatHistoryItemType[]>('/core/chat/list', data);
|
||||
export const getChatHistories = (data: getHistoriesProps) =>
|
||||
POST<ChatHistoryItemType[]>('/core/chat/getHistories', data);
|
||||
|
||||
/**
|
||||
* 删除一条历史记录
|
||||
* delete one history
|
||||
*/
|
||||
export const delChatHistoryById = (chatId: string) => DELETE(`/core/chat/delete`, { chatId });
|
||||
export const delChatHistoryById = (data: DelHistoryProps) => DELETE(`/core/chat/delHistory`, data);
|
||||
/**
|
||||
* clear all history by appid
|
||||
*/
|
||||
export const clearChatHistoryByAppId = (appId: string) => DELETE(`/core/chat/delete`, { appId });
|
||||
export const clearChatHistoryByAppId = (data: ClearHistoriesProps) =>
|
||||
DELETE(`/core/chat/clearHistories`, data);
|
||||
|
||||
/**
|
||||
* 删除一句对话
|
||||
* delete one chat record
|
||||
*/
|
||||
export const delChatRecordById = (data: { chatId: string; contentId: string }) =>
|
||||
export const delChatRecordById = (data: DeleteChatItemProps) =>
|
||||
DELETE(`/core/chat/item/delete`, data);
|
||||
|
||||
/**
|
||||
* 修改历史记录: 标题/置顶
|
||||
*/
|
||||
export const putChatHistory = (data: UpdateHistoryProps) => PUT('/core/chat/update', data);
|
||||
export const putChatHistory = (data: UpdateHistoryProps) => PUT('/core/chat/updateHistory', data);
|
||||
|
||||
export const userUpdateChatFeedback = (data: { chatItemId: string; userFeedback?: string }) =>
|
||||
POST('/core/chat/feedback/userUpdate', data);
|
||||
|
@@ -2,35 +2,36 @@ import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { InitChatResponse } from '@fastgpt/global/core/chat/api';
|
||||
import { delChatHistoryById, getChatHistory, clearChatHistoryByAppId } from '@/web/core/chat/api';
|
||||
import type {
|
||||
InitChatResponse,
|
||||
getHistoriesProps,
|
||||
ClearHistoriesProps,
|
||||
DelHistoryProps,
|
||||
UpdateHistoryProps
|
||||
} from '@/global/core/chat/api';
|
||||
import {
|
||||
delChatHistoryById,
|
||||
getChatHistories,
|
||||
clearChatHistoryByAppId,
|
||||
delChatRecordById,
|
||||
putChatHistory
|
||||
} from '@/web/core/chat/api';
|
||||
import { defaultChatData } from '@/global/core/chat/constants';
|
||||
|
||||
type State = {
|
||||
history: ChatHistoryItemType[];
|
||||
loadHistory: (data: { appId: string }) => Promise<null>;
|
||||
delHistory(history: string): Promise<void>;
|
||||
clearHistory(appId: string): Promise<void>;
|
||||
updateHistory: (history: ChatHistoryItemType) => void;
|
||||
histories: ChatHistoryItemType[];
|
||||
loadHistories: (data: getHistoriesProps) => Promise<null>;
|
||||
delOneHistory(data: DelHistoryProps): Promise<void>;
|
||||
clearHistories(data: ClearHistoriesProps): Promise<void>;
|
||||
pushHistory: (history: ChatHistoryItemType) => void;
|
||||
updateHistory: (e: UpdateHistoryProps & { updateTime?: Date; title?: string }) => Promise<any>;
|
||||
chatData: InitChatResponse;
|
||||
setChatData: (e: InitChatResponse | ((e: InitChatResponse) => InitChatResponse)) => void;
|
||||
lastChatAppId: string;
|
||||
setLastChatAppId: (id: string) => void;
|
||||
lastChatId: string;
|
||||
setLastChatId: (id: string) => void;
|
||||
};
|
||||
|
||||
const defaultChatData: InitChatResponse = {
|
||||
chatId: '',
|
||||
appId: '',
|
||||
app: {
|
||||
name: 'Loading',
|
||||
avatar: '/icon/logo.svg',
|
||||
intro: '',
|
||||
canUse: false
|
||||
},
|
||||
title: '新对话',
|
||||
variables: {},
|
||||
history: []
|
||||
delOneHistoryItem: (e: { chatId: string; contentId?: string; index: number }) => Promise<any>;
|
||||
};
|
||||
|
||||
export const useChatStore = create<State>()(
|
||||
@@ -49,49 +50,62 @@ export const useChatStore = create<State>()(
|
||||
state.lastChatId = id;
|
||||
});
|
||||
},
|
||||
history: [],
|
||||
async loadHistory({ appId }) {
|
||||
const oneHistory = get().history[0];
|
||||
if (oneHistory && oneHistory.appId === appId) return null;
|
||||
const data = await getChatHistory({
|
||||
appId,
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
});
|
||||
histories: [],
|
||||
async loadHistories(e) {
|
||||
const data = await getChatHistories(e);
|
||||
set((state) => {
|
||||
state.history = data;
|
||||
state.histories = data;
|
||||
});
|
||||
return null;
|
||||
},
|
||||
async delHistory(chatId) {
|
||||
async delOneHistory(props) {
|
||||
set((state) => {
|
||||
state.history = state.history.filter((item) => item.chatId !== chatId);
|
||||
state.histories = state.histories.filter((item) => item.chatId !== props.chatId);
|
||||
});
|
||||
await delChatHistoryById(chatId);
|
||||
await delChatHistoryById(props);
|
||||
},
|
||||
async clearHistory(appId) {
|
||||
async clearHistories(data) {
|
||||
set((state) => {
|
||||
state.history = [];
|
||||
state.histories = [];
|
||||
});
|
||||
await clearChatHistoryByAppId(appId);
|
||||
await clearChatHistoryByAppId(data);
|
||||
},
|
||||
updateHistory(history) {
|
||||
const index = get().history.findIndex((item) => item.chatId === history.chatId);
|
||||
pushHistory(history) {
|
||||
set((state) => {
|
||||
const newHistory = (() => {
|
||||
if (index > -1) {
|
||||
return [
|
||||
history,
|
||||
...get().history.slice(0, index),
|
||||
...get().history.slice(index + 1)
|
||||
];
|
||||
} else {
|
||||
return [history, ...state.history];
|
||||
}
|
||||
})();
|
||||
state.histories = [history, ...state.histories];
|
||||
});
|
||||
},
|
||||
async updateHistory(props) {
|
||||
const { chatId, customTitle, top, title, updateTime } = props;
|
||||
const index = get().histories.findIndex((item) => item.chatId === chatId);
|
||||
|
||||
state.history = newHistory;
|
||||
});
|
||||
if (index > -1) {
|
||||
const newHistory = {
|
||||
...get().histories[index],
|
||||
...(title && { title }),
|
||||
...(updateTime && { updateTime }),
|
||||
...(customTitle !== undefined && { customTitle }),
|
||||
...(top !== undefined && { top })
|
||||
};
|
||||
|
||||
if (customTitle !== undefined || top !== undefined) {
|
||||
try {
|
||||
putChatHistory(props);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
const newHistories = (() => {
|
||||
return [
|
||||
newHistory,
|
||||
...get().histories.slice(0, index),
|
||||
...get().histories.slice(index + 1)
|
||||
];
|
||||
})();
|
||||
|
||||
state.histories = newHistories;
|
||||
});
|
||||
}
|
||||
},
|
||||
chatData: defaultChatData,
|
||||
setChatData(e = defaultChatData) {
|
||||
@@ -104,6 +118,19 @@ export const useChatStore = create<State>()(
|
||||
state.chatData = e;
|
||||
});
|
||||
}
|
||||
},
|
||||
async delOneHistoryItem({ chatId, contentId, index }) {
|
||||
if (!chatId || !contentId) return;
|
||||
|
||||
try {
|
||||
get().setChatData((state) => ({
|
||||
...state,
|
||||
history: state.history.filter((_, i) => i !== index)
|
||||
}));
|
||||
await delChatRecordById({ chatId, contentId });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
})),
|
||||
{
|
||||
|
@@ -1,142 +1,39 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import type {
|
||||
ShareChatHistoryItemType,
|
||||
ShareChatType
|
||||
} from '@fastgpt/global/support/outLink/api.d';
|
||||
import type { ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { HUMAN_ICON } from '@fastgpt/global/core/chat/constants';
|
||||
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet(
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890_',
|
||||
24
|
||||
);
|
||||
|
||||
type State = {
|
||||
shareChatData: ShareChatType;
|
||||
setShareChatData: (e: ShareChatType | ((e: ShareChatType) => ShareChatType)) => void;
|
||||
shareChatHistory: ShareChatHistoryItemType[];
|
||||
saveChatResponse: (e: {
|
||||
chatId: string;
|
||||
prompts: ChatSiteItemType[];
|
||||
variables: Record<string, any>;
|
||||
shareId: string;
|
||||
}) => void;
|
||||
delOneShareHistoryByChatId: (chatId: string) => void;
|
||||
delShareChatHistoryItemById: (e: { chatId: string; contentId?: string; index: number }) => void;
|
||||
delManyShareChatHistoryByShareId: (shareId?: string) => void;
|
||||
};
|
||||
|
||||
export const defaultHistory: ShareChatHistoryItemType = {
|
||||
chatId: `${Date.now()}`,
|
||||
updateTime: new Date(),
|
||||
title: '新对话',
|
||||
shareId: '',
|
||||
chats: []
|
||||
};
|
||||
const defaultShareChatData: ShareChatType = {
|
||||
userAvatar: HUMAN_ICON,
|
||||
app: {
|
||||
name: '',
|
||||
avatar: '/icon/logo.svg',
|
||||
intro: ''
|
||||
},
|
||||
history: defaultHistory
|
||||
localUId: string;
|
||||
shareChatHistory: (ChatHistoryItemType & { delete?: boolean })[];
|
||||
clearLocalHistory: (shareId?: string) => void;
|
||||
};
|
||||
|
||||
export const useShareChatStore = create<State>()(
|
||||
devtools(
|
||||
persist(
|
||||
immer((set, get) => ({
|
||||
shareChatData: defaultShareChatData,
|
||||
setShareChatData(e) {
|
||||
const val = (() => {
|
||||
if (typeof e === 'function') {
|
||||
return e(get().shareChatData);
|
||||
} else {
|
||||
return e;
|
||||
}
|
||||
})();
|
||||
localUId: `shareChat-${Date.now()}-${nanoid()}`,
|
||||
shareChatHistory: [], // old version field
|
||||
clearLocalHistory() {
|
||||
// abandon
|
||||
set((state) => {
|
||||
state.shareChatData = val;
|
||||
// update history
|
||||
state.shareChatHistory = state.shareChatHistory.map((item) =>
|
||||
item.chatId === val.history.chatId ? val.history : item
|
||||
);
|
||||
});
|
||||
},
|
||||
shareChatHistory: [],
|
||||
saveChatResponse({ chatId, prompts, variables, shareId }) {
|
||||
const chatHistory = get().shareChatHistory.find((item) => item.chatId === chatId);
|
||||
const newTitle =
|
||||
chatContentReplaceBlock(prompts[prompts.length - 2]?.value).slice(0, 20) ||
|
||||
prompts[prompts.length - 1]?.value?.slice(0, 20) ||
|
||||
'Chat';
|
||||
|
||||
const historyList = (() => {
|
||||
if (chatHistory) {
|
||||
return get().shareChatHistory.map((item) =>
|
||||
item.chatId === chatId
|
||||
? {
|
||||
...item,
|
||||
title: newTitle,
|
||||
updateTime: new Date(),
|
||||
chats: chatHistory.chats.concat(prompts).slice(-30),
|
||||
variables
|
||||
}
|
||||
: item
|
||||
);
|
||||
}
|
||||
return get().shareChatHistory.concat({
|
||||
chatId,
|
||||
shareId,
|
||||
title: newTitle,
|
||||
updateTime: new Date(),
|
||||
chats: prompts,
|
||||
variables
|
||||
});
|
||||
})();
|
||||
|
||||
// @ts-ignore
|
||||
historyList.sort((a, b) => new Date(b.updateTime) - new Date(a.updateTime));
|
||||
|
||||
set((state) => {
|
||||
state.shareChatHistory = historyList.slice(0, 50);
|
||||
});
|
||||
},
|
||||
delOneShareHistoryByChatId(chatId: string) {
|
||||
set((state) => {
|
||||
state.shareChatHistory = state.shareChatHistory.filter(
|
||||
(item) => item.chatId !== chatId
|
||||
);
|
||||
});
|
||||
},
|
||||
delShareChatHistoryItemById({ chatId, contentId }) {
|
||||
set((state) => {
|
||||
// update history store
|
||||
const newHistoryList = state.shareChatHistory.map((item) =>
|
||||
item.chatId === chatId
|
||||
? {
|
||||
...item,
|
||||
chats: item.chats.filter((item) => item.dataId !== contentId)
|
||||
}
|
||||
: item
|
||||
);
|
||||
state.shareChatHistory = newHistoryList;
|
||||
});
|
||||
},
|
||||
delManyShareChatHistoryByShareId(shareId?: string) {
|
||||
set((state) => {
|
||||
if (shareId) {
|
||||
state.shareChatHistory = state.shareChatHistory.filter(
|
||||
(item) => item.shareId !== shareId
|
||||
);
|
||||
} else {
|
||||
state.shareChatHistory = [];
|
||||
}
|
||||
state.shareChatHistory = state.shareChatHistory.map((item) => ({
|
||||
...item,
|
||||
delete: true
|
||||
}));
|
||||
});
|
||||
}
|
||||
})),
|
||||
{
|
||||
name: 'shareChatStore',
|
||||
partialize: (state) => ({
|
||||
localUId: state.localUId,
|
||||
shareChatHistory: state.shareChatHistory
|
||||
})
|
||||
}
|
||||
|
@@ -79,8 +79,7 @@ export const useDatasetStore = create<State>()(
|
||||
item._id === data.id
|
||||
? {
|
||||
...item,
|
||||
...data,
|
||||
tags: data.tags || []
|
||||
...data
|
||||
}
|
||||
: item
|
||||
);
|
||||
|
@@ -1,13 +1,6 @@
|
||||
import { GET, POST, DELETE } from '@/web/common/api/request';
|
||||
import type { InitShareChatResponse } from '@fastgpt/global/support/outLink/api.d';
|
||||
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';
|
||||
|
||||
/**
|
||||
* 初始化分享聊天
|
||||
*/
|
||||
export const initShareChatInfo = (data: { shareId: string; authToken?: string }) =>
|
||||
GET<InitShareChatResponse>(`/support/outLink/init`, data);
|
||||
|
||||
/**
|
||||
* create a shareChat
|
||||
*/
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import { GET } from '@/web/common/api/request';
|
||||
import type { PaySchema } from '@fastgpt/global/support/wallet/pay/type.d';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
export const getPayOrders = () => GET<PaySchema[]>(`/plusApi/support/wallet/pay/getPayOrders`);
|
||||
|
||||
export const getPayCode = (amount: number) =>
|
||||
@@ -11,15 +9,9 @@ export const getPayCode = (amount: number) =>
|
||||
}>(`/plusApi/support/wallet/pay/getPayCode`, { amount });
|
||||
|
||||
export const checkPayResult = (payId: string) =>
|
||||
GET<number>(`/plusApi/support/wallet/pay/checkPayResult`, { payId }).then(() => {
|
||||
async function startQueue() {
|
||||
try {
|
||||
await GET('/common/system/unlockTask');
|
||||
} catch (error) {
|
||||
await delay(1000);
|
||||
startQueue();
|
||||
}
|
||||
}
|
||||
startQueue();
|
||||
return 'success';
|
||||
GET<string>(`/plusApi/support/wallet/pay/checkPayResult`, { payId }).then((data) => {
|
||||
try {
|
||||
GET('/common/system/unlockTask');
|
||||
} catch (error) {}
|
||||
return data;
|
||||
});
|
||||
|
Reference in New Issue
Block a user