diff --git a/client/src/hooks/useMessageHandler.ts b/client/src/hooks/useMessageHandler.ts
index 21deadb18d..47c9a44eeb 100644
--- a/client/src/hooks/useMessageHandler.ts
+++ b/client/src/hooks/useMessageHandler.ts
@@ -1,7 +1,7 @@
import { v4 } from 'uuid';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
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';
type TAskProps = {
@@ -98,7 +98,7 @@ const useMessageHandler = () => {
error: false,
};
- const submission = {
+ const submission: TSubmission = {
conversation: {
...currentConversation,
conversationId,
diff --git a/client/src/routes/MessageHandler.jsx b/client/src/hooks/useServerStream.ts
similarity index 79%
rename from client/src/routes/MessageHandler.jsx
rename to client/src/hooks/useServerStream.ts
index 8769497eb4..f8ecbcf72e 100644
--- a/client/src/routes/MessageHandler.jsx
+++ b/client/src/hooks/useServerStream.ts
@@ -1,20 +1,29 @@
import { useEffect } from 'react';
-import { useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil';
-import { SSE, createPayload } from 'librechat-data-provider';
+import { useResetRecoilState, useSetRecoilState } from 'recoil';
+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 store from '~/store';
-export default function MessageHandler() {
- const submission = useRecoilValue(store.submission);
- const setIsSubmitting = useSetRecoilState(store.isSubmitting);
+type TResData = {
+ plugin: TPlugin;
+ final?: boolean;
+ initial?: boolean;
+ requestMessage: TMessage;
+ responseMessage: TMessage;
+ conversation: TConversation;
+};
+
+export default function useServerStream(submission: TSubmission | null) {
const setMessages = useSetRecoilState(store.messages);
+ const setIsSubmitting = useSetRecoilState(store.isSubmitting);
const setConversation = useSetRecoilState(store.conversation);
const resetLatestMessage = useResetRecoilState(store.latestMessage);
const { token } = useAuthContext();
const { refreshConversations } = store.useConversations();
- const messageHandler = (data, submission) => {
+ const messageHandler = (data: string, submission: TSubmission) => {
const {
messages,
message,
@@ -30,9 +39,9 @@ export default function MessageHandler() {
{
...initialResponse,
text: data,
- parentMessageId: message?.overrideParentMessageId,
+ parentMessageId: message?.overrideParentMessageId ?? null,
messageId: message?.overrideParentMessageId + '_',
- plugin: plugin ? plugin : null,
+ plugin: plugin ?? null,
submitting: true,
// unfinished: true
},
@@ -46,7 +55,7 @@ export default function MessageHandler() {
text: data,
parentMessageId: message?.messageId,
messageId: message?.messageId + '_',
- plugin: plugin ? plugin : null,
+ plugin: plugin ?? null,
submitting: 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 { 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 {
messages,
message,
@@ -98,7 +107,7 @@ export default function MessageHandler() {
...(isEdited ? messages.slice(0, -1) : messages),
{
...initialResponse,
- parentMessageId: message?.overrideParentMessageId,
+ parentMessageId: message?.overrideParentMessageId ?? null,
messageId: message?.overrideParentMessageId + '_',
submitting: true,
},
@@ -117,14 +126,16 @@ export default function MessageHandler() {
}
const { conversationId } = message;
- setConversation((prevState) => ({
- ...prevState,
- conversationId,
- }));
+ setConversation((prevState) =>
+ tConversationSchema.parse({
+ ...prevState,
+ conversationId,
+ }),
+ );
resetLatestMessage();
};
- const finalHandler = (data, submission) => {
+ const finalHandler = (data: TResData, submission: TSubmission) => {
const { requestMessage, responseMessage, conversation } = data;
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;
console.log('Error:', data);
- const errorResponse = {
+ const errorResponse = tMessageSchema.parse({
...data,
error: true,
parentMessageId: message?.messageId,
- };
+ });
setIsSubmitting(false);
setMessages([...messages, message, errorResponse]);
return;
};
- const abortConversation = (conversationId) => {
+ const abortConversation = (conversationId = '', submission: TSubmission) => {
console.log(submission);
const { endpoint } = submission?.conversation || {};
@@ -212,7 +223,7 @@ export default function MessageHandler() {
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
});
- events.onmessage = (e) => {
+ events.onmessage = (e: MessageEvent) => {
const data = JSON.parse(e.data);
if (data.final) {
@@ -227,8 +238,8 @@ export default function MessageHandler() {
createdHandler(data, { ...submission, message });
console.log('created', message);
} else {
- let text = data.text || data.response;
- let { initial, plugin } = data;
+ const text = data.text || data.response;
+ const { initial, plugin } = data;
if (initial) {
console.log(data);
}
@@ -242,9 +253,9 @@ export default function MessageHandler() {
events.onopen = () => console.log('connection is opened');
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.');
events.close();
@@ -268,6 +279,4 @@ export default function MessageHandler() {
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [submission]);
-
- return null;
}
diff --git a/client/src/routes/Root.jsx b/client/src/routes/Root.tsx
similarity index 91%
rename from client/src/routes/Root.jsx
rename to client/src/routes/Root.tsx
index 71acb0686c..36b9ace6b7 100644
--- a/client/src/routes/Root.jsx
+++ b/client/src/routes/Root.tsx
@@ -1,6 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useEffect, useState } from 'react';
-import { useSetRecoilState } from 'recoil';
+import { useRecoilValue, useSetRecoilState } from 'recoil';
import { Outlet } from 'react-router-dom';
import {
useGetEndpointsQuery,
@@ -9,8 +9,7 @@ import {
} from 'librechat-data-provider';
import { Nav, MobileNav } from '~/components/Nav';
-import { useAuthContext } from '~/hooks/AuthContext';
-import MessageHandler from './MessageHandler';
+import { useAuthContext, useServerStream } from '~/hooks';
import store from '~/store';
export default function Root() {
@@ -19,6 +18,9 @@ export default function Root() {
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : false;
});
+ const submission = useRecoilValue(store.submission);
+ useServerStream(submission ?? null);
+
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
const setEndpointsConfig = useSetRecoilState(store.endpointsConfig);
const setPresets = useSetRecoilState(store.presets);
@@ -71,7 +73,6 @@ export default function Root() {
-
>
);
}
diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts
index cd2fb3af48..a66860390e 100644
--- a/packages/data-provider/src/schemas.ts
+++ b/packages/data-provider/src/schemas.ts
@@ -12,32 +12,6 @@ export enum 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;
-
export const tPluginAuthConfigSchema = z.object({
authField: z.string(),
label: z.string(),
@@ -76,6 +50,36 @@ export const tAgentOptionsSchema = z.object({
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;
+
export const tConversationSchema = z.object({
conversationId: z.string().nullable(),
title: z.string(),
diff --git a/packages/data-provider/src/types.ts b/packages/data-provider/src/types.ts
index 81c2e75304..40bb5ad1e0 100644
--- a/packages/data-provider/src/types.ts
+++ b/packages/data-provider/src/types.ts
@@ -1,4 +1,4 @@
-import type { TMessage, EModelEndpoint, TConversation, TEndpointOption } from './schemas';
+import type { TPlugin, TMessage, TConversation, TEndpointOption } from './schemas';
export * from './schemas';
@@ -7,32 +7,14 @@ export type TMessages = TMessage[];
export type TMessagesAtom = TMessages | null;
export type TSubmission = {
- clientId?: string;
- 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;
+ plugin?: TPlugin;
message: TMessage;
+ isEdited?: boolean;
+ messages: TMessage[];
+ isRegenerate?: boolean;
+ conversationId?: string;
+ initialResponse: TMessage;
+ conversation: TConversation;
endpointOption: TEndpointOption;
};
@@ -167,7 +149,7 @@ export type TResetPassword = {
};
export type TStartupConfig = {
- appTitle: boolean;
+ appTitle: string;
googleLoginEnabled: boolean;
openidLoginEnabled: boolean;
githubLoginEnabled: boolean;