diff --git a/web/src/app/app/(chat_experience)/chat/[chatId]/reasoning/[messageId]/page.tsx b/web/src/app/app/(chat_experience)/chat/[chatId]/reasoning/[messageId]/page.tsx index b5bfdb305..cf6b24b21 100644 --- a/web/src/app/app/(chat_experience)/chat/[chatId]/reasoning/[messageId]/page.tsx +++ b/web/src/app/app/(chat_experience)/chat/[chatId]/reasoning/[messageId]/page.tsx @@ -5,9 +5,5 @@ export default function Page({ }: { params: { chatId: string; messageId: string }; }) { - return ( - <> - - - ); + return ; } diff --git a/web/src/app/app/_components/Text/VersionPill.tsx b/web/src/app/app/_components/Text/VersionPill.tsx index a8e8cdf99..f183bfc5a 100644 --- a/web/src/app/app/_components/Text/VersionPill.tsx +++ b/web/src/app/app/_components/Text/VersionPill.tsx @@ -1,5 +1,5 @@ import { createStyles } from 'antd-style'; -import { Text } from '@/components'; +import { Text } from '@/components/text'; import React from 'react'; export const VersionPill: React.FC<{ version_number: number }> = React.memo( diff --git a/web/src/app/app/_controllers/ReasoningController/ReasoningController.tsx b/web/src/app/app/_controllers/ReasoningController/ReasoningController.tsx index 62b83468b..9314e6962 100644 --- a/web/src/app/app/_controllers/ReasoningController/ReasoningController.tsx +++ b/web/src/app/app/_controllers/ReasoningController/ReasoningController.tsx @@ -1,5 +1,5 @@ 'use client'; -import React, { useMemo } from 'react'; +import React from 'react'; import { useChatIndividualContextSelector } from '../../_layouts/ChatLayout/ChatContext'; import { ReasoningMessageContainer } from './ReasoningMessageContainer'; import { useBusterChatContextSelector } from '@/context/Chats'; diff --git a/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileButtons.tsx b/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileButtons.tsx index 1b3acef77..9b59c4be7 100644 --- a/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileButtons.tsx +++ b/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileButtons.tsx @@ -1,6 +1,7 @@ import type { FileType } from '@/api/asset_interfaces'; import { createChatAssetRoute } from '@appLayouts/ChatLayout/ChatLayoutContext/helpers'; -import { AppMaterialIcons, AppTooltip } from '@/components'; +import { AppTooltip } from '@/components/tooltip'; +import { AppMaterialIcons } from '@/components/icons'; import { Button } from 'antd'; import React from 'react'; import { useChatLayoutContextSelector } from '@/app/app/_layouts/ChatLayout'; diff --git a/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileTitle.tsx b/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileTitle.tsx index 688ad4459..934a3c394 100644 --- a/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileTitle.tsx +++ b/web/src/app/app/_controllers/ReasoningController/ReasoningMessages/ReasoningMessage_File/ReasoningFileTitle.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Text } from '@/components'; -import { VersionPill } from '@/app/app/_components/Text/VersionPill'; +import { Text } from '@/components/text'; +import { VersionPill } from '@appComponents/Text/VersionPill'; export const ReasoningFileTitle = React.memo( ({ file_name, version_number }: { file_name: string; version_number: number }) => { diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatContent/ChatInput/ChatInput.tsx b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatContent/ChatInput/ChatInput.tsx index b691b9623..fa301a3f7 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatContent/ChatInput/ChatInput.tsx +++ b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatContent/ChatInput/ChatInput.tsx @@ -13,14 +13,11 @@ const autoSize = { minRows: 3, maxRows: 16 }; export const ChatInput: React.FC<{}> = React.memo(({}) => { const { styles, cx } = useStyles(); - const isNewChat = useChatIndividualContextSelector((x) => x.isNewChat); - const isFollowUpChat = useChatIndividualContextSelector((x) => x.isFollowUpChat); const inputRef = useRef(null); + const loading = useChatIndividualContextSelector((x) => x.isLoading); const [inputValue, setInputValue] = useState(''); - const [isFocused, setIsFocused] = React.useState(false); - - const loading = isNewChat || isFollowUpChat; + const [isFocused, setIsFocused] = useState(false); const disableSendButton = useMemo(() => { return !inputHasText(inputValue); diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx b/web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx index f9a7a85d2..8d060083b 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx +++ b/web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx @@ -7,6 +7,7 @@ import { import type { SelectedFile } from '../interfaces'; import { useSubscribeIndividualChat } from './useSubscribeIndividualChat'; import { useAutoChangeLayout } from './useAutoChangeLayout'; +import { useBusterChatContextSelector } from '@/context/Chats'; export const useChatIndividualContext = ({ chatId, @@ -25,6 +26,7 @@ export const useChatIndividualContext = ({ chatId, defaultSelectedFile }); + const hasChat = !!chatId && !!chat; const chatTitle = chat?.title; const chatMessageIds = chat?.messages ?? []; @@ -34,10 +36,11 @@ export const useChatIndividualContext = ({ //MESSAGES const currentMessageId = chatMessageIds[chatMessageIds.length - 1]; - const isNewChat = chat?.isNewChat ?? false; - const isFollowUpChat = chat?.isFollowupMessage ?? false; + const isLoading = useBusterChatContextSelector( + (x) => x.chatsMessages[currentMessageId]?.isCompletedStream + ); - useAutoChangeLayout({ lastMessageId: currentMessageId, onSetSelectedFile, chat }); + useAutoChangeLayout({ lastMessageId: currentMessageId, onSetSelectedFile }); return { hasChat, @@ -48,8 +51,7 @@ export const useChatIndividualContext = ({ selectedFileType, chatMessageIds, chatId, - isNewChat, - isFollowUpChat + isLoading }; }; diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContext/useAutoChangeLayout.ts b/web/src/app/app/_layouts/ChatLayout/ChatContext/useAutoChangeLayout.ts index 66a4a7b84..58f3abd47 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatContext/useAutoChangeLayout.ts +++ b/web/src/app/app/_layouts/ChatLayout/ChatContext/useAutoChangeLayout.ts @@ -1,24 +1,20 @@ -import { IBusterChat, useBusterChatContextSelector } from '@/context/Chats'; -import { SelectedFile } from '../interfaces'; +import { useBusterChatContextSelector } from '@/context/Chats'; +import type { SelectedFile } from '../interfaces'; import { usePrevious } from 'ahooks'; import { useEffect } from 'react'; export const useAutoChangeLayout = ({ lastMessageId, - chat, onSetSelectedFile }: { lastMessageId: string; - chat: IBusterChat | undefined; onSetSelectedFile: (file: SelectedFile) => void; }) => { const message = useBusterChatContextSelector((x) => x.chatsMessages[lastMessageId]); const reasoningMessagesLength = message?.reasoning?.length; const previousReasoningMessagesLength = usePrevious(reasoningMessagesLength); const isCompletedStream = message?.isCompletedStream; - const isFollowupMessage = chat?.isFollowupMessage; - const isNewChat = chat?.isNewChat; - const isLoading = isNewChat || isFollowupMessage || !isCompletedStream; + const isLoading = !isCompletedStream; const hasReasoning = !!reasoningMessagesLength; const previousIsEmpty = previousReasoningMessagesLength === 0; diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContext/useFileFallback.ts b/web/src/app/app/_layouts/ChatLayout/ChatContext/useFileFallback.ts index ca0ce12a7..ee2ef7141 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatContext/useFileFallback.ts +++ b/web/src/app/app/_layouts/ChatLayout/ChatContext/useFileFallback.ts @@ -74,8 +74,6 @@ export const useFileFallback = ({ const fallbackToFileChat = ({ id }: { id: string }): IBusterChat => { return { id, - isNewChat: false, - isFollowupMessage: false, messages: [fallbackMessageId(id)], title: '', is_favorited: false, diff --git a/web/src/context/Chats/ChatProvider/ChatProvider.tsx b/web/src/context/Chats/ChatProvider/ChatProvider.tsx index 21757ed09..779f9ce89 100644 --- a/web/src/context/Chats/ChatProvider/ChatProvider.tsx +++ b/web/src/context/Chats/ChatProvider/ChatProvider.tsx @@ -5,7 +5,7 @@ import { useContextSelector } from '@fluentui/react-context-selector'; import type { BusterChat } from '@/api/asset_interfaces'; -import { IBusterChat, IBusterChatMessage } from '../interfaces'; +import type { IBusterChat, IBusterChatMessage } from '../interfaces'; import { useChatSubscriptions } from './useChatSubscriptions'; import { useChatAssosciations } from './useChatAssosciations'; import { useChatSelectors } from './useChatSelectors'; diff --git a/web/src/context/Chats/ChatProvider/helpers/index.ts b/web/src/context/Chats/ChatProvider/helpers/index.ts index 8ae1712e8..1cfc1663b 100644 --- a/web/src/context/Chats/ChatProvider/helpers/index.ts +++ b/web/src/context/Chats/ChatProvider/helpers/index.ts @@ -1 +1 @@ -export * from './chatUpgrader'; +export * from './useFileFallback'; diff --git a/web/src/context/Chats/ChatProvider/helpers/prepareChatForUpdate.ts b/web/src/context/Chats/ChatProvider/helpers/prepareChatForUpdate.ts new file mode 100644 index 000000000..e69de29bb diff --git a/web/src/context/Chats/ChatProvider/helpers/useFileFallback.ts b/web/src/context/Chats/ChatProvider/helpers/useFileFallback.ts index 44549ec5a..8824e672c 100644 --- a/web/src/context/Chats/ChatProvider/helpers/useFileFallback.ts +++ b/web/src/context/Chats/ChatProvider/helpers/useFileFallback.ts @@ -61,8 +61,6 @@ export const useFileFallback = ({ const fallbackToFileChat = ({ id }: { id: string }): IBusterChat => { return { id, - isNewChat: true, - isFollowupMessage: false, messages: [fallbackMessageId(id)], title: '', is_favorited: false, diff --git a/web/src/context/Chats/ChatProvider/useChatSelectors.ts b/web/src/context/Chats/ChatProvider/useChatSelectors.ts index 03120e7dc..0c18512c7 100644 --- a/web/src/context/Chats/ChatProvider/useChatSelectors.ts +++ b/web/src/context/Chats/ChatProvider/useChatSelectors.ts @@ -1,5 +1,5 @@ -import { MutableRefObject, useCallback } from 'react'; -import { IBusterChat, IBusterChatMessage } from '../interfaces'; +import { type MutableRefObject, useCallback } from 'react'; +import type { IBusterChat, IBusterChatMessage } from '../interfaces'; import { useMemoizedFn } from 'ahooks'; export const useChatSelectors = ({ diff --git a/web/src/context/Chats/ChatProvider/useChatSubscriptions.ts b/web/src/context/Chats/ChatProvider/useChatSubscriptions.ts index 06307bee8..99a0fe741 100644 --- a/web/src/context/Chats/ChatProvider/useChatSubscriptions.ts +++ b/web/src/context/Chats/ChatProvider/useChatSubscriptions.ts @@ -2,8 +2,8 @@ import { MutableRefObject } from 'react'; import { useBusterWebSocket } from '../../BusterWebSocket'; import { useMemoizedFn } from 'ahooks'; import type { BusterChat } from '@/api/asset_interfaces'; -import { IBusterChat, IBusterChatMessage } from '../interfaces'; -import { chatMessageUpgrader, chatUpgrader } from './helpers'; +import type { IBusterChat, IBusterChatMessage } from '../interfaces'; +import { updateChatToIChat } from '@/utils/chat'; import { MOCK_CHAT } from './MOCK_CHAT'; export const useChatSubscriptions = ({ @@ -18,17 +18,17 @@ export const useChatSubscriptions = ({ const busterSocket = useBusterWebSocket(); const _onGetChat = useMemoizedFn((chat: BusterChat): IBusterChat => { - const upgradedChat = chatUpgrader(chat); - const upgradedChatMessages = chatMessageUpgrader(chat.messages); - chatsRef.current[chat.id] = upgradedChat; + const { iChat, iChatMessages } = updateChatToIChat(chat); + + chatsRef.current[chat.id] = iChat; chatsMessagesRef.current = { ...chatsMessagesRef.current, - ...upgradedChatMessages + ...iChatMessages }; startTransition(() => { //just used to trigger UI update }); - return upgradedChat; + return iChat; }); const unsubscribeFromChat = useMemoizedFn(({ chatId }: { chatId: string }) => { diff --git a/web/src/context/Chats/ChatProvider/useChatUpdate.ts b/web/src/context/Chats/ChatProvider/useChatUpdate.ts index 90608860e..62579f7b6 100644 --- a/web/src/context/Chats/ChatProvider/useChatUpdate.ts +++ b/web/src/context/Chats/ChatProvider/useChatUpdate.ts @@ -1,7 +1,7 @@ import { useBusterWebSocket } from '@/context/BusterWebSocket'; import { useMemoizedFn } from 'ahooks'; -import { MutableRefObject } from 'react'; -import { IBusterChat, IBusterChatMessage } from '../interfaces'; +import { type MutableRefObject } from 'react'; +import type { IBusterChat, IBusterChatMessage } from '../interfaces'; export const useChatUpdate = ({ chatsRef, @@ -15,13 +15,25 @@ export const useChatUpdate = ({ const busterSocket = useBusterWebSocket(); const onUpdateChat = useMemoizedFn( - async (newChatConfig: Partial & { id: string }) => { + async (newChatConfig: Partial & { id: string }, saveToServer: boolean = false) => { chatsRef.current[newChatConfig.id] = { ...chatsRef.current[newChatConfig.id], ...newChatConfig }; startTransition(() => { //just used to trigger UI update + + if (saveToServer) { + const { title, is_favorited, id } = chatsRef.current[newChatConfig.id]; + busterSocket.emit({ + route: '/chats/update', + payload: { + id, + title, + is_favorited + } + }); + } }); } ); @@ -38,8 +50,21 @@ export const useChatUpdate = ({ } ); + const onBulkSetChatMessages = useMemoizedFn( + (newMessagesConfig: Record) => { + chatsMessagesRef.current = { + ...chatsMessagesRef.current, + ...newMessagesConfig + }; + startTransition(() => { + //just used to trigger UI update + }); + } + ); + return { onUpdateChat, - onUpdateChatMessage + onUpdateChatMessage, + onBulkSetChatMessages }; }; diff --git a/web/src/context/Chats/NewChatProvider/NewChatProvider.tsx b/web/src/context/Chats/NewChatProvider/NewChatProvider.tsx index 89bf8a014..54f1c30b9 100644 --- a/web/src/context/Chats/NewChatProvider/NewChatProvider.tsx +++ b/web/src/context/Chats/NewChatProvider/NewChatProvider.tsx @@ -5,7 +5,7 @@ import { useContextSelector } from '@fluentui/react-context-selector'; import { useMemoizedFn } from 'ahooks'; -import type { BusterDatasetListItem, BusterSearchResult, FileType } from '@/api/asset_interfaces'; +import type { BusterSearchResult, FileType } from '@/api/asset_interfaces'; import { useBusterWebSocket } from '@/context/BusterWebSocket'; import { useChatUpdateMessage } from './useChatUpdateMessage'; diff --git a/web/src/context/Chats/NewChatProvider/useChatUpdateMessage.ts b/web/src/context/Chats/NewChatProvider/useChatUpdateMessage.ts index 5c2de4998..f06440dbf 100644 --- a/web/src/context/Chats/NewChatProvider/useChatUpdateMessage.ts +++ b/web/src/context/Chats/NewChatProvider/useChatUpdateMessage.ts @@ -7,6 +7,7 @@ import { ChatEvent_GeneratingResponseMessage, ChatEvent_GeneratingTitle } from '@/api/buster_socket/chats'; +import { updateChatToIChat } from '@/utils/chat'; export const useChatUpdateMessage = () => { const busterSocket = useBusterWebSocket(); @@ -14,6 +15,7 @@ export const useChatUpdateMessage = () => { const getChatMemoized = useBusterChatContextSelector((x) => x.getChatMemoized); const onUpdateChatMessage = useBusterChatContextSelector((x) => x.onUpdateChatMessage); const getChatMessageMemoized = useBusterChatContextSelector((x) => x.getChatMessageMemoized); + const onBulkSetChatMessages = useBusterChatContextSelector((x) => x.onBulkSetChatMessages); const _generatingTitleCallback = useMemoizedFn((d: ChatEvent_GeneratingTitle) => { const { chat_id, title, title_chunk } = d; @@ -57,10 +59,9 @@ export const useChatUpdateMessage = () => { ); const completeChatCallback = useMemoizedFn((d: BusterChat) => { - onUpdateChatMessage({ - ...d, - isCompletedStream: true - }); + const { iChat, iChatMessages } = updateChatToIChat(d); + onBulkSetChatMessages(iChatMessages); + onUpdateChat(iChat); }); const stopChatCallback = useMemoizedFn((chatId: string) => { diff --git a/web/src/context/Chats/interfaces.ts b/web/src/context/Chats/interfaces.ts index 1b4adb72c..c5212e937 100644 --- a/web/src/context/Chats/interfaces.ts +++ b/web/src/context/Chats/interfaces.ts @@ -1,8 +1,6 @@ import type { BusterChat, BusterChatMessage } from '@/api/asset_interfaces'; export interface IBusterChat extends Omit { - isNewChat: boolean; - isFollowupMessage: boolean; messages: string[]; } diff --git a/web/src/context/Chats/ChatProvider/helpers/chatUpgrader.ts b/web/src/utils/chat.ts similarity index 69% rename from web/src/context/Chats/ChatProvider/helpers/chatUpgrader.ts rename to web/src/utils/chat.ts index c52e0fe59..c93af729e 100644 --- a/web/src/context/Chats/ChatProvider/helpers/chatUpgrader.ts +++ b/web/src/utils/chat.ts @@ -1,20 +1,14 @@ import type { BusterChat, BusterChatMessage } from '@/api/asset_interfaces'; -import type { IBusterChat, IBusterChatMessage } from '../../interfaces'; +import type { IBusterChat, IBusterChatMessage } from '@/context/Chats/interfaces'; -export const chatUpgrader = ( - chat: BusterChat, - options?: { isNewChat?: boolean; isFollowupMessage?: boolean } -): IBusterChat => { - const { isNewChat = false, isFollowupMessage = false } = options || {}; +const chatUpgrader = (chat: BusterChat): IBusterChat => { return { ...chat, - isNewChat, - isFollowupMessage, messages: chat.messages.map((message) => message.id) }; }; -export const chatMessageUpgrader = ( +const chatMessageUpgrader = ( message: BusterChatMessage[], options?: { isCompletedStream: boolean; messageId: string } ): Record => { @@ -40,3 +34,12 @@ export const chatMessageUpgrader = ( {} as Record ); }; + +export const updateChatToIChat = (chat: BusterChat) => { + const iChat = chatUpgrader(chat); + const iChatMessages = chatMessageUpgrader(chat.messages); + return { + iChat, + iChatMessages + }; +};