diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx index 30486a75e..b94f6f838 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/Provider.tsx @@ -41,7 +41,6 @@ type useChatStoreType = ChatProviderProps & { ttsConfig: AppTTSConfigType; whisperConfig: AppWhisperConfigType; autoTTSResponse: boolean; - autoExecute: AppAutoExecuteConfigType; startSegmentedAudio: () => Promise; splitText2Audio: (text: string, done?: boolean | undefined) => void; finishSegmentedAudio: () => void; @@ -136,24 +135,38 @@ const Provider = ({ }: ChatProviderProps & { children: React.ReactNode; }) => { - const chatConfig = useContextSelector( + const welcomeText = useContextSelector( ChatItemContext, - (v) => v.chatBoxData?.app?.chatConfig || {} + (v) => v.chatBoxData?.app?.chatConfig?.welcomeText ?? '' ); + const variables = useContextSelector( + ChatItemContext, + (v) => v.chatBoxData?.app?.chatConfig?.variables ?? [] + ); + const questionGuide = useContextSelector( + ChatItemContext, + (v) => v.chatBoxData?.app?.chatConfig?.questionGuide ?? false + ); + const ttsConfig = useContextSelector( + ChatItemContext, + (v) => v.chatBoxData?.app?.chatConfig?.ttsConfig ?? defaultTTSConfig + ); + const whisperConfig = useContextSelector( + ChatItemContext, + (v) => v.chatBoxData?.app?.chatConfig?.whisperConfig ?? defaultWhisperConfig + ); + const chatInputGuide = useContextSelector( + ChatItemContext, + (v) => v.chatBoxData?.app?.chatConfig?.chatInputGuide ?? defaultChatInputGuideConfig + ); + const fileSelectConfig = useContextSelector( + ChatItemContext, + (v) => v.chatBoxData?.app?.chatConfig?.fileSelectConfig ?? defaultAppSelectFileConfig + ); + const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const setChatRecords = useContextSelector(ChatRecordContext, (v) => v.setChatRecords); - const { - welcomeText = '', - variables = [], - questionGuide = false, - ttsConfig = defaultTTSConfig, - whisperConfig = defaultWhisperConfig, - chatInputGuide = defaultChatInputGuideConfig, - fileSelectConfig = defaultAppSelectFileConfig, - autoExecute = defaultAutoExecuteConfig - } = useMemo(() => chatConfig, [chatConfig]); - // segment audio const [audioPlayingChatId, setAudioPlayingChatId] = useState(); const { @@ -202,7 +215,6 @@ const Provider = ({ const value: useChatStoreType = { ...props, welcomeText, - autoExecute, variableList: variables.filter((item) => item.type !== VariableInputEnum.custom), allVariableList: variables, questionGuide, diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx index 82390296f..beac6282b 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx @@ -83,7 +83,6 @@ enum FeedbackTypeEnum { type Props = OutLinkChatAuthProps & ChatProviderProps & { - isReady?: boolean; feedbackType?: `${FeedbackTypeEnum}`; showMarkIcon?: boolean; // admin mark dataset showVoiceIcon?: boolean; @@ -98,7 +97,6 @@ type Props = OutLinkChatAuthProps & }; const ChatBox = ({ - isReady = true, feedbackType = FeedbackTypeEnum.hidden, showMarkIcon = false, showVoiceIcon = true, @@ -132,6 +130,7 @@ const ChatBox = ({ const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app?.avatar); const userAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.userAvatar); + const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); const ChatBoxRef = useContextSelector(ChatItemContext, (v) => v.ChatBoxRef); const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); @@ -146,7 +145,6 @@ const ChatBox = ({ const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList); const allVariableList = useContextSelector(ChatBoxContext, (v) => v.allVariableList); const questionGuide = useContextSelector(ChatBoxContext, (v) => v.questionGuide); - const autoExecute = useContextSelector(ChatBoxContext, (v) => v.autoExecute); const startSegmentedAudio = useContextSelector(ChatBoxContext, (v) => v.startSegmentedAudio); const finishSegmentedAudio = useContextSelector(ChatBoxContext, (v) => v.finishSegmentedAudio); const setAudioPlayingChatId = useContextSelector(ChatBoxContext, (v) => v.setAudioPlayingChatId); @@ -166,7 +164,9 @@ const ChatBox = ({ }); const { setValue, watch } = chatForm; const chatStartedWatch = watch('chatStarted'); - const chatStarted = chatStartedWatch || chatRecords.length > 0 || variableList.length === 0; + const chatStarted = + chatBoxData?.appId === appId && + (chatStartedWatch || chatRecords.length > 0 || variableList.length === 0); // 滚动到底部 const scrollToBottom = useMemoizedFn((behavior: 'smooth' | 'auto' = 'smooth', delay = 0) => { @@ -802,9 +802,9 @@ const ChatBox = ({ setQuestionGuide([]); setValue('chatStarted', false); abortRequest('leave'); - }, [router.query, setValue, chatId]); + }, [abortRequest, setValue]); - // add listener + // Add listener useEffect(() => { const windowMessage = ({ data }: MessageEvent<{ type: 'sendPrompt'; text: string }>) => { if (data?.type === 'sendPrompt' && data?.text) { @@ -829,30 +829,27 @@ const ChatBox = ({ eventBus.off(EventNameEnum.sendQuestion); eventBus.off(EventNameEnum.editQuestion); }; - }, [isReady, resetInputVal, sendPrompt]); + }, [chatBoxData, resetInputVal, sendPrompt]); // Auto send prompt useEffect(() => { if ( - isReady && - autoExecute.open && + chatBoxData?.app?.chatConfig?.autoExecute?.open && chatStarted && chatRecords.length === 0 && isChatRecordsLoaded ) { sendPrompt({ - text: autoExecute.defaultPrompt || 'AUTO_EXECUTE', + text: chatBoxData?.app?.chatConfig?.autoExecute?.defaultPrompt || 'AUTO_EXECUTE', hideInUI: true }); } }, [ chatStarted, - chatRecords, + chatRecords.length, isChatRecordsLoaded, sendPrompt, - isReady, - autoExecute.open, - autoExecute.defaultPrompt + chatBoxData?.app?.chatConfig?.autoExecute ]); // output data diff --git a/projects/app/src/pages/chat/components/ChatHeader.tsx b/projects/app/src/pages/chat/components/ChatHeader.tsx index 78183ab0c..51e634240 100644 --- a/projects/app/src/pages/chat/components/ChatHeader.tsx +++ b/projects/app/src/pages/chat/components/ChatHeader.tsx @@ -1,5 +1,5 @@ import React, { useState, useCallback } from 'react'; -import { Flex, useTheme, Box, useDisclosure } from '@chakra-ui/react'; +import { Flex, Box, useDisclosure } from '@chakra-ui/react'; import MyIcon from '@fastgpt/web/components/common/Icon'; import Avatar from '@fastgpt/web/components/common/Avatar'; import ToolMenu from './ToolMenu'; @@ -10,7 +10,6 @@ import MyTag from '@fastgpt/web/components/common/Tag/index'; import { useContextSelector } from 'use-context-selector'; import { ChatContext } from '@/web/core/chat/context/chatContext'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; -import { InitChatResponse } from '@/global/core/chat/api'; import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; @@ -22,9 +21,9 @@ import { } from '@fastgpt/global/common/parentFolder/type'; import { getMyApps } from '@/web/core/app/api'; import SelectOneResource from '@/components/common/folder/SelectOneResource'; +import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; const ChatHeader = ({ - chatData, history, showHistory, apps, @@ -33,15 +32,16 @@ const ChatHeader = ({ }: { history: ChatItemType[]; showHistory?: boolean; - chatData: InitChatResponse; apps?: AppListItemType[]; onRouteToAppDetail?: () => void; totalRecordsCount: number; }) => { const { t } = useTranslation(); - const isPlugin = chatData.app.type === AppTypeEnum.plugin; const { isPc } = useSystem(); + const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); + const isPlugin = chatData.app.type === AppTypeEnum.plugin; + return isPc && isPlugin ? null : ( import('./components/CustomPluginRunBox')); @@ -56,13 +55,13 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables); const isPlugin = useContextSelector(ChatItemContext, (v) => v.isPlugin); + const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount); // Load chat init data - const [chatData, setChatData] = useState(defaultChatData); const { loading } = useRequest2( async () => { if (!appId || forbidLoadChat.current) return; @@ -71,11 +70,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { res.userAvatar = userInfo?.avatar; // Wait for state update to complete - await new Promise((resolve) => { - setChatData(res); - setChatBoxData(res); - setTimeout(resolve, 0); - }); + setChatBoxData(res); // reset chat variables resetVariables({ @@ -132,14 +127,14 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { // new chat onUpdateHistoryTitle({ chatId, newTitle }); // update chat window - setChatData((state) => ({ + setChatBoxData((state) => ({ ...state, title: newTitle })); return { responseText, responseData, isNewChat: forbidLoadChat.current }; }, - [chatId, appId, onUpdateHistoryTitle, forbidLoadChat] + [appId, chatId, onUpdateHistoryTitle, setChatBoxData, forbidLoadChat] ); const RenderHistorySlider = useMemo(() => { const Children = ( @@ -164,7 +159,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { return ( - + {/* pc show myself apps */} {isPc && ( @@ -188,7 +183,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { router.push(`/app/detail?appId=${appId}`)} @@ -213,7 +207,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { feedbackType={'user'} onStartChat={onStartChat} chatType={'chat'} - isReady={!loading} showRawSource showNodeStatus /> diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx index 8420cb43c..f2dbd5b73 100644 --- a/projects/app/src/pages/chat/share.tsx +++ b/projects/app/src/pages/chat/share.tsx @@ -87,7 +87,6 @@ const OutLink = (props: Props) => { const isChatRecordsLoaded = useContextSelector(ChatRecordContext, (v) => v.isChatRecordsLoaded); const initSign = useRef(false); - const [chatData, setChatData] = useState(defaultChatData); const { data, loading } = useRequest2( async () => { const shareId = outLinkAuthData.shareId; @@ -100,11 +99,7 @@ const OutLink = (props: Props) => { outLinkUid }); - await new Promise((resolve) => { - setChatData(res); - setChatBoxData(res); - setTimeout(resolve, 0); - }); + setChatBoxData(res); resetVariables({ variables: res.variables @@ -180,7 +175,7 @@ const OutLink = (props: Props) => { onUpdateHistoryTitle({ chatId: completionChatId, newTitle }); // update chat window - setChatData((state) => ({ + setChatBoxData((state) => ({ ...state, title: newTitle })); @@ -199,7 +194,15 @@ const OutLink = (props: Props) => { return { responseText, responseData, isNewChat: forbidLoadChat.current }; }, - [chatId, customVariables, outLinkAuthData, onUpdateHistoryTitle, forbidLoadChat, onChangeChatId] + [ + chatId, + customVariables, + outLinkAuthData, + onUpdateHistoryTitle, + setChatBoxData, + forbidLoadChat, + onChangeChatId + ] ); // window init @@ -258,7 +261,6 @@ const OutLink = (props: Props) => { {/* header */} {showHead === '1' ? ( { feedbackType={'user'} onStartChat={startChat} chatType="share" - isReady={!loading} showRawSource={showRawSource} showNodeStatus={showNodeStatus} /> diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx index 0c5f6958b..e71b9f1ed 100644 --- a/projects/app/src/pages/chat/team.tsx +++ b/projects/app/src/pages/chat/team.tsx @@ -62,24 +62,21 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { const onUpdateHistoryTitle = useContextSelector(ChatContext, (v) => v.onUpdateHistoryTitle); const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables); + const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); const setChatBoxData = useContextSelector(ChatItemContext, (v) => v.setChatBoxData); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount); // get chat app info - const [chatData, setChatData] = useState(defaultChatData); const { loading } = useRequest2( async () => { if (!appId || forbidLoadChat.current) return; const res = await getTeamChatInfo({ teamId, appId, chatId, teamToken }); - await new Promise((resolve) => { - setChatData(res); - setChatBoxData(res); - setTimeout(resolve, 0); - }); + setChatBoxData(res); + // reset chat records resetVariables({ variables: res.variables @@ -124,7 +121,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { teamId, teamToken, chatId: completionChatId, - appType: chatData.app.type + appType: chatBoxData.app.type }, onMessage: generatingMessage, abortCtrl: controller @@ -139,7 +136,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { onUpdateHistoryTitle({ chatId: completionChatId, newTitle }); // update chat window - setChatData((state) => ({ + setChatBoxData((state) => ({ ...state, title: newTitle })); @@ -152,8 +149,9 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { appId, teamId, teamToken, - chatData.app.type, + chatBoxData.app.type, onUpdateHistoryTitle, + setChatBoxData, forbidLoadChat, onChangeChatId ] @@ -182,7 +180,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { return ( - + {/* pc show myself apps */} {isPc && ( @@ -205,13 +203,12 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { {/* chat box */} - {chatData.app.type === AppTypeEnum.plugin ? ( + {chatBoxData.app.type === AppTypeEnum.plugin ? ( { feedbackType={'user'} onStartChat={startChat} chatType="team" - isReady={!loading} showRawSource showNodeStatus /> diff --git a/projects/app/src/web/core/chat/context/chatItemContext.tsx b/projects/app/src/web/core/chat/context/chatItemContext.tsx index caae074fe..94eb77fef 100644 --- a/projects/app/src/web/core/chat/context/chatItemContext.tsx +++ b/projects/app/src/web/core/chat/context/chatItemContext.tsx @@ -10,13 +10,17 @@ import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; type ChatBoxDataType = { + appId: string; + title?: string; userAvatar?: string; + app: { chatConfig?: AppChatConfigType; name: string; avatar: string; type: `${AppTypeEnum}`; pluginInputs: FlowNodeInputItemType[]; + chatModels?: string[]; }; }; @@ -55,7 +59,9 @@ const ChatItemContextProvider = ({ children }: { children: ReactNode }) => { const ChatBoxRef = useRef(null); const variablesForm = useForm(); - const [chatBoxData, setChatBoxData] = useState(defaultChatData); + const [chatBoxData, setChatBoxData] = useState({ + ...defaultChatData + }); const isPlugin = chatBoxData.app.type === AppTypeEnum.plugin; diff --git a/projects/app/src/web/core/chat/context/chatRecordContext.tsx b/projects/app/src/web/core/chat/context/chatRecordContext.tsx index 0e1a05a17..16adcd193 100644 --- a/projects/app/src/web/core/chat/context/chatRecordContext.tsx +++ b/projects/app/src/web/core/chat/context/chatRecordContext.tsx @@ -56,10 +56,6 @@ const ChatRecordContextProvider = ({ const ChatBoxRef = useContextSelector(ChatItemContext, (v) => v.ChatBoxRef); const [isChatRecordsLoaded, setIsChatRecordsLoaded] = useState(false); - useEffect(() => { - setIsChatRecordsLoaded(false); - }, [params]); - const { data: chatRecords, ScrollData, @@ -67,6 +63,8 @@ const ChatRecordContextProvider = ({ total: totalRecordsCount } = useScrollPagination( async (data: getPaginationRecordsBody): Promise> => { + setIsChatRecordsLoaded(false); + const res = await getChatRecords(data); // First load scroll to bottom