🏄‍♂️ fix: Handle SSE Stream Edge Case (#8556)

* refactor: Move draft-related utilities to a new `drafts.ts` file

* refactor: auto-save draft logic to use new get/set functions

* fix: Ensure `getDraft` properly decodes stored draft values

* fix: Handle edge case where stream is cancelled before any response, which creates a blank page
This commit is contained in:
Danny Avila 2025-07-19 13:44:02 -04:00 committed by GitHub
parent f70e0cf849
commit 4c754c1190
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 56 deletions

View file

@ -7,10 +7,10 @@ import {
QueryKeys,
Constants,
EndpointURLs,
ContentTypes,
tPresetSchema,
tMessageSchema,
tConvoUpdateSchema,
ContentTypes,
isAssistantsEndpoint,
} from 'librechat-data-provider';
import type { TMessage, TConversation, EventSubmission } from 'librechat-data-provider';
@ -21,6 +21,7 @@ import type { SetterOrUpdater, Resetter } from 'recoil';
import type { ConversationCursorData } from '~/utils';
import {
logger,
setDraft,
scrollToEnd,
getAllContentText,
addConvoToAllQueries,
@ -457,6 +458,38 @@ export default function useEventHandlers({
announcePolite({ message: 'end', isStatus: true });
announcePolite({ message: getAllContentText(responseMessage) });
const isNewConvo = conversation.conversationId !== submissionConvo.conversationId;
const setFinalMessages = (id: string | null, _messages: TMessage[]) => {
setMessages(_messages);
queryClient.setQueryData<TMessage[]>([QueryKeys.messages, id], _messages);
};
/** Handle edge case where stream is cancelled before any response, which creates a blank page */
if (
!conversation.conversationId &&
responseMessage?.content?.[0]?.['text']?.value ===
submission.initialResponse?.content?.[0]?.['text']?.value
) {
const currentConvoId =
(submissionConvo.conversationId ?? conversation.conversationId) || Constants.NEW_CONVO;
if (isNewConvo && submissionConvo.conversationId) {
removeConvoFromAllQueries(queryClient, submissionConvo.conversationId);
}
const isNewChat =
location.pathname === `/c/${Constants.NEW_CONVO}` &&
currentConvoId === Constants.NEW_CONVO;
setFinalMessages(currentConvoId, isNewChat ? [] : [...messages]);
setDraft({ id: currentConvoId, value: requestMessage?.text });
setIsSubmitting(false);
if (isNewChat) {
navigate(`/c/${Constants.NEW_CONVO}`, { replace: true, state: { focusChat: true } });
}
return;
}
/* Update messages; if assistants endpoint, client doesn't receive responseMessage */
let finalMessages: TMessage[] = [];
if (runMessages) {
@ -467,11 +500,7 @@ export default function useEventHandlers({
finalMessages = [...messages, requestMessage, responseMessage];
}
if (finalMessages.length > 0) {
setMessages(finalMessages);
queryClient.setQueryData<TMessage[]>(
[QueryKeys.messages, conversation.conversationId],
finalMessages,
);
setFinalMessages(conversation.conversationId, finalMessages);
} else if (
isAssistantsEndpoint(submissionConvo.endpoint) &&
(!submissionConvo.conversationId || submissionConvo.conversationId === Constants.NEW_CONVO)
@ -482,9 +511,8 @@ export default function useEventHandlers({
);
}
const isNewConvo = conversation.conversationId !== submissionConvo.conversationId;
if (isNewConvo) {
removeConvoFromAllQueries(queryClient, submissionConvo.conversationId as string);
if (isNewConvo && submissionConvo.conversationId) {
removeConvoFromAllQueries(queryClient, submissionConvo.conversationId);
}
/* Refresh title */
@ -527,7 +555,7 @@ export default function useEventHandlers({
);
}
if (location.pathname === '/c/new') {
if (location.pathname === `/c/${Constants.NEW_CONVO}`) {
navigate(`/c/${conversation.conversationId}`, { replace: true });
}
}