From dc81abcc011e6fa72dc1ba5cf3f4024ee9855d23 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 18 Dec 2025 09:52:27 -0500 Subject: [PATCH] refactor: useResumableStreamToggle hook to manage resumable streams for legacy/assistants endpoints - Introduced a new hook, useResumableStreamToggle, to automatically toggle resumable streams off for assistants endpoints and restore the previous value when switching away. - Updated ChatView component to utilize the new hook, enhancing the handling of streaming behavior based on endpoint type. - Refactored imports in ChatView for better organization. --- client/src/components/Chat/ChatView.tsx | 13 +++++- client/src/hooks/SSE/index.ts | 1 + .../src/hooks/SSE/useResumableStreamToggle.ts | 41 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create 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 9275496734..c6eb25d546 100644 --- a/client/src/components/Chat/ChatView.tsx +++ b/client/src/components/Chat/ChatView.tsx @@ -7,7 +7,13 @@ 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 { useChatHelpers, useAddedResponse, useAdaptiveSSE, useResumeOnLoad } from '~/hooks'; +import { + useResumableStreamToggle, + useAddedResponse, + useResumeOnLoad, + useAdaptiveSSE, + useChatHelpers, +} from '~/hooks'; import ConversationStarters from './Input/ConversationStarters'; import { useGetMessagesByConvoId } from '~/data-provider'; import MessagesView from './Messages/MessagesView'; @@ -50,6 +56,11 @@ function ChatView({ index = 0 }: { index?: number }) { const chatHelpers = useChatHelpers(index, conversationId); const addedChatHelpers = useAddedResponse({ rootIndex: index }); + 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/hooks/SSE/index.ts b/client/src/hooks/SSE/index.ts index 2829db76f6..800de1e2a7 100644 --- a/client/src/hooks/SSE/index.ts +++ b/client/src/hooks/SSE/index.ts @@ -5,3 +5,4 @@ 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/useResumableStreamToggle.ts b/client/src/hooks/SSE/useResumableStreamToggle.ts new file mode 100644 index 0000000000..f14fde044a --- /dev/null +++ b/client/src/hooks/SSE/useResumableStreamToggle.ts @@ -0,0 +1,41 @@ +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]); +}