mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
♻️ refactor: Logout UX, Improved State Teardown, & Remove Unused Code (#5292)
* refactor: SearchBar and Nav components to streamline search functionality and improve state management * refactor: remove refresh conversations * chore: update useNewConvo calls to remove hardcoded default index * refactor: null check for submission in useSSE hook * refactor: remove useConversation hook and update useSearch to utilize useNewConvo * refactor: remove conversation and banner store files; consolidate state management into misc; improve typing of families and add messagesSiblingIdxFamily * refactor: more effectively clear all user/convo state without side effects on logout/delete user * refactor: replace useParams with useLocation in SearchBar to correctly load conversation * refactor: update SearchButtons to use button element and improve conversation ID handling * refactor: use named function for `newConversation` for better call stack tracing * refactor: enhance TermsAndConditionsModal to support array content and improve type definitions for terms of service * refactor: add SetConvoProvider and message invalidation when navigating from search results to prevent initial route rendering edge cases * refactor: rename getLocalStorageItems to localStorage and update imports for consistency * refactor: move clearLocalStorage function to utils and simplify localStorage clearing logic * refactor: migrate authentication mutations to a dedicated Auth data provider and update related tests
This commit is contained in:
parent
24beda3d69
commit
aa80e4594e
45 changed files with 378 additions and 434 deletions
14
client/src/Providers/SetConvoContext.tsx
Normal file
14
client/src/Providers/SetConvoContext.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { createContext, useContext, useRef } from 'react';
|
||||||
|
import type { MutableRefObject } from 'react';
|
||||||
|
|
||||||
|
type SetConvoContext = MutableRefObject<boolean>;
|
||||||
|
|
||||||
|
export const SetConvoContext = createContext<SetConvoContext>({} as SetConvoContext);
|
||||||
|
|
||||||
|
export const SetConvoProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const hasSetConversation = useRef<boolean>(false);
|
||||||
|
|
||||||
|
return <SetConvoContext.Provider value={hasSetConversation}>{children}</SetConvoContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetConvoContext = () => useContext(SetConvoContext);
|
||||||
|
|
@ -18,3 +18,4 @@ export * from './AnnouncerContext';
|
||||||
export * from './AgentsMapContext';
|
export * from './AgentsMapContext';
|
||||||
export * from './CodeBlockContext';
|
export * from './CodeBlockContext';
|
||||||
export * from './ToolCallsMapContext';
|
export * from './ToolCallsMapContext';
|
||||||
|
export * from './SetConvoContext';
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
|
||||||
import { getByTestId, render, waitFor } from 'test/layout-test-utils';
|
import { getByTestId, render, waitFor } from 'test/layout-test-utils';
|
||||||
import * as mockDataProvider from 'librechat-data-provider/react-query';
|
import * as mockDataProvider from 'librechat-data-provider/react-query';
|
||||||
import type { TStartupConfig } from 'librechat-data-provider';
|
import type { TStartupConfig } from 'librechat-data-provider';
|
||||||
|
import * as authDataProvider from '~/data-provider/Auth/mutations';
|
||||||
import AuthLayout from '~/components/Auth/AuthLayout';
|
import AuthLayout from '~/components/Auth/AuthLayout';
|
||||||
import Login from '~/components/Auth/Login';
|
import Login from '~/components/Auth/Login';
|
||||||
|
|
||||||
|
|
@ -61,7 +62,7 @@ const setup = ({
|
||||||
},
|
},
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const mockUseLoginUser = jest
|
const mockUseLoginUser = jest
|
||||||
.spyOn(mockDataProvider, 'useLoginUserMutation')
|
.spyOn(authDataProvider, 'useLoginUserMutation')
|
||||||
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
||||||
.mockReturnValue(useLoginUserReturnValue);
|
.mockReturnValue(useLoginUserReturnValue);
|
||||||
const mockUseGetUserQuery = jest
|
const mockUseGetUserQuery = jest
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { render, getByTestId } from 'test/layout-test-utils';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import * as mockDataProvider from 'librechat-data-provider/react-query';
|
import * as mockDataProvider from 'librechat-data-provider/react-query';
|
||||||
import type { TStartupConfig } from 'librechat-data-provider';
|
import type { TStartupConfig } from 'librechat-data-provider';
|
||||||
|
import * as authDataProvider from '~/data-provider/Auth/mutations';
|
||||||
import Login from '../LoginForm';
|
import Login from '../LoginForm';
|
||||||
|
|
||||||
jest.mock('librechat-data-provider/react-query');
|
jest.mock('librechat-data-provider/react-query');
|
||||||
|
|
@ -66,7 +67,7 @@ const setup = ({
|
||||||
},
|
},
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const mockUseLoginUser = jest
|
const mockUseLoginUser = jest
|
||||||
.spyOn(mockDataProvider, 'useLoginUserMutation')
|
.spyOn(authDataProvider, 'useLoginUserMutation')
|
||||||
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
||||||
.mockReturnValue(useLoginUserReturnValue);
|
.mockReturnValue(useLoginUserReturnValue);
|
||||||
const mockUseGetUserQuery = jest
|
const mockUseGetUserQuery = jest
|
||||||
|
|
|
||||||
|
|
@ -8,33 +8,34 @@ export default function SearchButtons({ message }: { message: TMessage }) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const { searchQueryRes } = useSearchContext();
|
const { searchQueryRes } = useSearchContext();
|
||||||
const { navigateWithLastTools } = useNavigateToConvo();
|
const { navigateWithLastTools } = useNavigateToConvo();
|
||||||
|
const conversationId = message.conversationId ?? '';
|
||||||
|
|
||||||
if (!message.conversationId) {
|
if (!conversationId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clickHandler = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
const clickHandler = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const conversation = getConversationById(searchQueryRes?.data, message.conversationId);
|
const conversation = getConversationById(searchQueryRes?.data, conversationId);
|
||||||
if (!conversation) {
|
if (!conversation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.title = message.title ?? '';
|
document.title = message.title ?? '';
|
||||||
navigateWithLastTools(conversation);
|
navigateWithLastTools(conversation, true, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="visible mt-0 flex items-center justify-center gap-1 self-end text-gray-400 lg:justify-start">
|
<div className="visible mt-0 flex items-center justify-center gap-1 self-end text-text-secondary lg:justify-start">
|
||||||
<a
|
<button
|
||||||
className="ml-0 flex cursor-pointer items-center gap-1.5 rounded-md p-1 text-xs hover:text-gray-900 hover:underline dark:text-gray-400/70 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"
|
className="ml-0 flex cursor-pointer items-center gap-1.5 rounded-md p-1 text-xs hover:text-text-primary hover:underline"
|
||||||
onClick={clickHandler}
|
onClick={clickHandler}
|
||||||
title={localize('com_ui_go_to_conversation')}
|
title={localize('com_ui_go_to_conversation')}
|
||||||
>
|
>
|
||||||
<Link className="icon-sm" />
|
<Link className="icon-sm" />
|
||||||
{message.title}
|
{message.title}
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { Constants } from 'librechat-data-provider';
|
||||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||||
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
|
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
|
||||||
import type { TConversation } from 'librechat-data-provider';
|
import type { TConversation } from 'librechat-data-provider';
|
||||||
import { useConversations, useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks';
|
import { useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks';
|
||||||
import { useUpdateConversationMutation } from '~/data-provider';
|
import { useUpdateConversationMutation } from '~/data-provider';
|
||||||
import EndpointIcon from '~/components/Endpoints/EndpointIcon';
|
import EndpointIcon from '~/components/Endpoints/EndpointIcon';
|
||||||
import { NotificationSeverity } from '~/common';
|
import { NotificationSeverity } from '~/common';
|
||||||
|
|
@ -36,7 +36,6 @@ export default function Conversation({
|
||||||
const activeConvos = useRecoilValue(store.allConversationsSelector);
|
const activeConvos = useRecoilValue(store.allConversationsSelector);
|
||||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||||
const { navigateWithLastTools } = useNavigateToConvo();
|
const { navigateWithLastTools } = useNavigateToConvo();
|
||||||
const { refreshConversations } = useConversations();
|
|
||||||
const { showToast } = useToastContext();
|
const { showToast } = useToastContext();
|
||||||
const { conversationId, title } = conversation;
|
const { conversationId, title } = conversation;
|
||||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
@ -97,7 +96,6 @@ export default function Conversation({
|
||||||
updateConvoMutation.mutate(
|
updateConvoMutation.mutate(
|
||||||
{ conversationId, title: titleInput ?? '' },
|
{ conversationId, title: titleInput ?? '' },
|
||||||
{
|
{
|
||||||
onSuccess: () => refreshConversations(),
|
|
||||||
onError: () => {
|
onError: () => {
|
||||||
setTitleInput(title);
|
setTitleInput(title);
|
||||||
showToast({
|
showToast({
|
||||||
|
|
@ -109,7 +107,7 @@ export default function Conversation({
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[title, titleInput, conversationId, showToast, refreshConversations, updateConvoMutation],
|
[title, titleInput, conversationId, showToast, updateConvoMutation],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export default function MobileNav({
|
||||||
}) {
|
}) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { newConversation } = useNewConvo(0);
|
const { newConversation } = useNewConvo();
|
||||||
const conversation = useRecoilValue(store.conversationByIndex(0));
|
const conversation = useRecoilValue(store.conversationByIndex(0));
|
||||||
const { title = 'New Chat' } = conversation || {};
|
const { title = 'New Chat' } = conversation || {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { useCallback, useEffect, useState, useMemo, memo } from 'react';
|
import { useCallback, useEffect, useState, useMemo, memo } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
||||||
import type { ConversationListResponse } from 'librechat-data-provider';
|
import type { ConversationListResponse } from 'librechat-data-provider';
|
||||||
import {
|
import {
|
||||||
|
|
@ -8,10 +7,8 @@ import {
|
||||||
useHasAccess,
|
useHasAccess,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useAuthContext,
|
useAuthContext,
|
||||||
useConversation,
|
|
||||||
useLocalStorage,
|
useLocalStorage,
|
||||||
useNavScrolling,
|
useNavScrolling,
|
||||||
useConversations,
|
|
||||||
} from '~/hooks';
|
} from '~/hooks';
|
||||||
import { useConversationsInfiniteQuery } from '~/data-provider';
|
import { useConversationsInfiniteQuery } from '~/data-provider';
|
||||||
import { Conversations } from '~/components/Conversations';
|
import { Conversations } from '~/components/Conversations';
|
||||||
|
|
@ -33,7 +30,6 @@ const Nav = ({
|
||||||
setNavVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
setNavVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}) => {
|
}) => {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const { conversationId } = useParams();
|
|
||||||
const { isAuthenticated } = useAuthContext();
|
const { isAuthenticated } = useAuthContext();
|
||||||
|
|
||||||
const [navWidth, setNavWidth] = useState('260px');
|
const [navWidth, setNavWidth] = useState('260px');
|
||||||
|
|
@ -67,11 +63,9 @@ const Nav = ({
|
||||||
}
|
}
|
||||||
}, [isSmallScreen]);
|
}, [isSmallScreen]);
|
||||||
|
|
||||||
const { newConversation } = useConversation();
|
|
||||||
const [showLoading, setShowLoading] = useState(false);
|
const [showLoading, setShowLoading] = useState(false);
|
||||||
const isSearchEnabled = useRecoilValue(store.isSearchEnabled);
|
const isSearchEnabled = useRecoilValue(store.isSearchEnabled);
|
||||||
|
|
||||||
const { refreshConversations } = useConversations();
|
|
||||||
const { pageNumber, searchQuery, setPageNumber, searchQueryRes } = useSearchContext();
|
const { pageNumber, searchQuery, setPageNumber, searchQueryRes } = useSearchContext();
|
||||||
const [tags, setTags] = useState<string[]>([]);
|
const [tags, setTags] = useState<string[]>([]);
|
||||||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
|
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
|
||||||
|
|
@ -104,14 +98,6 @@ const Nav = ({
|
||||||
[data, searchQuery, searchQueryRes?.data],
|
[data, searchQuery, searchQueryRes?.data],
|
||||||
);
|
);
|
||||||
|
|
||||||
const clearSearch = () => {
|
|
||||||
setPageNumber(1);
|
|
||||||
refreshConversations();
|
|
||||||
if (conversationId == 'search') {
|
|
||||||
newConversation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleNavVisible = () => {
|
const toggleNavVisible = () => {
|
||||||
setNavVisible((prev: boolean) => {
|
setNavVisible((prev: boolean) => {
|
||||||
localStorage.setItem('navVisible', JSON.stringify(!prev));
|
localStorage.setItem('navVisible', JSON.stringify(!prev));
|
||||||
|
|
@ -174,7 +160,10 @@ const Nav = ({
|
||||||
subHeaders={
|
subHeaders={
|
||||||
<>
|
<>
|
||||||
{isSearchEnabled === true && (
|
{isSearchEnabled === true && (
|
||||||
<SearchBar clearSearch={clearSearch} isSmallScreen={isSmallScreen} />
|
<SearchBar
|
||||||
|
setPageNumber={setPageNumber}
|
||||||
|
isSmallScreen={isSmallScreen}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{hasAccessToBookmarks === true && (
|
{hasAccessToBookmarks === true && (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,39 @@
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import { Search, X } from 'lucide-react';
|
import { Search, X } from 'lucide-react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import { QueryKeys } from 'librechat-data-provider';
|
import { QueryKeys } from 'librechat-data-provider';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { forwardRef, useState, useCallback, useMemo, Ref } from 'react';
|
import { forwardRef, useState, useCallback, useMemo, Ref } from 'react';
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize, useNewConvo } from '~/hooks';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
type SearchBarProps = {
|
type SearchBarProps = {
|
||||||
clearSearch: () => void;
|
|
||||||
isSmallScreen?: boolean;
|
isSmallScreen?: boolean;
|
||||||
|
setPageNumber: React.Dispatch<React.SetStateAction<number>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SearchBar = forwardRef((props: SearchBarProps, ref: Ref<HTMLDivElement>) => {
|
const SearchBar = forwardRef((props: SearchBarProps, ref: Ref<HTMLDivElement>) => {
|
||||||
const { clearSearch, isSmallScreen } = props;
|
const localize = useLocalize();
|
||||||
|
const location = useLocation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const { setPageNumber, isSmallScreen } = props;
|
||||||
|
|
||||||
|
const [text, setText] = useState('');
|
||||||
|
const [showClearIcon, setShowClearIcon] = useState(false);
|
||||||
|
|
||||||
|
const { newConversation } = useNewConvo();
|
||||||
const clearConvoState = store.useClearConvoState();
|
const clearConvoState = store.useClearConvoState();
|
||||||
const setSearchQuery = useSetRecoilState(store.searchQuery);
|
const setSearchQuery = useSetRecoilState(store.searchQuery);
|
||||||
const [showClearIcon, setShowClearIcon] = useState(false);
|
|
||||||
const [text, setText] = useState('');
|
|
||||||
const setIsSearching = useSetRecoilState(store.isSearching);
|
const setIsSearching = useSetRecoilState(store.isSearching);
|
||||||
const localize = useLocalize();
|
|
||||||
|
const clearSearch = useCallback(() => {
|
||||||
|
setPageNumber(1);
|
||||||
|
if (location.pathname.includes('/search')) {
|
||||||
|
newConversation();
|
||||||
|
}
|
||||||
|
}, [newConversation, setPageNumber, location.pathname]);
|
||||||
|
|
||||||
const clearText = useCallback(() => {
|
const clearText = useCallback(() => {
|
||||||
setShowClearIcon(false);
|
setShowClearIcon(false);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useClearConversationsMutation } from 'librechat-data-provider/react-query';
|
import { useClearConversationsMutation } from 'librechat-data-provider/react-query';
|
||||||
import { Label, Button, OGDialog, OGDialogTrigger, Spinner } from '~/components';
|
import { Label, Button, OGDialog, OGDialogTrigger, Spinner } from '~/components';
|
||||||
import { useConversation, useConversations, useLocalize } from '~/hooks';
|
import { useLocalize, useNewConvo } from '~/hooks';
|
||||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||||
|
|
||||||
export const ClearChats = () => {
|
export const ClearChats = () => {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const { newConversation } = useConversation();
|
const { newConversation } = useNewConvo();
|
||||||
const { refreshConversations } = useConversations();
|
|
||||||
const clearConvosMutation = useClearConversationsMutation();
|
const clearConvosMutation = useClearConversationsMutation();
|
||||||
|
|
||||||
const clearConvos = () => {
|
const clearConvos = () => {
|
||||||
|
|
@ -17,7 +16,6 @@ export const ClearChats = () => {
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
newConversation();
|
newConversation();
|
||||||
refreshConversations();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { useState, useRef } from 'react';
|
||||||
import { Import } from 'lucide-react';
|
import { Import } from 'lucide-react';
|
||||||
import type { TError } from 'librechat-data-provider';
|
import type { TError } from 'librechat-data-provider';
|
||||||
import { useUploadConversationsMutation } from '~/data-provider';
|
import { useUploadConversationsMutation } from '~/data-provider';
|
||||||
import { useLocalize, useConversations } from '~/hooks';
|
|
||||||
import { useToastContext } from '~/Providers';
|
import { useToastContext } from '~/Providers';
|
||||||
import { Spinner } from '~/components/svg';
|
import { Spinner } from '~/components/svg';
|
||||||
|
import { useLocalize } from '~/hooks';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
function ImportConversations() {
|
function ImportConversations() {
|
||||||
|
|
@ -15,11 +15,9 @@ function ImportConversations() {
|
||||||
const [, setErrors] = useState<string[]>([]);
|
const [, setErrors] = useState<string[]>([]);
|
||||||
const [allowImport, setAllowImport] = useState(true);
|
const [allowImport, setAllowImport] = useState(true);
|
||||||
const setError = (error: string) => setErrors((prevErrors) => [...prevErrors, error]);
|
const setError = (error: string) => setErrors((prevErrors) => [...prevErrors, error]);
|
||||||
const { refreshConversations } = useConversations();
|
|
||||||
|
|
||||||
const uploadFile = useUploadConversationsMutation({
|
const uploadFile = useUploadConversationsMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
refreshConversations();
|
|
||||||
showToast({ message: localize('com_ui_import_conversation_success') });
|
showToast({ message: localize('com_ui_import_conversation_success') });
|
||||||
setAllowImport(true);
|
setAllowImport(true);
|
||||||
},
|
},
|
||||||
|
|
@ -29,7 +27,7 @@ function ImportConversations() {
|
||||||
setError(
|
setError(
|
||||||
(error as TError).response?.data?.message ?? 'An error occurred while uploading the file.',
|
(error as TError).response?.data?.message ?? 'An error occurred while uploading the file.',
|
||||||
);
|
);
|
||||||
if (error?.toString().includes('Unsupported import type')) {
|
if (error?.toString().includes('Unsupported import type') === true) {
|
||||||
showToast({
|
showToast({
|
||||||
message: localize('com_ui_import_conversation_file_type_error'),
|
message: localize('com_ui_import_conversation_file_type_error'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import type { TTermsOfService } from 'librechat-data-provider';
|
||||||
import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
||||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||||
import { useAcceptTermsMutation } from '~/data-provider';
|
import { useAcceptTermsMutation } from '~/data-provider';
|
||||||
|
|
@ -19,7 +21,7 @@ const TermsAndConditionsModal = ({
|
||||||
onDecline: () => void;
|
onDecline: () => void;
|
||||||
title?: string;
|
title?: string;
|
||||||
contentUrl?: string;
|
contentUrl?: string;
|
||||||
modalContent?: string;
|
modalContent?: TTermsOfService['modalContent'];
|
||||||
}) => {
|
}) => {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const { showToast } = useToastContext();
|
const { showToast } = useToastContext();
|
||||||
|
|
@ -49,6 +51,18 @@ const TermsAndConditionsModal = ({
|
||||||
onOpenChange(isOpen);
|
onOpenChange(isOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const content = useMemo(() => {
|
||||||
|
if (typeof modalContent === 'string') {
|
||||||
|
return modalContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(modalContent)) {
|
||||||
|
return modalContent.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}, [modalContent]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OGDialog open={open} onOpenChange={handleOpenChange}>
|
<OGDialog open={open} onOpenChange={handleOpenChange}>
|
||||||
<DialogTemplate
|
<DialogTemplate
|
||||||
|
|
@ -65,8 +79,8 @@ const TermsAndConditionsModal = ({
|
||||||
aria-label={localize('com_ui_terms_and_conditions')}
|
aria-label={localize('com_ui_terms_and_conditions')}
|
||||||
>
|
>
|
||||||
<div className="prose dark:prose-invert w-full max-w-none !text-text-primary">
|
<div className="prose dark:prose-invert w-full max-w-none !text-text-primary">
|
||||||
{modalContent != null && modalContent ? (
|
{content !== '' ? (
|
||||||
<MarkdownLite content={modalContent} />
|
<MarkdownLite content={content} />
|
||||||
) : (
|
) : (
|
||||||
<p>{localize('com_ui_no_terms_content')}</p>
|
<p>{localize('com_ui_no_terms_content')}</p>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
1
client/src/data-provider/Auth/index.ts
Normal file
1
client/src/data-provider/Auth/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './mutations';
|
||||||
65
client/src/data-provider/Auth/mutations.ts
Normal file
65
client/src/data-provider/Auth/mutations.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { useResetRecoilState } from 'recoil';
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { MutationKeys, dataService } from 'librechat-data-provider';
|
||||||
|
import type { UseMutationResult } from '@tanstack/react-query';
|
||||||
|
import type * as t from 'librechat-data-provider';
|
||||||
|
import useClearStates from '~/hooks/Config/useClearStates';
|
||||||
|
import store from '~/store';
|
||||||
|
|
||||||
|
/* login/logout */
|
||||||
|
export const useLogoutUserMutation = (
|
||||||
|
options?: t.LogoutOptions,
|
||||||
|
): UseMutationResult<unknown, unknown, undefined, unknown> => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const clearStates = useClearStates();
|
||||||
|
const resetDefaultPreset = useResetRecoilState(store.defaultPreset);
|
||||||
|
|
||||||
|
return useMutation([MutationKeys.logoutUser], {
|
||||||
|
mutationFn: () => dataService.logout(),
|
||||||
|
...(options || {}),
|
||||||
|
onSuccess: (...args) => {
|
||||||
|
resetDefaultPreset();
|
||||||
|
clearStates();
|
||||||
|
queryClient.removeQueries();
|
||||||
|
options?.onSuccess?.(...args);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLoginUserMutation = (): UseMutationResult<
|
||||||
|
t.TLoginResponse,
|
||||||
|
unknown,
|
||||||
|
t.TLoginUser,
|
||||||
|
unknown
|
||||||
|
> => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const clearStates = useClearStates();
|
||||||
|
const resetDefaultPreset = useResetRecoilState(store.defaultPreset);
|
||||||
|
return useMutation((payload: t.TLoginUser) => dataService.login(payload), {
|
||||||
|
onMutate: () => {
|
||||||
|
resetDefaultPreset();
|
||||||
|
clearStates();
|
||||||
|
queryClient.removeQueries();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/* User */
|
||||||
|
export const useDeleteUserMutation = (
|
||||||
|
options?: t.MutationOptions<unknown, undefined>,
|
||||||
|
): UseMutationResult<unknown, unknown, undefined, unknown> => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const clearStates = useClearStates();
|
||||||
|
const resetDefaultPreset = useResetRecoilState(store.defaultPreset);
|
||||||
|
|
||||||
|
return useMutation([MutationKeys.deleteUser], {
|
||||||
|
mutationFn: () => dataService.deleteUser(),
|
||||||
|
...(options || {}),
|
||||||
|
onSuccess: (...args) => {
|
||||||
|
resetDefaultPreset();
|
||||||
|
clearStates();
|
||||||
|
queryClient.removeQueries();
|
||||||
|
options?.onSuccess?.(...args);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
export * from './Auth';
|
||||||
export * from './Agents';
|
export * from './Agents';
|
||||||
export * from './Files';
|
export * from './Files';
|
||||||
export * from './Tools';
|
export * from './Tools';
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import {
|
import {
|
||||||
Constants,
|
Constants,
|
||||||
LocalStorageKeys,
|
|
||||||
InfiniteCollections,
|
InfiniteCollections,
|
||||||
defaultAssistantsVersion,
|
defaultAssistantsVersion,
|
||||||
ConversationListResponse,
|
ConversationListResponse,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import { useSetRecoilState } from 'recoil';
|
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
|
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
|
||||||
import type * as t from 'librechat-data-provider';
|
import type * as t from 'librechat-data-provider';
|
||||||
|
|
@ -13,7 +11,6 @@ import type { InfiniteData, UseMutationResult } from '@tanstack/react-query';
|
||||||
import useUpdateTagsInConvo from '~/hooks/Conversations/useUpdateTagsInConvo';
|
import useUpdateTagsInConvo from '~/hooks/Conversations/useUpdateTagsInConvo';
|
||||||
import { updateConversationTag } from '~/utils/conversationTags';
|
import { updateConversationTag } from '~/utils/conversationTags';
|
||||||
import { normalizeData } from '~/utils/collection';
|
import { normalizeData } from '~/utils/collection';
|
||||||
import store from '~/store';
|
|
||||||
import {
|
import {
|
||||||
useConversationTagsQuery,
|
useConversationTagsQuery,
|
||||||
useConversationsInfiniteQuery,
|
useConversationsInfiniteQuery,
|
||||||
|
|
@ -692,34 +689,6 @@ export const useDeletePresetMutation = (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/* login/logout */
|
|
||||||
export const useLogoutUserMutation = (
|
|
||||||
options?: t.LogoutOptions,
|
|
||||||
): UseMutationResult<unknown, unknown, undefined, unknown> => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const setDefaultPreset = useSetRecoilState(store.defaultPreset);
|
|
||||||
return useMutation([MutationKeys.logoutUser], {
|
|
||||||
mutationFn: () => dataService.logout(),
|
|
||||||
|
|
||||||
...(options || {}),
|
|
||||||
onSuccess: (...args) => {
|
|
||||||
options?.onSuccess?.(...args);
|
|
||||||
},
|
|
||||||
onMutate: (...args) => {
|
|
||||||
setDefaultPreset(null);
|
|
||||||
queryClient.removeQueries();
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_CONVO_SETUP);
|
|
||||||
localStorage.removeItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_0`);
|
|
||||||
localStorage.removeItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_1`);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_MODEL);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_TOOLS);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.FILES_TO_DELETE);
|
|
||||||
// localStorage.removeItem('lastAssistant');
|
|
||||||
options?.onMutate?.(...args);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Avatar upload */
|
/* Avatar upload */
|
||||||
export const useUploadAvatarMutation = (
|
export const useUploadAvatarMutation = (
|
||||||
options?: t.UploadAvatarOptions,
|
options?: t.UploadAvatarOptions,
|
||||||
|
|
@ -735,32 +704,6 @@ export const useUploadAvatarMutation = (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDeleteUserMutation = (
|
|
||||||
options?: t.MutationOptions<unknown, undefined>,
|
|
||||||
): UseMutationResult<unknown, unknown, undefined, unknown> => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const setDefaultPreset = useSetRecoilState(store.defaultPreset);
|
|
||||||
return useMutation([MutationKeys.deleteUser], {
|
|
||||||
mutationFn: () => dataService.deleteUser(),
|
|
||||||
|
|
||||||
...(options || {}),
|
|
||||||
onSuccess: (...args) => {
|
|
||||||
options?.onSuccess?.(...args);
|
|
||||||
},
|
|
||||||
onMutate: (...args) => {
|
|
||||||
setDefaultPreset(null);
|
|
||||||
queryClient.removeQueries();
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_CONVO_SETUP);
|
|
||||||
localStorage.removeItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_0`);
|
|
||||||
localStorage.removeItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_1`);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_MODEL);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_TOOLS);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.FILES_TO_DELETE);
|
|
||||||
options?.onMutate?.(...args);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Speech to text */
|
/* Speech to text */
|
||||||
export const useSpeechToTextMutation = (
|
export const useSpeechToTextMutation = (
|
||||||
options?: t.SpeechToTextOptions,
|
options?: t.SpeechToTextOptions,
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,10 @@ import {
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { setTokenHeader, SystemRoles } from 'librechat-data-provider';
|
import { setTokenHeader, SystemRoles } from 'librechat-data-provider';
|
||||||
import {
|
import { useGetUserQuery, useRefreshTokenMutation } from 'librechat-data-provider/react-query';
|
||||||
useGetUserQuery,
|
|
||||||
useLoginUserMutation,
|
|
||||||
useRefreshTokenMutation,
|
|
||||||
} from 'librechat-data-provider/react-query';
|
|
||||||
import type { TLoginResponse, TLoginUser } from 'librechat-data-provider';
|
import type { TLoginResponse, TLoginUser } from 'librechat-data-provider';
|
||||||
|
import { useLoginUserMutation, useLogoutUserMutation, useGetRole } from '~/data-provider';
|
||||||
import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common';
|
import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common';
|
||||||
import { useLogoutUserMutation, useGetRole } from '~/data-provider';
|
|
||||||
import useTimeout from './useTimeout';
|
import useTimeout from './useTimeout';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
export { default as useAppStartup } from './useAppStartup';
|
export { default as useAppStartup } from './useAppStartup';
|
||||||
|
export { default as useClearStates } from './useClearStates';
|
||||||
|
|
|
||||||
52
client/src/hooks/Config/useClearStates.ts
Normal file
52
client/src/hooks/Config/useClearStates.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
import { clearLocalStorage } from '~/utils/localStorage';
|
||||||
|
import store from '~/store';
|
||||||
|
|
||||||
|
export default function useClearStates() {
|
||||||
|
const clearConversations = store.useClearConvoState();
|
||||||
|
const clearSubmissions = store.useClearSubmissionState();
|
||||||
|
const clearLatestMessages = store.useClearLatestMessages();
|
||||||
|
|
||||||
|
const clearStates = useRecoilCallback(
|
||||||
|
({ reset, snapshot }) =>
|
||||||
|
async (skipFirst?: boolean) => {
|
||||||
|
await clearSubmissions(skipFirst);
|
||||||
|
await clearConversations(skipFirst);
|
||||||
|
await clearLatestMessages(skipFirst);
|
||||||
|
|
||||||
|
const keys = await snapshot.getPromise(store.conversationKeysAtom);
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
if (skipFirst === true && key === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(store.filesByIndex(key));
|
||||||
|
reset(store.presetByIndex(key));
|
||||||
|
reset(store.textByIndex(key));
|
||||||
|
reset(store.showStopButtonByIndex(key));
|
||||||
|
reset(store.abortScrollFamily(key));
|
||||||
|
reset(store.isSubmittingFamily(key));
|
||||||
|
reset(store.optionSettingsFamily(key));
|
||||||
|
reset(store.showAgentSettingsFamily(key));
|
||||||
|
reset(store.showBingToneSettingFamily(key));
|
||||||
|
reset(store.showPopoverFamily(key));
|
||||||
|
reset(store.showMentionPopoverFamily(key));
|
||||||
|
reset(store.showPlusPopoverFamily(key));
|
||||||
|
reset(store.showPromptsPopoverFamily(key));
|
||||||
|
reset(store.activePromptByIndex(key));
|
||||||
|
reset(store.globalAudioURLFamily(key));
|
||||||
|
reset(store.globalAudioFetchingFamily(key));
|
||||||
|
reset(store.globalAudioPlayingFamily(key));
|
||||||
|
reset(store.activeRunFamily(key));
|
||||||
|
reset(store.audioRunFamily(key));
|
||||||
|
reset(store.messagesSiblingIdxFamily(key.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
clearLocalStorage(skipFirst);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return clearStates;
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import store from '~/store';
|
||||||
|
|
||||||
type TempOverrideType = Record<string, unknown> & {
|
type TempOverrideType = Record<string, unknown> & {
|
||||||
endpointsConfig: TEndpointsConfig;
|
endpointsConfig: TEndpointsConfig;
|
||||||
modelsConfig: TModelsConfig;
|
modelsConfig?: TModelsConfig;
|
||||||
combinedOptions: unknown[];
|
combinedOptions: unknown[];
|
||||||
combined: boolean;
|
combined: boolean;
|
||||||
};
|
};
|
||||||
|
|
@ -38,7 +38,7 @@ export default function useConfigOverride() {
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (overrideQuery.data) {
|
if (overrideQuery.data != null) {
|
||||||
handleOverride(overrideQuery.data);
|
handleOverride(overrideQuery.data);
|
||||||
}
|
}
|
||||||
}, [overrideQuery.data, handleOverride]);
|
}, [overrideQuery.data, handleOverride]);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@ export { default as useSearch } from './useSearch';
|
||||||
export { default as usePresets } from './usePresets';
|
export { default as usePresets } from './usePresets';
|
||||||
export { default as useGetSender } from './useGetSender';
|
export { default as useGetSender } from './useGetSender';
|
||||||
export { default as useDefaultConvo } from './useDefaultConvo';
|
export { default as useDefaultConvo } from './useDefaultConvo';
|
||||||
export { default as useConversation } from './useConversation';
|
|
||||||
export { default as useGenerateConvo } from './useGenerateConvo';
|
export { default as useGenerateConvo } from './useGenerateConvo';
|
||||||
export { default as useConversations } from './useConversations';
|
|
||||||
export { default as useArchiveHandler } from './useArchiveHandler';
|
export { default as useArchiveHandler } from './useArchiveHandler';
|
||||||
export { default as useDebouncedInput } from './useDebouncedInput';
|
export { default as useDebouncedInput } from './useDebouncedInput';
|
||||||
export { default as useBookmarkSuccess } from './useBookmarkSuccess';
|
export { default as useBookmarkSuccess } from './useBookmarkSuccess';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
|
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
|
||||||
import { useArchiveConversationMutation } from '~/data-provider';
|
import { useArchiveConversationMutation } from '~/data-provider';
|
||||||
import useConversations from './useConversations';
|
|
||||||
import { NotificationSeverity } from '~/common';
|
import { NotificationSeverity } from '~/common';
|
||||||
import { useToastContext } from '~/Providers';
|
import { useToastContext } from '~/Providers';
|
||||||
import useLocalize from '../useLocalize';
|
import useLocalize from '../useLocalize';
|
||||||
|
|
@ -16,7 +15,6 @@ export default function useArchiveHandler(
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { showToast } = useToastContext();
|
const { showToast } = useToastContext();
|
||||||
const { newConversation } = useNewConvo();
|
const { newConversation } = useNewConvo();
|
||||||
const { refreshConversations } = useConversations();
|
|
||||||
const { conversationId: currentConvoId } = useParams();
|
const { conversationId: currentConvoId } = useParams();
|
||||||
|
|
||||||
const archiveConvoMutation = useArchiveConversationMutation(conversationId ?? '');
|
const archiveConvoMutation = useArchiveConversationMutation(conversationId ?? '');
|
||||||
|
|
@ -38,7 +36,6 @@ export default function useArchiveHandler(
|
||||||
newConversation();
|
newConversation();
|
||||||
navigate('/c/new', { replace: true });
|
navigate('/c/new', { replace: true });
|
||||||
}
|
}
|
||||||
refreshConversations();
|
|
||||||
retainView();
|
retainView();
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
|
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { QueryKeys } from 'librechat-data-provider';
|
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
|
||||||
import { useSetRecoilState, useResetRecoilState, useRecoilCallback } from 'recoil';
|
|
||||||
import { useGetEndpointsQuery, useGetModelsQuery } from 'librechat-data-provider/react-query';
|
|
||||||
import type {
|
|
||||||
TConversation,
|
|
||||||
TMessagesAtom,
|
|
||||||
TSubmission,
|
|
||||||
TPreset,
|
|
||||||
TModelsConfig,
|
|
||||||
TEndpointsConfig,
|
|
||||||
} from 'librechat-data-provider';
|
|
||||||
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField, logger } from '~/utils';
|
|
||||||
import store from '~/store';
|
|
||||||
|
|
||||||
const useConversation = () => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const setConversation = useSetRecoilState(store.conversation);
|
|
||||||
const resetLatestMessage = useResetRecoilState(store.latestMessage);
|
|
||||||
const setMessages = useSetRecoilState<TMessagesAtom>(store.messages);
|
|
||||||
const setSubmission = useSetRecoilState<TSubmission | null>(store.submission);
|
|
||||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
|
||||||
const modelsQuery = useGetModelsQuery();
|
|
||||||
|
|
||||||
const switchToConversation = useRecoilCallback(
|
|
||||||
() =>
|
|
||||||
async (
|
|
||||||
conversation: TConversation,
|
|
||||||
messages: TMessagesAtom = null,
|
|
||||||
preset: TPreset | null = null,
|
|
||||||
modelsData?: TModelsConfig,
|
|
||||||
) => {
|
|
||||||
const modelsConfig = modelsData ?? modelsQuery.data;
|
|
||||||
const { endpoint = null } = conversation;
|
|
||||||
|
|
||||||
if (endpoint === null) {
|
|
||||||
const defaultEndpoint = getDefaultEndpoint({
|
|
||||||
convoSetup: preset ?? conversation,
|
|
||||||
endpointsConfig,
|
|
||||||
});
|
|
||||||
|
|
||||||
const endpointType = getEndpointField(endpointsConfig, defaultEndpoint, 'type');
|
|
||||||
if (!conversation.endpointType && endpointType) {
|
|
||||||
conversation.endpointType = endpointType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const models = modelsConfig?.[defaultEndpoint] ?? [];
|
|
||||||
conversation = buildDefaultConvo({
|
|
||||||
conversation,
|
|
||||||
lastConversationSetup: preset as TConversation,
|
|
||||||
endpoint: defaultEndpoint,
|
|
||||||
models,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setConversation(conversation);
|
|
||||||
setMessages(messages);
|
|
||||||
setSubmission({} as TSubmission);
|
|
||||||
resetLatestMessage();
|
|
||||||
logger.log(
|
|
||||||
'[useConversation] Switched to conversation and reset Latest Message',
|
|
||||||
conversation,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (conversation.conversationId === 'new' && !modelsData) {
|
|
||||||
queryClient.invalidateQueries([QueryKeys.messages, 'new']);
|
|
||||||
navigate('/c/new');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[endpointsConfig, modelsQuery.data],
|
|
||||||
);
|
|
||||||
|
|
||||||
const newConversation = useCallback(
|
|
||||||
(template = {}, preset?: TPreset, modelsData?: TModelsConfig) => {
|
|
||||||
switchToConversation(
|
|
||||||
{
|
|
||||||
conversationId: 'new',
|
|
||||||
title: 'New Chat',
|
|
||||||
...template,
|
|
||||||
endpoint: null,
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: '',
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
preset,
|
|
||||||
modelsData,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[switchToConversation],
|
|
||||||
);
|
|
||||||
|
|
||||||
const searchPlaceholderConversation = useCallback(() => {
|
|
||||||
switchToConversation(
|
|
||||||
{
|
|
||||||
conversationId: 'search',
|
|
||||||
title: 'Search',
|
|
||||||
endpoint: null,
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: '',
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
}, [switchToConversation]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
switchToConversation,
|
|
||||||
newConversation,
|
|
||||||
searchPlaceholderConversation,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useConversation;
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { useSetRecoilState } from 'recoil';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import store from '~/store';
|
|
||||||
|
|
||||||
const useConversations = () => {
|
|
||||||
const setRefreshConversationsHint = useSetRecoilState(store.refreshConversationsHint);
|
|
||||||
|
|
||||||
const refreshConversations = useCallback(() => {
|
|
||||||
setRefreshConversationsHint((prevState) => prevState + 1);
|
|
||||||
}, [setRefreshConversationsHint]);
|
|
||||||
|
|
||||||
return { refreshConversations };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useConversations;
|
|
||||||
|
|
@ -12,20 +12,29 @@ const useNavigateToConvo = (index = 0) => {
|
||||||
const clearAllConversations = store.useClearConvoState();
|
const clearAllConversations = store.useClearConvoState();
|
||||||
const clearAllLatestMessages = store.useClearLatestMessages(`useNavigateToConvo ${index}`);
|
const clearAllLatestMessages = store.useClearLatestMessages(`useNavigateToConvo ${index}`);
|
||||||
const setSubmission = useSetRecoilState(store.submissionByIndex(index));
|
const setSubmission = useSetRecoilState(store.submissionByIndex(index));
|
||||||
const { setConversation } = store.useCreateConversationAtom(index);
|
const { hasSetConversation, setConversation } = store.useCreateConversationAtom(index);
|
||||||
|
|
||||||
const navigateToConvo = (conversation: TConversation, _resetLatestMessage = true) => {
|
const navigateToConvo = (
|
||||||
|
conversation?: TConversation | null,
|
||||||
|
_resetLatestMessage = true,
|
||||||
|
invalidateMessages = false,
|
||||||
|
) => {
|
||||||
if (!conversation) {
|
if (!conversation) {
|
||||||
console.log('Conversation not provided');
|
console.log('Conversation not provided');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
hasSetConversation.current = true;
|
||||||
setSubmission(null);
|
setSubmission(null);
|
||||||
if (_resetLatestMessage) {
|
if (_resetLatestMessage) {
|
||||||
clearAllLatestMessages();
|
clearAllLatestMessages();
|
||||||
}
|
}
|
||||||
|
if (invalidateMessages && conversation.conversationId != null && conversation.conversationId) {
|
||||||
|
queryClient.setQueryData([QueryKeys.messages, Constants.NEW_CONVO], []);
|
||||||
|
queryClient.invalidateQueries([QueryKeys.messages, conversation.conversationId]);
|
||||||
|
}
|
||||||
|
|
||||||
let convo = { ...conversation };
|
let convo = { ...conversation };
|
||||||
if (!convo?.endpoint) {
|
if (!convo.endpoint) {
|
||||||
/* undefined endpoint edge case */
|
/* undefined endpoint edge case */
|
||||||
const modelsConfig = queryClient.getQueryData<TModelsConfig>([QueryKeys.models]);
|
const modelsConfig = queryClient.getQueryData<TModelsConfig>([QueryKeys.models]);
|
||||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||||
|
|
@ -53,9 +62,17 @@ const useNavigateToConvo = (index = 0) => {
|
||||||
navigate(`/c/${convo.conversationId ?? Constants.NEW_CONVO}`);
|
navigate(`/c/${convo.conversationId ?? Constants.NEW_CONVO}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateWithLastTools = (conversation: TConversation, _resetLatestMessage?: boolean) => {
|
const navigateWithLastTools = (
|
||||||
|
conversation?: TConversation | null,
|
||||||
|
_resetLatestMessage?: boolean,
|
||||||
|
invalidateMessages?: boolean,
|
||||||
|
) => {
|
||||||
|
if (!conversation) {
|
||||||
|
console.log('Conversation not provided');
|
||||||
|
return;
|
||||||
|
}
|
||||||
// set conversation to the new conversation
|
// set conversation to the new conversation
|
||||||
if (conversation?.endpoint === EModelEndpoint.gptPlugins) {
|
if (conversation.endpoint === EModelEndpoint.gptPlugins) {
|
||||||
let lastSelectedTools = [];
|
let lastSelectedTools = [];
|
||||||
try {
|
try {
|
||||||
lastSelectedTools =
|
lastSelectedTools =
|
||||||
|
|
@ -63,15 +80,17 @@ const useNavigateToConvo = (index = 0) => {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// console.error(e);
|
// console.error(e);
|
||||||
}
|
}
|
||||||
|
const hasTools = (conversation.tools?.length ?? 0) > 0;
|
||||||
navigateToConvo(
|
navigateToConvo(
|
||||||
{
|
{
|
||||||
...conversation,
|
...conversation,
|
||||||
tools: conversation?.tools?.length ? conversation?.tools : lastSelectedTools,
|
tools: hasTools ? conversation.tools : lastSelectedTools,
|
||||||
},
|
},
|
||||||
_resetLatestMessage,
|
_resetLatestMessage,
|
||||||
|
invalidateMessages,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
navigateToConvo(conversation, _resetLatestMessage);
|
navigateToConvo(conversation, _resetLatestMessage, invalidateMessages);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,23 @@ import { useGetSearchEnabledQuery } from 'librechat-data-provider/react-query';
|
||||||
import type { UseInfiniteQueryResult } from '@tanstack/react-query';
|
import type { UseInfiniteQueryResult } from '@tanstack/react-query';
|
||||||
import type { ConversationListResponse } from 'librechat-data-provider';
|
import type { ConversationListResponse } from 'librechat-data-provider';
|
||||||
import { useSearchInfiniteQuery } from '~/data-provider';
|
import { useSearchInfiniteQuery } from '~/data-provider';
|
||||||
import useConversation from './useConversation';
|
import useNewConvo from '~/hooks/useNewConvo';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
export default function useSearchMessages({ isAuthenticated }: { isAuthenticated: boolean }) {
|
export default function useSearchMessages({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [pageNumber, setPageNumber] = useState(1);
|
const [pageNumber, setPageNumber] = useState(1);
|
||||||
const { searchPlaceholderConversation } = useConversation();
|
const { switchToConversation } = useNewConvo();
|
||||||
|
const searchPlaceholderConversation = useCallback(() => {
|
||||||
|
switchToConversation({
|
||||||
|
conversationId: 'search',
|
||||||
|
title: 'Search',
|
||||||
|
endpoint: null,
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
});
|
||||||
|
}, [switchToConversation]);
|
||||||
|
|
||||||
const searchQuery = useRecoilValue(store.searchQuery);
|
const searchQuery = useRecoilValue(store.searchQuery);
|
||||||
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
|
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export default function useSSE(
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (submission === null || Object.keys(submission).length === 0) {
|
if (submission == null || Object.keys(submission).length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,11 @@ const useNewConvo = (index = 0) => {
|
||||||
) ?? assistants[0]?.id;
|
) ?? assistants[0]?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentAssistantId && isAssistantEndpoint && conversation.conversationId === Constants.NEW_CONVO) {
|
if (
|
||||||
|
currentAssistantId &&
|
||||||
|
isAssistantEndpoint &&
|
||||||
|
conversation.conversationId === Constants.NEW_CONVO
|
||||||
|
) {
|
||||||
const assistant = assistants.find((asst) => asst.id === currentAssistantId);
|
const assistant = assistants.find((asst) => asst.id === currentAssistantId);
|
||||||
conversation.model = assistant?.model;
|
conversation.model = assistant?.model;
|
||||||
updateLastSelectedModel({
|
updateLastSelectedModel({
|
||||||
|
|
@ -168,7 +172,7 @@ const useNewConvo = (index = 0) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const newConversation = useCallback(
|
const newConversation = useCallback(
|
||||||
({
|
function createNewConvo({
|
||||||
template: _template = {},
|
template: _template = {},
|
||||||
preset: _preset,
|
preset: _preset,
|
||||||
modelsData,
|
modelsData,
|
||||||
|
|
@ -182,7 +186,7 @@ const useNewConvo = (index = 0) => {
|
||||||
buildDefault?: boolean;
|
buildDefault?: boolean;
|
||||||
keepLatestMessage?: boolean;
|
keepLatestMessage?: boolean;
|
||||||
keepAddedConvos?: boolean;
|
keepAddedConvos?: boolean;
|
||||||
} = {}) => {
|
} = {}) {
|
||||||
pauseGlobalAudio();
|
pauseGlobalAudio();
|
||||||
|
|
||||||
const templateConvoId = _template.conversationId ?? '';
|
const templateConvoId = _template.conversationId ?? '';
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { Constants, EModelEndpoint } from 'librechat-data-provider';
|
import { Constants, EModelEndpoint } from 'librechat-data-provider';
|
||||||
import {
|
import {
|
||||||
|
|
@ -25,9 +25,8 @@ export default function ChatRoute() {
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const { conversationId = '' } = useParams();
|
const { conversationId = '' } = useParams();
|
||||||
|
|
||||||
const { conversation } = store.useCreateConversationAtom(index);
|
const { hasSetConversation, conversation } = store.useCreateConversationAtom(index);
|
||||||
const { newConversation } = useNewConvo();
|
const { newConversation } = useNewConvo();
|
||||||
const hasSetConversation = useRef(false);
|
|
||||||
|
|
||||||
const modelsQuery = useGetModelsQuery({
|
const modelsQuery = useGetModelsQuery({
|
||||||
enabled: isAuthenticated,
|
enabled: isAuthenticated,
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Outlet, useNavigate } from 'react-router-dom';
|
import { Outlet, useNavigate } from 'react-router-dom';
|
||||||
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
|
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||||
import { useUserTermsQuery } from '~/data-provider';
|
|
||||||
|
|
||||||
import type { ContextType } from '~/common';
|
import type { ContextType } from '~/common';
|
||||||
import { AgentsMapContext, AssistantsMapContext, FileMapContext, SearchContext } from '~/Providers';
|
import {
|
||||||
|
AgentsMapContext,
|
||||||
|
AssistantsMapContext,
|
||||||
|
FileMapContext,
|
||||||
|
SearchContext,
|
||||||
|
SetConvoProvider,
|
||||||
|
} from '~/Providers';
|
||||||
import { useAuthContext, useAssistantsMap, useAgentsMap, useFileMap, useSearch } from '~/hooks';
|
import { useAuthContext, useAssistantsMap, useAgentsMap, useFileMap, useSearch } from '~/hooks';
|
||||||
import { Nav, MobileNav } from '~/components/Nav';
|
|
||||||
import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal';
|
import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal';
|
||||||
|
import { useUserTermsQuery } from '~/data-provider';
|
||||||
|
import { Nav, MobileNav } from '~/components/Nav';
|
||||||
import { Banner } from '~/components/Banners';
|
import { Banner } from '~/components/Banners';
|
||||||
|
|
||||||
export default function Root() {
|
export default function Root() {
|
||||||
const { isAuthenticated, logout } = useAuthContext();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [showTerms, setShowTerms] = useState(false);
|
||||||
|
const [bannerHeight, setBannerHeight] = useState(0);
|
||||||
const [navVisible, setNavVisible] = useState(() => {
|
const [navVisible, setNavVisible] = useState(() => {
|
||||||
const savedNavVisible = localStorage.getItem('navVisible');
|
const savedNavVisible = localStorage.getItem('navVisible');
|
||||||
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : true;
|
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : true;
|
||||||
});
|
});
|
||||||
const [bannerHeight, setBannerHeight] = useState(0);
|
|
||||||
|
|
||||||
const search = useSearch({ isAuthenticated });
|
const { isAuthenticated, logout } = useAuthContext();
|
||||||
const fileMap = useFileMap({ isAuthenticated });
|
|
||||||
const assistantsMap = useAssistantsMap({ isAuthenticated });
|
const assistantsMap = useAssistantsMap({ isAuthenticated });
|
||||||
const agentsMap = useAgentsMap({ isAuthenticated });
|
const agentsMap = useAgentsMap({ isAuthenticated });
|
||||||
|
const fileMap = useFileMap({ isAuthenticated });
|
||||||
|
const search = useSearch({ isAuthenticated });
|
||||||
|
|
||||||
const [showTerms, setShowTerms] = useState(false);
|
|
||||||
const { data: config } = useGetStartupConfig();
|
const { data: config } = useGetStartupConfig();
|
||||||
const { data: termsData } = useUserTermsQuery({
|
const { data: termsData } = useUserTermsQuery({
|
||||||
enabled: isAuthenticated && config?.interface?.termsOfService?.modalAcceptance === true,
|
enabled: isAuthenticated && config?.interface?.termsOfService?.modalAcceptance === true,
|
||||||
|
|
@ -51,33 +56,35 @@ export default function Root() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SearchContext.Provider value={search}>
|
<SetConvoProvider>
|
||||||
<FileMapContext.Provider value={fileMap}>
|
<SearchContext.Provider value={search}>
|
||||||
<AssistantsMapContext.Provider value={assistantsMap}>
|
<FileMapContext.Provider value={fileMap}>
|
||||||
<AgentsMapContext.Provider value={agentsMap}>
|
<AssistantsMapContext.Provider value={assistantsMap}>
|
||||||
<Banner onHeightChange={setBannerHeight} />
|
<AgentsMapContext.Provider value={agentsMap}>
|
||||||
<div className="flex" style={{ height: `calc(100dvh - ${bannerHeight}px)` }}>
|
<Banner onHeightChange={setBannerHeight} />
|
||||||
<div className="relative z-0 flex h-full w-full overflow-hidden">
|
<div className="flex" style={{ height: `calc(100dvh - ${bannerHeight}px)` }}>
|
||||||
<Nav navVisible={navVisible} setNavVisible={setNavVisible} />
|
<div className="relative z-0 flex h-full w-full overflow-hidden">
|
||||||
<div className="relative flex h-full max-w-full flex-1 flex-col overflow-hidden">
|
<Nav navVisible={navVisible} setNavVisible={setNavVisible} />
|
||||||
<MobileNav setNavVisible={setNavVisible} />
|
<div className="relative flex h-full max-w-full flex-1 flex-col overflow-hidden">
|
||||||
<Outlet context={{ navVisible, setNavVisible } satisfies ContextType} />
|
<MobileNav setNavVisible={setNavVisible} />
|
||||||
|
<Outlet context={{ navVisible, setNavVisible } satisfies ContextType} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AgentsMapContext.Provider>
|
||||||
</AgentsMapContext.Provider>
|
{config?.interface?.termsOfService?.modalAcceptance === true && (
|
||||||
{config?.interface?.termsOfService?.modalAcceptance === true && (
|
<TermsAndConditionsModal
|
||||||
<TermsAndConditionsModal
|
open={showTerms}
|
||||||
open={showTerms}
|
onOpenChange={setShowTerms}
|
||||||
onOpenChange={setShowTerms}
|
onAccept={handleAcceptTerms}
|
||||||
onAccept={handleAcceptTerms}
|
onDecline={handleDeclineTerms}
|
||||||
onDecline={handleDeclineTerms}
|
title={config.interface.termsOfService.modalTitle}
|
||||||
title={config.interface.termsOfService.modalTitle}
|
modalContent={config.interface.termsOfService.modalContent}
|
||||||
modalContent={config.interface.termsOfService.modalContent}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
</AssistantsMapContext.Provider>
|
||||||
</AssistantsMapContext.Provider>
|
</FileMapContext.Provider>
|
||||||
</FileMapContext.Provider>
|
</SearchContext.Provider>
|
||||||
</SearchContext.Provider>
|
</SetConvoProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { atomWithLocalStorage } from './utils';
|
|
||||||
|
|
||||||
const hideBannerHint = atomWithLocalStorage('hideBannerHint', [] as string[]);
|
|
||||||
|
|
||||||
export default { hideBannerHint };
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { atom, selector, atomFamily } from 'recoil';
|
|
||||||
import { TConversation, TMessagesAtom, TMessage, TAttachment } from 'librechat-data-provider';
|
|
||||||
import { buildTree } from '~/utils';
|
|
||||||
|
|
||||||
const conversation = atom<TConversation | null>({
|
|
||||||
key: 'conversation',
|
|
||||||
default: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
// current messages of the conversation, must be an array
|
|
||||||
// sample structure
|
|
||||||
// [{text, sender, messageId, parentMessageId, isCreatedByUser}]
|
|
||||||
const messages = atom<TMessagesAtom>({
|
|
||||||
key: 'messages',
|
|
||||||
default: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const messagesTree = selector({
|
|
||||||
key: 'messagesTree',
|
|
||||||
get: ({ get }) => {
|
|
||||||
return buildTree({ messages: get(messages) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const messageAttachmentsMap = atom<Record<string, TAttachment[] | undefined>>({
|
|
||||||
key: 'messageAttachmentsMap',
|
|
||||||
default: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
const latestMessage = atom<TMessage | null>({
|
|
||||||
key: 'latestMessage',
|
|
||||||
default: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const messagesSiblingIdxFamily = atomFamily<number, string | null | undefined>({
|
|
||||||
key: 'messagesSiblingIdx',
|
|
||||||
default: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default {
|
|
||||||
messages,
|
|
||||||
conversation,
|
|
||||||
messagesTree,
|
|
||||||
latestMessage,
|
|
||||||
messageAttachmentsMap,
|
|
||||||
messagesSiblingIdxFamily,
|
|
||||||
};
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
const refreshConversationsHint = atom<number>({
|
|
||||||
key: 'refreshConversationsHint',
|
|
||||||
default: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default { refreshConversationsHint };
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
atom,
|
atom,
|
||||||
selector,
|
selector,
|
||||||
|
|
@ -12,8 +13,8 @@ import {
|
||||||
import { LocalStorageKeys, Constants } from 'librechat-data-provider';
|
import { LocalStorageKeys, Constants } from 'librechat-data-provider';
|
||||||
import type { TMessage, TPreset, TConversation, TSubmission } from 'librechat-data-provider';
|
import type { TMessage, TPreset, TConversation, TSubmission } from 'librechat-data-provider';
|
||||||
import type { TOptionSettings, ExtendedFile } from '~/common';
|
import type { TOptionSettings, ExtendedFile } from '~/common';
|
||||||
|
import { useSetConvoContext } from '~/Providers/SetConvoContext';
|
||||||
import { storeEndpointSettings, logger } from '~/utils';
|
import { storeEndpointSettings, logger } from '~/utils';
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
const latestMessageKeysAtom = atom<(string | number)[]>({
|
const latestMessageKeysAtom = atom<(string | number)[]>({
|
||||||
key: 'latestMessageKeys',
|
key: 'latestMessageKeys',
|
||||||
|
|
@ -75,16 +76,16 @@ const conversationByIndex = atomFamily<TConversation | null, string | number>({
|
||||||
onSet(async (newValue) => {
|
onSet(async (newValue) => {
|
||||||
const index = Number(node.key.split('__')[1]);
|
const index = Number(node.key.split('__')[1]);
|
||||||
logger.log('conversation', 'Setting conversation:', { index, newValue });
|
logger.log('conversation', 'Setting conversation:', { index, newValue });
|
||||||
if (newValue?.assistant_id) {
|
if (newValue?.assistant_id != null && newValue.assistant_id) {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
`${LocalStorageKeys.ASST_ID_PREFIX}${index}${newValue.endpoint}`,
|
`${LocalStorageKeys.ASST_ID_PREFIX}${index}${newValue.endpoint}`,
|
||||||
newValue.assistant_id,
|
newValue.assistant_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (newValue?.agent_id) {
|
if (newValue?.agent_id != null && newValue.agent_id) {
|
||||||
localStorage.setItem(`${LocalStorageKeys.AGENT_ID_PREFIX}${index}`, newValue.agent_id);
|
localStorage.setItem(`${LocalStorageKeys.AGENT_ID_PREFIX}${index}`, newValue.agent_id);
|
||||||
}
|
}
|
||||||
if (newValue?.spec) {
|
if (newValue?.spec != null && newValue.spec) {
|
||||||
localStorage.setItem(LocalStorageKeys.LAST_SPEC, newValue.spec);
|
localStorage.setItem(LocalStorageKeys.LAST_SPEC, newValue.spec);
|
||||||
}
|
}
|
||||||
if (newValue?.tools && Array.isArray(newValue.tools)) {
|
if (newValue?.tools && Array.isArray(newValue.tools)) {
|
||||||
|
|
@ -238,7 +239,13 @@ const audioRunFamily = atomFamily<string | null, string | number | null>({
|
||||||
default: null,
|
default: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const messagesSiblingIdxFamily = atomFamily<number, string | null | undefined>({
|
||||||
|
key: 'messagesSiblingIdx',
|
||||||
|
default: 0,
|
||||||
|
});
|
||||||
|
|
||||||
function useCreateConversationAtom(key: string | number) {
|
function useCreateConversationAtom(key: string | number) {
|
||||||
|
const hasSetConversation = useSetConvoContext();
|
||||||
const [keys, setKeys] = useRecoilState(conversationKeysAtom);
|
const [keys, setKeys] = useRecoilState(conversationKeysAtom);
|
||||||
const setConversation = useSetRecoilState(conversationByIndex(key));
|
const setConversation = useSetRecoilState(conversationByIndex(key));
|
||||||
const conversation = useRecoilValue(conversationByIndex(key));
|
const conversation = useRecoilValue(conversationByIndex(key));
|
||||||
|
|
@ -249,7 +256,7 @@ function useCreateConversationAtom(key: string | number) {
|
||||||
}
|
}
|
||||||
}, [key, keys, setKeys]);
|
}, [key, keys, setKeys]);
|
||||||
|
|
||||||
return { conversation, setConversation };
|
return { hasSetConversation, conversation, setConversation };
|
||||||
}
|
}
|
||||||
|
|
||||||
function useClearConvoState() {
|
function useClearConvoState() {
|
||||||
|
|
@ -260,7 +267,7 @@ function useClearConvoState() {
|
||||||
const conversationKeys = await snapshot.getPromise(conversationKeysAtom);
|
const conversationKeys = await snapshot.getPromise(conversationKeysAtom);
|
||||||
|
|
||||||
for (const conversationKey of conversationKeys) {
|
for (const conversationKey of conversationKeys) {
|
||||||
if (skipFirst && conversationKey == 0) {
|
if (skipFirst === true && conversationKey == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +305,7 @@ function useClearSubmissionState() {
|
||||||
logger.log('submissionKeys', submissionKeys);
|
logger.log('submissionKeys', submissionKeys);
|
||||||
|
|
||||||
for (const key of submissionKeys) {
|
for (const key of submissionKeys) {
|
||||||
if (skipFirst && key == 0) {
|
if (skipFirst === true && key == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,12 +327,12 @@ function useClearLatestMessages(context?: string) {
|
||||||
async (skipFirst?: boolean) => {
|
async (skipFirst?: boolean) => {
|
||||||
const latestMessageKeys = await snapshot.getPromise(latestMessageKeysSelector);
|
const latestMessageKeys = await snapshot.getPromise(latestMessageKeysSelector);
|
||||||
logger.log('[clearAllLatestMessages] latestMessageKeys', latestMessageKeys);
|
logger.log('[clearAllLatestMessages] latestMessageKeys', latestMessageKeys);
|
||||||
if (context) {
|
if (context != null && context) {
|
||||||
logger.log(`[clearAllLatestMessages] context: ${context}`);
|
logger.log(`[clearAllLatestMessages] context: ${context}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of latestMessageKeys) {
|
for (const key of latestMessageKeys) {
|
||||||
if (skipFirst && key == 0) {
|
if (skipFirst === true && key == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -367,6 +374,7 @@ const updateConversationSelector = selectorFamily({
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
conversationKeysAtom,
|
||||||
conversationByIndex,
|
conversationByIndex,
|
||||||
filesByIndex,
|
filesByIndex,
|
||||||
presetByIndex,
|
presetByIndex,
|
||||||
|
|
@ -380,6 +388,7 @@ export default {
|
||||||
showBingToneSettingFamily,
|
showBingToneSettingFamily,
|
||||||
showPopoverFamily,
|
showPopoverFamily,
|
||||||
latestMessageFamily,
|
latestMessageFamily,
|
||||||
|
messagesSiblingIdxFamily,
|
||||||
allConversationsSelector,
|
allConversationsSelector,
|
||||||
conversationByKeySelector,
|
conversationByKeySelector,
|
||||||
useClearConvoState,
|
useClearConvoState,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import * as artifacts from './artifacts';
|
import * as artifacts from './artifacts';
|
||||||
import conversation from './conversation';
|
|
||||||
import conversations from './conversations';
|
|
||||||
import families from './families';
|
import families from './families';
|
||||||
import endpoints from './endpoints';
|
import endpoints from './endpoints';
|
||||||
import user from './user';
|
import user from './user';
|
||||||
|
|
@ -12,12 +10,10 @@ import preset from './preset';
|
||||||
import prompts from './prompts';
|
import prompts from './prompts';
|
||||||
import lang from './language';
|
import lang from './language';
|
||||||
import settings from './settings';
|
import settings from './settings';
|
||||||
import banner from './banner';
|
import misc from './misc';
|
||||||
export default {
|
export default {
|
||||||
...artifacts,
|
...artifacts,
|
||||||
...families,
|
...families,
|
||||||
...conversation,
|
|
||||||
...conversations,
|
|
||||||
...endpoints,
|
...endpoints,
|
||||||
...user,
|
...user,
|
||||||
...text,
|
...text,
|
||||||
|
|
@ -28,5 +24,5 @@ export default {
|
||||||
...preset,
|
...preset,
|
||||||
...lang,
|
...lang,
|
||||||
...settings,
|
...settings,
|
||||||
...banner,
|
...misc,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
12
client/src/store/misc.ts
Normal file
12
client/src/store/misc.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { atom } from 'recoil';
|
||||||
|
import { TAttachment } from 'librechat-data-provider';
|
||||||
|
import { atomWithLocalStorage } from './utils';
|
||||||
|
|
||||||
|
const hideBannerHint = atomWithLocalStorage('hideBannerHint', [] as string[]);
|
||||||
|
|
||||||
|
const messageAttachmentsMap = atom<Record<string, TAttachment[] | undefined>>({
|
||||||
|
key: 'messageAttachmentsMap',
|
||||||
|
default: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default { hideBannerHint, messageAttachmentsMap };
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
import { atom } from 'recoil';
|
import { atom } from 'recoil';
|
||||||
import { TPreset } from 'librechat-data-provider';
|
import { TPreset } from 'librechat-data-provider';
|
||||||
|
|
||||||
const presets = atom<TPreset[]>({
|
|
||||||
key: 'presets',
|
|
||||||
default: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const preset = atom<TPreset | null>({
|
|
||||||
key: 'preset',
|
|
||||||
default: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const defaultPreset = atom<TPreset | null>({
|
const defaultPreset = atom<TPreset | null>({
|
||||||
key: 'defaultPreset',
|
key: 'defaultPreset',
|
||||||
default: null,
|
default: null,
|
||||||
|
|
@ -22,8 +12,6 @@ const presetModalVisible = atom<boolean>({
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
preset,
|
|
||||||
presets,
|
|
||||||
defaultPreset,
|
defaultPreset,
|
||||||
presetModalVisible,
|
presetModalVisible,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {
|
||||||
isAgentsEndpoint,
|
isAgentsEndpoint,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import type { TConversation } from 'librechat-data-provider';
|
import type { TConversation } from 'librechat-data-provider';
|
||||||
import getLocalStorageItems from './getLocalStorageItems';
|
import { getLocalStorageItems } from './localStorage';
|
||||||
|
|
||||||
const buildDefaultConvo = ({
|
const buildDefaultConvo = ({
|
||||||
conversation,
|
conversation,
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,20 @@ import type {
|
||||||
TEndpointsConfig,
|
TEndpointsConfig,
|
||||||
EModelEndpoint,
|
EModelEndpoint,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import getLocalStorageItems from './getLocalStorageItems';
|
import { getLocalStorageItems } from './localStorage';
|
||||||
import { mapEndpoints } from './endpoints';
|
import { mapEndpoints } from './endpoints';
|
||||||
|
|
||||||
type TConvoSetup = Partial<TPreset> | Partial<TConversation>;
|
type TConvoSetup = Partial<TPreset> | Partial<TConversation>;
|
||||||
|
|
||||||
type TDefaultEndpoint = { convoSetup: TConvoSetup; endpointsConfig: TEndpointsConfig };
|
type TDefaultEndpoint = { convoSetup: TConvoSetup; endpointsConfig: TEndpointsConfig };
|
||||||
|
|
||||||
const getEndpointFromSetup = (convoSetup: TConvoSetup, endpointsConfig: TEndpointsConfig) => {
|
const getEndpointFromSetup = (
|
||||||
const { endpoint: targetEndpoint } = convoSetup || {};
|
convoSetup: TConvoSetup | null,
|
||||||
if (targetEndpoint && endpointsConfig?.[targetEndpoint ?? '']) {
|
endpointsConfig: TEndpointsConfig,
|
||||||
|
) => {
|
||||||
|
let { endpoint: targetEndpoint = '' } = convoSetup || {};
|
||||||
|
targetEndpoint = targetEndpoint ?? '';
|
||||||
|
if (targetEndpoint && endpointsConfig?.[targetEndpoint]) {
|
||||||
return targetEndpoint;
|
return targetEndpoint;
|
||||||
} else if (targetEndpoint) {
|
} else if (targetEndpoint) {
|
||||||
console.warn(`Illegal target endpoint ${targetEndpoint} ${endpointsConfig}`);
|
console.warn(`Illegal target endpoint ${targetEndpoint} ${endpointsConfig}`);
|
||||||
|
|
@ -24,8 +28,8 @@ const getEndpointFromSetup = (convoSetup: TConvoSetup, endpointsConfig: TEndpoin
|
||||||
const getEndpointFromLocalStorage = (endpointsConfig: TEndpointsConfig) => {
|
const getEndpointFromLocalStorage = (endpointsConfig: TEndpointsConfig) => {
|
||||||
try {
|
try {
|
||||||
const { lastConversationSetup } = getLocalStorageItems();
|
const { lastConversationSetup } = getLocalStorageItems();
|
||||||
const { endpoint } = lastConversationSetup;
|
const { endpoint } = lastConversationSetup ?? { endpoint: null };
|
||||||
const isDefaultConfig = Object.values(endpointsConfig ?? {})?.every((value) => !value);
|
const isDefaultConfig = Object.values(endpointsConfig ?? {}).every((value) => !value);
|
||||||
|
|
||||||
if (isDefaultConfig && endpoint) {
|
if (isDefaultConfig && endpoint) {
|
||||||
return endpoint;
|
return endpoint;
|
||||||
|
|
@ -35,7 +39,7 @@ const getEndpointFromLocalStorage = (endpointsConfig: TEndpointsConfig) => {
|
||||||
return endpoint;
|
return endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoint && endpointsConfig?.[endpoint ?? ''] ? endpoint : null;
|
return endpoint && endpointsConfig?.[endpoint] != null ? endpoint : null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -47,9 +51,12 @@ const getDefinedEndpoint = (endpointsConfig: TEndpointsConfig) => {
|
||||||
return endpoints.find((e) => Object.hasOwn(endpointsConfig ?? {}, e));
|
return endpoints.find((e) => Object.hasOwn(endpointsConfig ?? {}, e));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDefaultEndpoint = ({ convoSetup, endpointsConfig }: TDefaultEndpoint): EModelEndpoint => {
|
const getDefaultEndpoint = ({
|
||||||
|
convoSetup,
|
||||||
|
endpointsConfig,
|
||||||
|
}: TDefaultEndpoint): EModelEndpoint | string | undefined => {
|
||||||
return (
|
return (
|
||||||
getEndpointFromSetup(convoSetup, endpointsConfig) ||
|
(getEndpointFromSetup(convoSetup, endpointsConfig) ?? '') ||
|
||||||
getEndpointFromLocalStorage(endpointsConfig) ||
|
getEndpointFromLocalStorage(endpointsConfig) ||
|
||||||
getDefinedEndpoint(endpointsConfig)
|
getDefinedEndpoint(endpointsConfig)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export * from './messages';
|
||||||
export * from './languages';
|
export * from './languages';
|
||||||
export * from './endpoints';
|
export * from './endpoints';
|
||||||
export * from './sharedLink';
|
export * from './sharedLink';
|
||||||
|
export * from './localStorage';
|
||||||
export * from './promptGroups';
|
export * from './promptGroups';
|
||||||
export { default as cn } from './cn';
|
export { default as cn } from './cn';
|
||||||
export { default as logger } from './logger';
|
export { default as logger } from './logger';
|
||||||
|
|
@ -21,7 +22,6 @@ export { default as cleanupPreset } from './cleanupPreset';
|
||||||
export { default as validateIframe } from './validateIframe';
|
export { default as validateIframe } from './validateIframe';
|
||||||
export { default as buildDefaultConvo } from './buildDefaultConvo';
|
export { default as buildDefaultConvo } from './buildDefaultConvo';
|
||||||
export { default as getDefaultEndpoint } from './getDefaultEndpoint';
|
export { default as getDefaultEndpoint } from './getDefaultEndpoint';
|
||||||
export { default as getLocalStorageItems } from './getLocalStorageItems';
|
|
||||||
|
|
||||||
export const languages = [
|
export const languages = [
|
||||||
'java',
|
'java',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { LocalStorageKeys, TConversation } from 'librechat-data-provider';
|
import { LocalStorageKeys, TConversation } from 'librechat-data-provider';
|
||||||
|
|
||||||
export default function getLocalStorageItems() {
|
export function getLocalStorageItems() {
|
||||||
const items = {
|
const items = {
|
||||||
lastSelectedModel: localStorage.getItem(LocalStorageKeys.LAST_MODEL) ?? '',
|
lastSelectedModel: localStorage.getItem(LocalStorageKeys.LAST_MODEL) ?? '',
|
||||||
lastSelectedTools: localStorage.getItem(LocalStorageKeys.LAST_TOOLS) ?? '',
|
lastSelectedTools: localStorage.getItem(LocalStorageKeys.LAST_TOOLS) ?? '',
|
||||||
|
|
@ -23,3 +23,23 @@ export default function getLocalStorageItems() {
|
||||||
lastConversationSetup,
|
lastConversationSetup,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clearLocalStorage(skipFirst?: boolean) {
|
||||||
|
const keys = Object.keys(localStorage);
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (skipFirst === true && key.endsWith('0')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
key.startsWith(LocalStorageKeys.ASST_ID_PREFIX) ||
|
||||||
|
key.startsWith(LocalStorageKeys.AGENT_ID_PREFIX) ||
|
||||||
|
key.startsWith(LocalStorageKeys.LAST_CONVO_SETUP) ||
|
||||||
|
key === LocalStorageKeys.LAST_SPEC ||
|
||||||
|
key === LocalStorageKeys.LAST_TOOLS ||
|
||||||
|
key === LocalStorageKeys.LAST_MODEL ||
|
||||||
|
key === LocalStorageKeys.FILES_TO_DELETE
|
||||||
|
) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -427,6 +427,16 @@ export enum EImageOutputType {
|
||||||
JPEG = 'jpeg',
|
JPEG = 'jpeg',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const termsOfServiceSchema = z.object({
|
||||||
|
externalUrl: z.string().optional(),
|
||||||
|
openNewTab: z.boolean().optional(),
|
||||||
|
modalAcceptance: z.boolean().optional(),
|
||||||
|
modalTitle: z.string().optional(),
|
||||||
|
modalContent: z.string().or(z.array(z.string())).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TTermsOfService = z.infer<typeof termsOfServiceSchema>;
|
||||||
|
|
||||||
export const intefaceSchema = z
|
export const intefaceSchema = z
|
||||||
.object({
|
.object({
|
||||||
privacyPolicy: z
|
privacyPolicy: z
|
||||||
|
|
@ -435,15 +445,7 @@ export const intefaceSchema = z
|
||||||
openNewTab: z.boolean().optional(),
|
openNewTab: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
termsOfService: z
|
termsOfService: termsOfServiceSchema.optional(),
|
||||||
.object({
|
|
||||||
externalUrl: z.string().optional(),
|
|
||||||
openNewTab: z.boolean().optional(),
|
|
||||||
modalAcceptance: z.boolean().optional(),
|
|
||||||
modalTitle: z.string().optional(),
|
|
||||||
modalContent: z.string().or(z.array(z.string())).optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
endpointsMenu: z.boolean().optional(),
|
endpointsMenu: z.boolean().optional(),
|
||||||
modelSelect: z.boolean().optional(),
|
modelSelect: z.boolean().optional(),
|
||||||
parameters: z.boolean().optional(),
|
parameters: z.boolean().optional(),
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ export const parseConvo = ({
|
||||||
possibleValues,
|
possibleValues,
|
||||||
}: {
|
}: {
|
||||||
endpoint: EModelEndpoint;
|
endpoint: EModelEndpoint;
|
||||||
endpointType?: EModelEndpoint;
|
endpointType?: EModelEndpoint | null;
|
||||||
conversation: Partial<s.TConversation | s.TPreset> | null;
|
conversation: Partial<s.TConversation | s.TPreset> | null;
|
||||||
possibleValues?: TPossibleValues;
|
possibleValues?: TPossibleValues;
|
||||||
// TODO: POC for default schema
|
// TODO: POC for default schema
|
||||||
|
|
@ -338,7 +338,7 @@ export const parseCompactConvo = ({
|
||||||
possibleValues,
|
possibleValues,
|
||||||
}: {
|
}: {
|
||||||
endpoint?: EModelEndpoint;
|
endpoint?: EModelEndpoint;
|
||||||
endpointType?: EModelEndpoint;
|
endpointType?: EModelEndpoint | null;
|
||||||
conversation: Partial<s.TConversation | s.TPreset>;
|
conversation: Partial<s.TConversation | s.TPreset>;
|
||||||
possibleValues?: TPossibleValues;
|
possibleValues?: TPossibleValues;
|
||||||
// TODO: POC for default schema
|
// TODO: POC for default schema
|
||||||
|
|
@ -348,7 +348,7 @@ export const parseCompactConvo = ({
|
||||||
throw new Error(`undefined endpoint: ${endpoint}`);
|
throw new Error(`undefined endpoint: ${endpoint}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema = compactEndpointSchemas[endpoint];
|
let schema = compactEndpointSchemas[endpoint] as CompactEndpointSchema | undefined;
|
||||||
|
|
||||||
if (!schema && !endpointType) {
|
if (!schema && !endpointType) {
|
||||||
throw new Error(`Unknown endpoint: ${endpoint}`);
|
throw new Error(`Unknown endpoint: ${endpoint}`);
|
||||||
|
|
@ -356,7 +356,11 @@ export const parseCompactConvo = ({
|
||||||
schema = compactEndpointSchemas[endpointType];
|
schema = compactEndpointSchemas[endpointType];
|
||||||
}
|
}
|
||||||
|
|
||||||
const convo = schema.parse(conversation) as s.TConversation;
|
if (!schema) {
|
||||||
|
throw new Error(`Unknown endpointType: ${endpointType}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const convo = schema.parse(conversation) as s.TConversation | null;
|
||||||
// const { models, secondaryModels } = possibleValues ?? {};
|
// const { models, secondaryModels } = possibleValues ?? {};
|
||||||
const { models } = possibleValues ?? {};
|
const { models } = possibleValues ?? {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import type {
|
||||||
UseMutationResult,
|
UseMutationResult,
|
||||||
QueryObserverResult,
|
QueryObserverResult,
|
||||||
} from '@tanstack/react-query';
|
} from '@tanstack/react-query';
|
||||||
import { initialModelsConfig, LocalStorageKeys } from '../config';
|
import { initialModelsConfig } from '../config';
|
||||||
import type { TStartupConfig } from '../config';
|
import type { TStartupConfig } from '../config';
|
||||||
import { defaultOrderQuery } from '../types/assistants';
|
import { defaultOrderQuery } from '../types/assistants';
|
||||||
import * as dataService from '../data-service';
|
import * as dataService from '../data-service';
|
||||||
|
|
@ -302,27 +302,6 @@ export const useUpdateTokenCountMutation = (): UseMutationResult<
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useLoginUserMutation = (): UseMutationResult<
|
|
||||||
t.TLoginResponse,
|
|
||||||
unknown,
|
|
||||||
t.TLoginUser,
|
|
||||||
unknown
|
|
||||||
> => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
return useMutation((payload: t.TLoginUser) => dataService.login(payload), {
|
|
||||||
onMutate: () => {
|
|
||||||
queryClient.removeQueries();
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_CONVO_SETUP);
|
|
||||||
localStorage.removeItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_0`);
|
|
||||||
localStorage.removeItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_1`);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_MODEL);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.LAST_TOOLS);
|
|
||||||
localStorage.removeItem(LocalStorageKeys.FILES_TO_DELETE);
|
|
||||||
// localStorage.removeItem('lastAssistant');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRegisterUserMutation = (
|
export const useRegisterUserMutation = (
|
||||||
options?: m.RegistrationOptions,
|
options?: m.RegistrationOptions,
|
||||||
): UseMutationResult<t.TError, unknown, t.TRegisterUser, unknown> => {
|
): UseMutationResult<t.TError, unknown, t.TRegisterUser, unknown> => {
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ export * from './schemas';
|
||||||
|
|
||||||
export type TMessages = TMessage[];
|
export type TMessages = TMessage[];
|
||||||
|
|
||||||
export type TMessagesAtom = TMessages | null;
|
|
||||||
|
|
||||||
/* TODO: Cleanup EndpointOption types */
|
/* TODO: Cleanup EndpointOption types */
|
||||||
export type TEndpointOption = {
|
export type TEndpointOption = {
|
||||||
endpoint: EModelEndpoint;
|
endpoint: EModelEndpoint;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue