From c30afb8b68cbf68379190fb0a726fd50f2bcd688 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 7 Jan 2026 20:37:35 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=8F=20chore:=20Remove=20Resumable=20St?= =?UTF-8?q?ream=20Toggle=20(#11258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚏 chore: Remove Resumable Stream Toggle - Removed the `useResumableStreamToggle` hook and its associated logic from the ChatView component. - Updated Conversations and useAdaptiveSSE hooks to determine resumable stream status based on the endpoint type. - Cleaned up settings by removing the `resumableStreams` state from the store and its related localization strings. * 🔧 refactor: Simplify Active Jobs Logic in Conversations Component - Removed the endpoint type checks and associated logic for resumable streams in the Conversations component. - Updated the `useActiveJobs` hook call to no longer depend on resumable stream status, streamlining the data fetching process. --- client/src/components/Chat/ChatView.tsx | 13 +----- .../Conversations/Conversations.tsx | 3 +- .../components/Nav/SettingsTabs/Chat/Chat.tsx | 39 ++++++++---------- client/src/hooks/SSE/index.ts | 1 - client/src/hooks/SSE/useAdaptiveSSE.ts | 13 +++--- .../src/hooks/SSE/useResumableStreamToggle.ts | 41 ------------------- client/src/hooks/SSE/useResumeOnLoad.ts | 8 +++- client/src/locales/en/translation.json | 2 - client/src/store/settings.ts | 1 - 9 files changed, 32 insertions(+), 89 deletions(-) delete mode 100644 client/src/hooks/SSE/useResumableStreamToggle.ts diff --git a/client/src/components/Chat/ChatView.tsx b/client/src/components/Chat/ChatView.tsx index d2e107ae19..66dec68f64 100644 --- a/client/src/components/Chat/ChatView.tsx +++ b/client/src/components/Chat/ChatView.tsx @@ -7,13 +7,7 @@ import { Constants, buildTree } from 'librechat-data-provider'; import type { TMessage } from 'librechat-data-provider'; import type { ChatFormValues } from '~/common'; import { ChatContext, AddedChatContext, useFileMapContext, ChatFormProvider } from '~/Providers'; -import { - useResumableStreamToggle, - useAddedResponse, - useResumeOnLoad, - useAdaptiveSSE, - useChatHelpers, -} from '~/hooks'; +import { useAddedResponse, useResumeOnLoad, useAdaptiveSSE, useChatHelpers } from '~/hooks'; import ConversationStarters from './Input/ConversationStarters'; import { useGetMessagesByConvoId } from '~/data-provider'; import MessagesView from './Messages/MessagesView'; @@ -56,11 +50,6 @@ function ChatView({ index = 0 }: { index?: number }) { const chatHelpers = useChatHelpers(index, conversationId); const addedChatHelpers = useAddedResponse(); - useResumableStreamToggle( - chatHelpers.conversation?.endpoint, - chatHelpers.conversation?.endpointType, - ); - useAdaptiveSSE(rootSubmission, chatHelpers, false, index); // Auto-resume if navigating back to conversation with active job diff --git a/client/src/components/Conversations/Conversations.tsx b/client/src/components/Conversations/Conversations.tsx index f0b05a5a00..b972d251b0 100644 --- a/client/src/components/Conversations/Conversations.tsx +++ b/client/src/components/Conversations/Conversations.tsx @@ -160,14 +160,13 @@ const Conversations: FC = ({ }) => { const localize = useLocalize(); const search = useRecoilValue(store.search); - const resumableEnabled = useRecoilValue(store.resumableStreams); const { favorites, isLoading: isFavoritesLoading } = useFavorites(); const isSmallScreen = useMediaQuery('(max-width: 768px)'); const convoHeight = isSmallScreen ? 44 : 34; const showAgentMarketplace = useShowMarketplace(); // Fetch active job IDs for showing generation indicators - const { data: activeJobsData } = useActiveJobs(resumableEnabled); + const { data: activeJobsData } = useActiveJobs(); const activeJobIds = useMemo( () => new Set(activeJobsData?.activeJobIds ?? []), [activeJobsData?.activeJobIds], diff --git a/client/src/components/Nav/SettingsTabs/Chat/Chat.tsx b/client/src/components/Nav/SettingsTabs/Chat/Chat.tsx index bfedd22c74..5cbbd73619 100644 --- a/client/src/components/Nav/SettingsTabs/Chat/Chat.tsx +++ b/client/src/components/Nav/SettingsTabs/Chat/Chat.tsx @@ -9,88 +9,81 @@ import store from '~/store'; const toggleSwitchConfigs = [ { stateAtom: store.enterToSend, - localizationKey: 'com_nav_enter_to_send', + localizationKey: 'com_nav_enter_to_send' as const, switchId: 'enterToSend', - hoverCardText: 'com_nav_info_enter_to_send', + hoverCardText: 'com_nav_info_enter_to_send' as const, key: 'enterToSend', }, { stateAtom: store.maximizeChatSpace, - localizationKey: 'com_nav_maximize_chat_space', + localizationKey: 'com_nav_maximize_chat_space' as const, switchId: 'maximizeChatSpace', hoverCardText: undefined, key: 'maximizeChatSpace', }, { stateAtom: store.centerFormOnLanding, - localizationKey: 'com_nav_center_chat_input', + localizationKey: 'com_nav_center_chat_input' as const, switchId: 'centerFormOnLanding', hoverCardText: undefined, key: 'centerFormOnLanding', }, { stateAtom: showThinkingAtom, - localizationKey: 'com_nav_show_thinking', + localizationKey: 'com_nav_show_thinking' as const, switchId: 'showThinking', hoverCardText: undefined, key: 'showThinking', }, { stateAtom: store.showCode, - localizationKey: 'com_nav_show_code', + localizationKey: 'com_nav_show_code' as const, switchId: 'showCode', hoverCardText: undefined, key: 'showCode', }, { stateAtom: store.LaTeXParsing, - localizationKey: 'com_nav_latex_parsing', + localizationKey: 'com_nav_latex_parsing' as const, switchId: 'latexParsing', - hoverCardText: 'com_nav_info_latex_parsing', + hoverCardText: 'com_nav_info_latex_parsing' as const, key: 'latexParsing', }, { stateAtom: store.saveDrafts, - localizationKey: 'com_nav_save_drafts', + localizationKey: 'com_nav_save_drafts' as const, switchId: 'saveDrafts', - hoverCardText: 'com_nav_info_save_draft', + hoverCardText: 'com_nav_info_save_draft' as const, key: 'saveDrafts', }, { stateAtom: store.showScrollButton, - localizationKey: 'com_nav_scroll_button', + localizationKey: 'com_nav_scroll_button' as const, switchId: 'showScrollButton', hoverCardText: undefined, key: 'showScrollButton', }, { stateAtom: store.saveBadgesState, - localizationKey: 'com_nav_save_badges_state', + localizationKey: 'com_nav_save_badges_state' as const, switchId: 'showBadges', - hoverCardText: 'com_nav_info_save_badges_state', + hoverCardText: 'com_nav_info_save_badges_state' as const, key: 'showBadges', }, { stateAtom: store.modularChat, - localizationKey: 'com_nav_modular_chat', + localizationKey: 'com_nav_modular_chat' as const, switchId: 'modularChat', hoverCardText: undefined, key: 'modularChat', }, { stateAtom: store.defaultTemporaryChat, - localizationKey: 'com_nav_default_temporary_chat', + localizationKey: 'com_nav_default_temporary_chat' as const, switchId: 'defaultTemporaryChat', - hoverCardText: 'com_nav_info_default_temporary_chat', + hoverCardText: 'com_nav_info_default_temporary_chat' as const, key: 'defaultTemporaryChat', }, - { - stateAtom: store.resumableStreams, - localizationKey: 'com_nav_resumable_streams', - switchId: 'resumableStreams', - hoverCardText: 'com_nav_info_resumable_streams', - key: 'resumableStreams', - }, ]; function Chat() { diff --git a/client/src/hooks/SSE/index.ts b/client/src/hooks/SSE/index.ts index 800de1e2a7..2829db76f6 100644 --- a/client/src/hooks/SSE/index.ts +++ b/client/src/hooks/SSE/index.ts @@ -5,4 +5,3 @@ export { default as useResumeOnLoad } from './useResumeOnLoad'; export { default as useStepHandler } from './useStepHandler'; export { default as useContentHandler } from './useContentHandler'; export { default as useAttachmentHandler } from './useAttachmentHandler'; -export { default as useResumableStreamToggle } from './useResumableStreamToggle'; diff --git a/client/src/hooks/SSE/useAdaptiveSSE.ts b/client/src/hooks/SSE/useAdaptiveSSE.ts index b196e4ef0c..e8c2de08e0 100644 --- a/client/src/hooks/SSE/useAdaptiveSSE.ts +++ b/client/src/hooks/SSE/useAdaptiveSSE.ts @@ -1,9 +1,8 @@ -import { useRecoilValue } from 'recoil'; +import { isAssistantsEndpoint } from 'librechat-data-provider'; import type { TSubmission } from 'librechat-data-provider'; import type { EventHandlerParams } from './useEventHandlers'; -import useSSE from './useSSE'; import useResumableSSE from './useResumableSSE'; -import store from '~/store'; +import useSSE from './useSSE'; type ChatHelpers = Pick< EventHandlerParams, @@ -17,7 +16,7 @@ type ChatHelpers = Pick< /** * Adaptive SSE hook that switches between standard and resumable modes. - * Uses Recoil state to determine which mode to use. + * Uses resumable streams by default, falls back to standard SSE for assistants endpoints. * * Note: Both hooks are always called to comply with React's Rules of Hooks. * We pass null submission to the inactive one. @@ -28,7 +27,11 @@ export default function useAdaptiveSSE( isAddedRequest = false, runIndex = 0, ) { - const resumableEnabled = useRecoilValue(store.resumableStreams); + const endpoint = submission?.conversation?.endpoint; + const endpointType = submission?.conversation?.endpointType; + const actualEndpoint = endpointType ?? endpoint; + const isAssistants = isAssistantsEndpoint(actualEndpoint); + const resumableEnabled = !isAssistants; useSSE(resumableEnabled ? null : submission, chatHelpers, isAddedRequest, runIndex); diff --git a/client/src/hooks/SSE/useResumableStreamToggle.ts b/client/src/hooks/SSE/useResumableStreamToggle.ts deleted file mode 100644 index f14fde044a..0000000000 --- a/client/src/hooks/SSE/useResumableStreamToggle.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useEffect, useRef } from 'react'; -import { useRecoilState } from 'recoil'; -import { isAssistantsEndpoint } from 'librechat-data-provider'; -import type { EModelEndpoint } from 'librechat-data-provider'; -import store from '~/store'; - -/** - * Automatically toggles resumable streams off for assistants endpoints - * and restores the previous value when switching away. - * - * Assistants endpoints have their own streaming mechanism and don't support resumable streams. - */ -export default function useResumableStreamToggle( - endpoint: EModelEndpoint | string | null | undefined, - endpointType?: EModelEndpoint | string | null, -) { - const [resumableStreams, setResumableStreams] = useRecoilState(store.resumableStreams); - const savedValueRef = useRef(null); - const wasAssistantsRef = useRef(false); - - useEffect(() => { - const actualEndpoint = endpointType ?? endpoint; - const isAssistants = isAssistantsEndpoint(actualEndpoint); - - if (isAssistants && !wasAssistantsRef.current) { - // Switching TO assistants: save current value and disable - savedValueRef.current = resumableStreams; - if (resumableStreams) { - setResumableStreams(false); - } - wasAssistantsRef.current = true; - } else if (!isAssistants && wasAssistantsRef.current) { - // Switching AWAY from assistants: restore saved value - if (savedValueRef.current !== null) { - setResumableStreams(savedValueRef.current); - savedValueRef.current = null; - } - wasAssistantsRef.current = false; - } - }, [endpoint, endpointType, resumableStreams, setResumableStreams]); -} diff --git a/client/src/hooks/SSE/useResumeOnLoad.ts b/client/src/hooks/SSE/useResumeOnLoad.ts index 5a674cec75..f09751db0e 100644 --- a/client/src/hooks/SSE/useResumeOnLoad.ts +++ b/client/src/hooks/SSE/useResumeOnLoad.ts @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react'; import { useSetRecoilState, useRecoilValue } from 'recoil'; -import { Constants, tMessageSchema } from 'librechat-data-provider'; +import { Constants, tMessageSchema, isAssistantsEndpoint } from 'librechat-data-provider'; import type { TMessage, TConversation, TSubmission, Agents } from 'librechat-data-provider'; import { useStreamStatus } from '~/data-provider'; import store from '~/store'; @@ -102,9 +102,13 @@ export default function useResumeOnLoad( runIndex = 0, messagesLoaded = true, ) { - const resumableEnabled = useRecoilValue(store.resumableStreams); const setSubmission = useSetRecoilState(store.submissionByIndex(runIndex)); const currentSubmission = useRecoilValue(store.submissionByIndex(runIndex)); + const currentConversation = useRecoilValue(store.conversationByIndex(runIndex)); + const endpoint = currentConversation?.endpoint; + const endpointType = currentConversation?.endpointType; + const actualEndpoint = endpointType ?? endpoint; + const resumableEnabled = !isAssistantsEndpoint(actualEndpoint); // Track conversations we've already processed (either resumed or skipped) const processedConvoRef = useRef(null); diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index a1b55cf451..c144e8bda5 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -485,7 +485,6 @@ "com_nav_info_fork_split_target_setting": "When enabled, forking will commence from the target message to the latest message in the conversation, according to the behavior selected.", "com_nav_info_include_shadcnui": "When enabled, instructions for using shadcn/ui components will be included. shadcn/ui is a collection of re-usable components built using Radix UI and Tailwind CSS. Note: these are lengthy instructions, you should only enable if informing the LLM of the correct imports and components is important to you. For more information about these components, visit: https://ui.shadcn.com/", "com_nav_info_latex_parsing": "When enabled, LaTeX code in messages will be rendered as mathematical equations. Disabling this may improve performance if you don't need LaTeX rendering.", - "com_nav_info_resumable_streams": "When enabled, LLM generation continues in the background even if your connection drops. You can reconnect and resume receiving the response without losing progress. This is useful for unstable connections or long responses.", "com_nav_info_save_badges_state": "When enabled, the state of the chat badges will be saved. This means that if you create a new chat, the badges will remain in the same state as the previous chat. If you disable this option, the badges will reset to their default state every time you create a new chat", "com_nav_info_save_draft": "When enabled, the text and attachments you enter in the chat form will be automatically saved locally as drafts. These drafts will be available even if you reload the page or switch to a different conversation. Drafts are stored locally on your device and are deleted once the message is sent.", "com_nav_info_show_thinking": "When enabled, the chat will display the thinking dropdowns open by default, allowing you to view the AI's reasoning in real-time. When disabled, the thinking dropdowns will remain closed by default for a cleaner and more streamlined interface", @@ -556,7 +555,6 @@ "com_nav_plus_command": "+-Command", "com_nav_plus_command_description": "Toggle command \"+\" for adding a multi-response setting", "com_nav_profile_picture": "Profile Picture", - "com_nav_resumable_streams": "Resumable Streams (Beta)", "com_nav_save_badges_state": "Save badges state", "com_nav_save_drafts": "Save drafts locally", "com_nav_scroll_button": "Scroll to the end button", diff --git a/client/src/store/settings.ts b/client/src/store/settings.ts index db5200d1ee..ece96d119a 100644 --- a/client/src/store/settings.ts +++ b/client/src/store/settings.ts @@ -42,7 +42,6 @@ const localStorageAtoms = { LaTeXParsing: atomWithLocalStorage('LaTeXParsing', true), centerFormOnLanding: atomWithLocalStorage('centerFormOnLanding', true), showFooter: atomWithLocalStorage('showFooter', true), - resumableStreams: atomWithLocalStorage('resumableStreams', true), // Commands settings atCommand: atomWithLocalStorage('atCommand', true),