4.6.4-alpha (#569)

This commit is contained in:
Archer
2023-12-07 13:43:08 +08:00
committed by GitHub
parent 71afe71192
commit e01c38efe0
80 changed files with 1401 additions and 1109 deletions

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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>