mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
refactor(MessageHandler -> useServerStream): convert all relating files to TS and correct typings based on this change: properly refactor MessageHandler to a custom hook, where it's passed a submission object to instantiate the stream. This is the bare minimum groundwork for potentially having multiple streams running, which would be a big project to modularize a lot of the global state into maps/multiple streams, particular useful for having multiple views in place
This commit is contained in:
parent
8b4d3c2c21
commit
c74c68a135
5 changed files with 83 additions and 87 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import { parseConvo, getResponseSender } from 'librechat-data-provider';
|
import { parseConvo, getResponseSender } from 'librechat-data-provider';
|
||||||
import type { TMessage } from 'librechat-data-provider';
|
import type { TMessage, TSubmission } from 'librechat-data-provider';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
type TAskProps = {
|
type TAskProps = {
|
||||||
|
|
@ -98,7 +98,7 @@ const useMessageHandler = () => {
|
||||||
error: false,
|
error: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const submission = {
|
const submission: TSubmission = {
|
||||||
conversation: {
|
conversation: {
|
||||||
...currentConversation,
|
...currentConversation,
|
||||||
conversationId,
|
conversationId,
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,29 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil';
|
import { useResetRecoilState, useSetRecoilState } from 'recoil';
|
||||||
import { SSE, createPayload } from 'librechat-data-provider';
|
import { SSE, createPayload, tMessageSchema, tConversationSchema } from 'librechat-data-provider';
|
||||||
|
import type { TPlugin, TMessage, TConversation, TSubmission } from 'librechat-data-provider';
|
||||||
import { useAuthContext } from '~/hooks/AuthContext';
|
import { useAuthContext } from '~/hooks/AuthContext';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
export default function MessageHandler() {
|
type TResData = {
|
||||||
const submission = useRecoilValue(store.submission);
|
plugin: TPlugin;
|
||||||
const setIsSubmitting = useSetRecoilState(store.isSubmitting);
|
final?: boolean;
|
||||||
|
initial?: boolean;
|
||||||
|
requestMessage: TMessage;
|
||||||
|
responseMessage: TMessage;
|
||||||
|
conversation: TConversation;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useServerStream(submission: TSubmission | null) {
|
||||||
const setMessages = useSetRecoilState(store.messages);
|
const setMessages = useSetRecoilState(store.messages);
|
||||||
|
const setIsSubmitting = useSetRecoilState(store.isSubmitting);
|
||||||
const setConversation = useSetRecoilState(store.conversation);
|
const setConversation = useSetRecoilState(store.conversation);
|
||||||
const resetLatestMessage = useResetRecoilState(store.latestMessage);
|
const resetLatestMessage = useResetRecoilState(store.latestMessage);
|
||||||
const { token } = useAuthContext();
|
const { token } = useAuthContext();
|
||||||
|
|
||||||
const { refreshConversations } = store.useConversations();
|
const { refreshConversations } = store.useConversations();
|
||||||
|
|
||||||
const messageHandler = (data, submission) => {
|
const messageHandler = (data: string, submission: TSubmission) => {
|
||||||
const {
|
const {
|
||||||
messages,
|
messages,
|
||||||
message,
|
message,
|
||||||
|
|
@ -30,9 +39,9 @@ export default function MessageHandler() {
|
||||||
{
|
{
|
||||||
...initialResponse,
|
...initialResponse,
|
||||||
text: data,
|
text: data,
|
||||||
parentMessageId: message?.overrideParentMessageId,
|
parentMessageId: message?.overrideParentMessageId ?? null,
|
||||||
messageId: message?.overrideParentMessageId + '_',
|
messageId: message?.overrideParentMessageId + '_',
|
||||||
plugin: plugin ? plugin : null,
|
plugin: plugin ?? null,
|
||||||
submitting: true,
|
submitting: true,
|
||||||
// unfinished: true
|
// unfinished: true
|
||||||
},
|
},
|
||||||
|
|
@ -46,7 +55,7 @@ export default function MessageHandler() {
|
||||||
text: data,
|
text: data,
|
||||||
parentMessageId: message?.messageId,
|
parentMessageId: message?.messageId,
|
||||||
messageId: message?.messageId + '_',
|
messageId: message?.messageId + '_',
|
||||||
plugin: plugin ? plugin : null,
|
plugin: plugin ?? null,
|
||||||
submitting: true,
|
submitting: true,
|
||||||
// unfinished: true
|
// unfinished: true
|
||||||
},
|
},
|
||||||
|
|
@ -54,7 +63,7 @@ export default function MessageHandler() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelHandler = (data, submission) => {
|
const cancelHandler = (data: TResData, submission: TSubmission) => {
|
||||||
const { requestMessage, responseMessage, conversation } = data;
|
const { requestMessage, responseMessage, conversation } = data;
|
||||||
const { messages, isRegenerate = false, isEdited = false } = submission;
|
const { messages, isRegenerate = false, isEdited = false } = submission;
|
||||||
|
|
||||||
|
|
@ -84,7 +93,7 @@ export default function MessageHandler() {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const createdHandler = (data, submission) => {
|
const createdHandler = (data: TResData, submission: TSubmission) => {
|
||||||
const {
|
const {
|
||||||
messages,
|
messages,
|
||||||
message,
|
message,
|
||||||
|
|
@ -98,7 +107,7 @@ export default function MessageHandler() {
|
||||||
...(isEdited ? messages.slice(0, -1) : messages),
|
...(isEdited ? messages.slice(0, -1) : messages),
|
||||||
{
|
{
|
||||||
...initialResponse,
|
...initialResponse,
|
||||||
parentMessageId: message?.overrideParentMessageId,
|
parentMessageId: message?.overrideParentMessageId ?? null,
|
||||||
messageId: message?.overrideParentMessageId + '_',
|
messageId: message?.overrideParentMessageId + '_',
|
||||||
submitting: true,
|
submitting: true,
|
||||||
},
|
},
|
||||||
|
|
@ -117,14 +126,16 @@ export default function MessageHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { conversationId } = message;
|
const { conversationId } = message;
|
||||||
setConversation((prevState) => ({
|
setConversation((prevState) =>
|
||||||
...prevState,
|
tConversationSchema.parse({
|
||||||
conversationId,
|
...prevState,
|
||||||
}));
|
conversationId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
resetLatestMessage();
|
resetLatestMessage();
|
||||||
};
|
};
|
||||||
|
|
||||||
const finalHandler = (data, submission) => {
|
const finalHandler = (data: TResData, submission: TSubmission) => {
|
||||||
const { requestMessage, responseMessage, conversation } = data;
|
const { requestMessage, responseMessage, conversation } = data;
|
||||||
const { messages, isRegenerate = false, isEdited = false } = submission;
|
const { messages, isRegenerate = false, isEdited = false } = submission;
|
||||||
|
|
||||||
|
|
@ -154,21 +165,21 @@ export default function MessageHandler() {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const errorHandler = (data, submission) => {
|
const errorHandler = (data: TResData, submission: TSubmission) => {
|
||||||
const { messages, message } = submission;
|
const { messages, message } = submission;
|
||||||
|
|
||||||
console.log('Error:', data);
|
console.log('Error:', data);
|
||||||
const errorResponse = {
|
const errorResponse = tMessageSchema.parse({
|
||||||
...data,
|
...data,
|
||||||
error: true,
|
error: true,
|
||||||
parentMessageId: message?.messageId,
|
parentMessageId: message?.messageId,
|
||||||
};
|
});
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
setMessages([...messages, message, errorResponse]);
|
setMessages([...messages, message, errorResponse]);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const abortConversation = (conversationId) => {
|
const abortConversation = (conversationId = '', submission: TSubmission) => {
|
||||||
console.log(submission);
|
console.log(submission);
|
||||||
const { endpoint } = submission?.conversation || {};
|
const { endpoint } = submission?.conversation || {};
|
||||||
|
|
||||||
|
|
@ -212,7 +223,7 @@ export default function MessageHandler() {
|
||||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
events.onmessage = (e) => {
|
events.onmessage = (e: MessageEvent) => {
|
||||||
const data = JSON.parse(e.data);
|
const data = JSON.parse(e.data);
|
||||||
|
|
||||||
if (data.final) {
|
if (data.final) {
|
||||||
|
|
@ -227,8 +238,8 @@ export default function MessageHandler() {
|
||||||
createdHandler(data, { ...submission, message });
|
createdHandler(data, { ...submission, message });
|
||||||
console.log('created', message);
|
console.log('created', message);
|
||||||
} else {
|
} else {
|
||||||
let text = data.text || data.response;
|
const text = data.text || data.response;
|
||||||
let { initial, plugin } = data;
|
const { initial, plugin } = data;
|
||||||
if (initial) {
|
if (initial) {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
}
|
}
|
||||||
|
|
@ -242,9 +253,9 @@ export default function MessageHandler() {
|
||||||
events.onopen = () => console.log('connection is opened');
|
events.onopen = () => console.log('connection is opened');
|
||||||
|
|
||||||
events.oncancel = () =>
|
events.oncancel = () =>
|
||||||
abortConversation(message?.conversationId || submission?.conversationId);
|
abortConversation(message?.conversationId ?? submission?.conversationId, submission);
|
||||||
|
|
||||||
events.onerror = function (e) {
|
events.onerror = function (e: MessageEvent) {
|
||||||
console.log('error in opening conn.');
|
console.log('error in opening conn.');
|
||||||
events.close();
|
events.close();
|
||||||
|
|
||||||
|
|
@ -268,6 +279,4 @@ export default function MessageHandler() {
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [submission]);
|
}, [submission]);
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
useGetEndpointsQuery,
|
useGetEndpointsQuery,
|
||||||
|
|
@ -9,8 +9,7 @@ import {
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
|
|
||||||
import { Nav, MobileNav } from '~/components/Nav';
|
import { Nav, MobileNav } from '~/components/Nav';
|
||||||
import { useAuthContext } from '~/hooks/AuthContext';
|
import { useAuthContext, useServerStream } from '~/hooks';
|
||||||
import MessageHandler from './MessageHandler';
|
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
export default function Root() {
|
export default function Root() {
|
||||||
|
|
@ -19,6 +18,9 @@ export default function Root() {
|
||||||
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : false;
|
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const submission = useRecoilValue(store.submission);
|
||||||
|
useServerStream(submission ?? null);
|
||||||
|
|
||||||
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
|
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
|
||||||
const setEndpointsConfig = useSetRecoilState(store.endpointsConfig);
|
const setEndpointsConfig = useSetRecoilState(store.endpointsConfig);
|
||||||
const setPresets = useSetRecoilState(store.presets);
|
const setPresets = useSetRecoilState(store.presets);
|
||||||
|
|
@ -71,7 +73,6 @@ export default function Root() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MessageHandler />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -12,32 +12,6 @@ export enum EModelEndpoint {
|
||||||
|
|
||||||
export const eModelEndpointSchema = z.nativeEnum(EModelEndpoint);
|
export const eModelEndpointSchema = z.nativeEnum(EModelEndpoint);
|
||||||
|
|
||||||
export const tMessageSchema = z.object({
|
|
||||||
messageId: z.string(),
|
|
||||||
clientId: z.string().nullable().optional(),
|
|
||||||
conversationId: z.string().nullable(),
|
|
||||||
parentMessageId: z.string().nullable(),
|
|
||||||
sender: z.string(),
|
|
||||||
text: z.string(),
|
|
||||||
isCreatedByUser: z.boolean(),
|
|
||||||
error: z.boolean(),
|
|
||||||
createdAt: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.default(() => new Date().toISOString()),
|
|
||||||
updatedAt: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.default(() => new Date().toISOString()),
|
|
||||||
current: z.boolean().optional(),
|
|
||||||
unfinished: z.boolean().optional(),
|
|
||||||
submitting: z.boolean().optional(),
|
|
||||||
searchResult: z.boolean().optional(),
|
|
||||||
finish_reason: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TMessage = z.input<typeof tMessageSchema>;
|
|
||||||
|
|
||||||
export const tPluginAuthConfigSchema = z.object({
|
export const tPluginAuthConfigSchema = z.object({
|
||||||
authField: z.string(),
|
authField: z.string(),
|
||||||
label: z.string(),
|
label: z.string(),
|
||||||
|
|
@ -76,6 +50,36 @@ export const tAgentOptionsSchema = z.object({
|
||||||
temperature: z.number(),
|
temperature: z.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const tMessageSchema = z.object({
|
||||||
|
messageId: z.string(),
|
||||||
|
clientId: z.string().nullable().optional(),
|
||||||
|
conversationId: z.string().nullable(),
|
||||||
|
parentMessageId: z.string().nullable(),
|
||||||
|
responseMessageId: z.string().nullable().optional(),
|
||||||
|
overrideParentMessageId: z.string().nullable().optional(),
|
||||||
|
plugin: tPluginSchema.nullable().optional(),
|
||||||
|
sender: z.string(),
|
||||||
|
text: z.string(),
|
||||||
|
generation: z.string().nullable().optional(),
|
||||||
|
isCreatedByUser: z.boolean(),
|
||||||
|
error: z.boolean(),
|
||||||
|
createdAt: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.default(() => new Date().toISOString()),
|
||||||
|
updatedAt: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.default(() => new Date().toISOString()),
|
||||||
|
current: z.boolean().optional(),
|
||||||
|
unfinished: z.boolean().optional(),
|
||||||
|
submitting: z.boolean().optional(),
|
||||||
|
searchResult: z.boolean().optional(),
|
||||||
|
finish_reason: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TMessage = z.input<typeof tMessageSchema>;
|
||||||
|
|
||||||
export const tConversationSchema = z.object({
|
export const tConversationSchema = z.object({
|
||||||
conversationId: z.string().nullable(),
|
conversationId: z.string().nullable(),
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { TMessage, EModelEndpoint, TConversation, TEndpointOption } from './schemas';
|
import type { TPlugin, TMessage, TConversation, TEndpointOption } from './schemas';
|
||||||
|
|
||||||
export * from './schemas';
|
export * from './schemas';
|
||||||
|
|
||||||
|
|
@ -7,32 +7,14 @@ export type TMessages = TMessage[];
|
||||||
export type TMessagesAtom = TMessages | null;
|
export type TMessagesAtom = TMessages | null;
|
||||||
|
|
||||||
export type TSubmission = {
|
export type TSubmission = {
|
||||||
clientId?: string;
|
plugin?: TPlugin;
|
||||||
context?: string;
|
|
||||||
conversationId?: string;
|
|
||||||
conversationSignature?: string;
|
|
||||||
current: boolean;
|
|
||||||
endpoint: EModelEndpoint | null;
|
|
||||||
invocationId: number;
|
|
||||||
isCreatedByUser: boolean;
|
|
||||||
jailbreak: boolean;
|
|
||||||
jailbreakConversationId?: string;
|
|
||||||
messageId: string;
|
|
||||||
overrideParentMessageId?: string | boolean;
|
|
||||||
parentMessageId?: string;
|
|
||||||
sender: string;
|
|
||||||
systemMessage?: string;
|
|
||||||
text: string;
|
|
||||||
toneStyle?: string;
|
|
||||||
model?: string;
|
|
||||||
promptPrefix?: string;
|
|
||||||
temperature?: number;
|
|
||||||
top_p?: number;
|
|
||||||
presence_penalty?: number;
|
|
||||||
frequence_penalty?: number;
|
|
||||||
isEdited?: boolean;
|
|
||||||
conversation: TConversation;
|
|
||||||
message: TMessage;
|
message: TMessage;
|
||||||
|
isEdited?: boolean;
|
||||||
|
messages: TMessage[];
|
||||||
|
isRegenerate?: boolean;
|
||||||
|
conversationId?: string;
|
||||||
|
initialResponse: TMessage;
|
||||||
|
conversation: TConversation;
|
||||||
endpointOption: TEndpointOption;
|
endpointOption: TEndpointOption;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -167,7 +149,7 @@ export type TResetPassword = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TStartupConfig = {
|
export type TStartupConfig = {
|
||||||
appTitle: boolean;
|
appTitle: string;
|
||||||
googleLoginEnabled: boolean;
|
googleLoginEnabled: boolean;
|
||||||
openidLoginEnabled: boolean;
|
openidLoginEnabled: boolean;
|
||||||
githubLoginEnabled: boolean;
|
githubLoginEnabled: boolean;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue