🚏 chore: Remove Resumable Stream Toggle (#11258)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions

* 🚏 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.
This commit is contained in:
Danny Avila 2026-01-07 20:37:35 -05:00 committed by GitHub
parent 24e8a258cd
commit c30afb8b68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 32 additions and 89 deletions

View file

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

View file

@ -160,14 +160,13 @@ const Conversations: FC<ConversationsProps> = ({
}) => {
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],

View file

@ -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() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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