Move data provider to shared package (#582)

* create data-provider package and move code from data-provider folder to be shared between apps

* fix type issues

* add packages to ignore

* add new data-provider package to apps

* refactor: change client imports to use @librechat/data-provider package

* include data-provider build script in frontend build

* fix type issue after rebasing

* delete admin/package.json from this branch

* update test ci script to include building of data-provider package

* Try using regular build for test action

* Switch frontend-review back to build:ci

* Remove loginRedirect from Login.tsx

* Add ChatGPT back to EModelEndpoint
This commit is contained in:
Dan Orlando 2023-07-04 12:47:41 -07:00 committed by GitHub
parent d0078d478d
commit 04e4259005
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1472 additions and 141 deletions

View file

@ -2,7 +2,7 @@ import { useEffect } from 'react';
import LoginForm from './LoginForm';
import { useAuthContext } from '~/hooks/AuthContext';
import { useNavigate } from 'react-router-dom';
import { useGetStartupConfig } from '~/data-provider';
import { useGetStartupConfig } from '@librechat/data-provider';
function Login() {
const { login, error, isAuthenticated } = useAuthContext();

View file

@ -1,5 +1,5 @@
import { useForm } from 'react-hook-form';
import { TLoginUser } from '~/data-provider';
import { TLoginUser } from '@librechat/data-provider';
type TLoginFormProps = {
onSubmit: (data: TLoginUser) => void;

View file

@ -1,7 +1,11 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { useRegisterUserMutation, TRegisterUser, useGetStartupConfig } from '~/data-provider';
import {
useRegisterUserMutation,
TRegisterUser,
useGetStartupConfig
} from '@librechat/data-provider';
function Registration() {
const navigate = useNavigate();
@ -27,7 +31,9 @@ function Registration() {
},
onError: (error) => {
setError(true);
//@ts-ignore - error is of type unknown
if (error.response?.data?.message) {
//@ts-ignore - error is of type unknown
setErrorMessage(error.response?.data?.message);
}
}

View file

@ -1,6 +1,10 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useRequestPasswordResetMutation, TRequestPasswordReset } from '~/data-provider';
import {
useRequestPasswordResetMutation,
TRequestPasswordReset,
TRequestPasswordResetResponse
} from '@librechat/data-provider';
function RequestPasswordReset() {
const {
@ -15,7 +19,7 @@ function RequestPasswordReset() {
const onSubmit = (data: TRequestPasswordReset) => {
requestPasswordReset.mutate(data, {
onSuccess: (data) => {
onSuccess: (data: TRequestPasswordResetResponse) => {
setSuccess(true);
setResetLink(data.link);
},

View file

@ -1,6 +1,6 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useResetPasswordMutation, TResetPassword } from '~/data-provider';
import { useResetPasswordMutation, TResetPassword } from '@librechat/data-provider';
import { useNavigate, useSearchParams } from 'react-router-dom';
function ResetPassword() {
@ -73,12 +73,14 @@ function ResetPassword() {
<input
type="hidden"
id="token"
// @ts-ignore - Type 'string | null' is not assignable to type 'string | number | readonly string[] | undefined'
value={params.get('token')}
{...register('token', { required: 'Unable to process: No valid reset token' })}
/>
<input
type="hidden"
id="userId"
// @ts-ignore - Type 'string | null' is not assignable to type 'string | number | readonly string[] | undefined'
value={params.get('userId')}
{...register('userId', { required: 'Unable to process: No valid user id' })}
/>

View file

@ -1,9 +1,9 @@
import { render, waitFor } from 'layout-test-utils';
import userEvent from '@testing-library/user-event';
import Login from '../Login';
import * as mockDataProvider from '~/data-provider';
import * as mockDataProvider from '@librechat/data-provider';
jest.mock('~/data-provider');
jest.mock('@librechat/data-provider');
const setup = ({
useGetUserQueryReturnValue = {

View file

@ -1,9 +1,9 @@
import { render, waitFor } from 'layout-test-utils';
import userEvent from '@testing-library/user-event';
import Registration from '../Registration';
import * as mockDataProvider from '~/data-provider';
import * as mockDataProvider from '@librechat/data-provider';
jest.mock('~/data-provider');
jest.mock('@librechat/data-provider');
const setup = ({
useGetUserQueryReturnValue = {

View file

@ -1,6 +1,6 @@
import { useState, useRef, useEffect } from 'react';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { useUpdateConversationMutation } from '~/data-provider';
import { useUpdateConversationMutation } from '@librechat/data-provider';
import RenameButton from './RenameButton';
import DeleteButton from './DeleteButton';
import ConvoIcon from '../svg/ConvoIcon';

View file

@ -2,7 +2,7 @@ import { useEffect } from 'react';
import TrashIcon from '../svg/TrashIcon';
import CrossIcon from '../svg/CrossIcon';
import { useRecoilValue } from 'recoil';
import { useDeleteConversationMutation } from '~/data-provider';
import { useDeleteConversationMutation } from '@librechat/data-provider';
import store from '~/store';

View file

@ -5,7 +5,7 @@ import { Checkbox } from '~/components/ui/Checkbox.tsx';
import SelectDropDown from '../../ui/SelectDropDown';
import { cn } from '~/utils/';
import useDebounce from '~/hooks/useDebounce';
import { useUpdateTokenCountMutation } from '~/data-provider';
import { useUpdateTokenCountMutation } from '@librechat/data-provider';
const defaultTextProps =
'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
@ -43,7 +43,7 @@ function Settings(props) {
}, [debouncedContext]);
return (
<div className="md:h-[350px] h-[490px] overflow-y-auto">
<div className="h-[490px] overflow-y-auto md:h-[350px]">
<div className="grid gap-6 sm:grid-cols-2">
<div className="col-span-1 flex flex-col items-center justify-start gap-6">
<div className="grid w-full items-center gap-2">
@ -113,7 +113,8 @@ function Settings(props) {
<a
href="https://github.com/danny-avila/LibreChat/blob/main/client/defaultSystemMessage.md"
target="_blank"
className="text-blue-500 transition-colors duration-200 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-500" rel="noreferrer"
className="text-blue-500 transition-colors duration-200 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-500"
rel="noreferrer"
>
System Message
</a>{' '}

View file

@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import {Dialog, DialogTemplate, Input, Label} from '../ui/';
import { Dialog, DialogTemplate, Input, Label } from '../ui/';
import { cn } from '~/utils/';
import cleanupPreset from '~/utils/cleanupPreset';
import { useCreatePresetMutation } from '~/data-provider';
import { useCreatePresetMutation } from '@librechat/data-provider';
import store from '~/store';
const SaveAsPresetDialog = ({ open, onOpenChange, preset }) => {

View file

@ -1,5 +1,5 @@
import React from 'react';
import { useGetStartupConfig } from '~/data-provider';
import { useGetStartupConfig } from '@librechat/data-provider';
export default function Footer() {
const { data: config } = useGetStartupConfig();

View file

@ -9,7 +9,7 @@ import { Trash2 } from 'lucide-react';
import FileUpload from './FileUpload';
import getIcon from '~/utils/getIcon';
import getDefaultConversation from '~/utils/getDefaultConversation';
import { useDeletePresetMutation, useCreatePresetMutation } from '~/data-provider';
import { useDeletePresetMutation, useCreatePresetMutation } from '@librechat/data-provider';
import {
Button,
DropdownMenu,
@ -19,7 +19,8 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
DialogTemplate,
Dialog, DialogTrigger
Dialog,
DialogTrigger
} from '../../ui/';
import { cn } from '~/utils/';
@ -87,8 +88,9 @@ export default function NewConversationMenu() {
// set the current model
const onSelectEndpoint = (newEndpoint) => {
setMenuOpen(false);
if (!newEndpoint) { return; }
else {
if (!newEndpoint) {
return;
} else {
newConversation({}, { endpoint: newEndpoint });
}
};
@ -101,7 +103,7 @@ export default function NewConversationMenu() {
const currentConvo = getDefaultConversation({
conversation,
endpointsConfig,
preset: newPreset,
preset: newPreset
});
setConversation(currentConvo);
@ -153,7 +155,7 @@ export default function NewConversationMenu() {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-96 dark:bg-gray-900 z-[100]"
className="z-[100] w-96 dark:bg-gray-900"
onCloseAutoFocus={(event) => event.preventDefault()}
>
<DropdownMenuLabel
@ -166,11 +168,15 @@ export default function NewConversationMenu() {
<DropdownMenuRadioGroup
value={endpoint}
onValueChange={onSelectEndpoint}
className="overflow-y-auto gap-1 flex flex-col"
className="flex flex-col gap-1 overflow-y-auto"
>
{showEndpoints &&
(availableEndpoints.length ? (
<EndpointItems selectedEndpoint={endpoint} endpoints={availableEndpoints} onSelect={onSelectEndpoint} />
<EndpointItems
selectedEndpoint={endpoint}
endpoints={availableEndpoints}
onSelect={onSelectEndpoint}
/>
) : (
<DropdownMenuLabel className="dark:text-gray-300">
No endpoint available.
@ -217,7 +223,10 @@ export default function NewConversationMenu() {
<DropdownMenuSeparator />
<DropdownMenuRadioGroup
onValueChange={onSelectPreset}
className={cn('overflow-y-auto overflow-x-hidden', showEndpoints ? 'max-h-[210px]' : 'max-h-[315px]')}
className={cn(
'overflow-y-auto overflow-x-hidden',
showEndpoints ? 'max-h-[210px]' : 'max-h-[315px]'
)}
>
{showPresets &&
(presets.length ? (

View file

@ -14,7 +14,7 @@ import { Settings, AgentSettings } from '../../Endpoints/Plugins/';
import { cn } from '~/utils/';
import store from '~/store';
import { useAuthContext } from '~/hooks/AuthContext';
import { useAvailablePluginsQuery } from '~/data-provider';
import { useAvailablePluginsQuery } from '@librechat/data-provider';
function PluginsOptions() {
const { data: allPlugins } = useAvailablePluginsQuery();

View file

@ -1,7 +1,6 @@
import { useEffect } from 'react';
import { useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil';
import { SSE } from '~/data-provider/sse.mjs';
import createPayload from '~/data-provider/createPayload';
import { SSE, createPayload } from '@librechat/data-provider';
import store from '~/store';
import { useAuthContext } from '~/hooks/AuthContext';
@ -224,7 +223,8 @@ export default function MessageHandler() {
events.onopen = () => console.log('connection is opened');
events.oncancel = () => abortConversation(message?.conversationId || submission?.conversationId);
events.oncancel = () =>
abortConversation(message?.conversationId || submission?.conversationId);
events.onerror = function (e) {
console.log('error in opening conn.');

View file

@ -10,7 +10,7 @@ import HoverButtons from './HoverButtons';
import SiblingSwitch from './SiblingSwitch';
import getIcon from '~/utils/getIcon';
import { useMessageHandler } from '~/utils/handleSubmit';
import { useGetConversationByIdQuery } from '~/data-provider';
import { useGetConversationByIdQuery } from '@librechat/data-provider';
import { cn } from '~/utils/';
import store from '~/store';
import getError from '~/utils/getError';
@ -192,7 +192,7 @@ export default function Message({
<div className="markdown prose dark:prose-invert light w-full break-words">
{!isCreatedByUser ? (
<>
<Content content={text} message={message}/>
<Content content={text} message={message} />
</>
) : (
<>{text}</>

View file

@ -1,7 +1,7 @@
import { useState, useEffect, useCallback } from 'react';
import { Dialog, DialogTemplate } from '../ui/';
import { ClearChatsButton } from './SettingsTabs/';
import { useClearConversationsMutation } from '~/data-provider';
import { useClearConversationsMutation } from '@librechat/data-provider';
import store from '~/store';
const ClearConvos = ({ open, onOpenChange }) => {
@ -32,7 +32,9 @@ const ClearConvos = ({ open, onOpenChange }) => {
<DialogTemplate
title="Clear conversations"
description="Are you sure you want to clear all conversations? This is irreversible."
leftButtons={<ClearChatsButton showText={false} confirmClear={confirmClear} onClick={clearConvos} />}
leftButtons={
<ClearChatsButton showText={false} confirmClear={confirmClear} onClick={clearConvos} />
}
/>
</Dialog>
);

View file

@ -4,7 +4,7 @@ import { General } from './SettingsTabs/';
import { CogIcon } from '~/components/svg';
import { useEffect, useState } from 'react';
import { cn } from '~/utils/';
import { useClearConversationsMutation } from '~/data-provider';
import { useClearConversationsMutation } from '@librechat/data-provider';
import store from '~/store';
export default function Settings({ open, onOpenChange }) {

View file

@ -2,9 +2,15 @@ import * as Tabs from '@radix-ui/react-tabs';
import { CheckIcon } from 'lucide-react';
import { ThemeContext } from '~/hooks/ThemeContext';
import React, { useState, useContext, useCallback } from 'react';
import { useClearConversationsMutation } from '~/data-provider';
import { useClearConversationsMutation } from '@librechat/data-provider';
export const ThemeSelector = ({ theme, onChange }: { theme: string, onChange: (value: string) => void }) => (
export const ThemeSelector = ({
theme,
onChange
}: {
theme: string;
onChange: (value: string) => void;
}) => (
<div className="flex items-center justify-between">
<div>Theme</div>
<select
@ -19,7 +25,15 @@ export const ThemeSelector = ({ theme, onChange }: { theme: string, onChange: (v
</div>
);
export const ClearChatsButton = ({ confirmClear, showText = true, onClick }: { confirmClear: boolean, showText: boolean, onClick: () => void }) => (
export const ClearChatsButton = ({
confirmClear,
showText = true,
onClick
}: {
confirmClear: boolean;
showText: boolean;
onClick: () => void;
}) => (
<div className="flex items-center justify-between">
{showText && <div>Clear all chats</div>}
<button
@ -45,7 +59,7 @@ function General() {
const { theme, setTheme } = useContext(ThemeContext);
const clearConvosMutation = useClearConversationsMutation();
const [confirmClear, setConfirmClear] = useState(false);
const clearConvos = useCallback(() => {
if (confirmClear) {
console.log('Clearing conversations...');
@ -56,18 +70,21 @@ function General() {
}
}, [confirmClear, clearConvosMutation]);
const changeTheme = useCallback((value: string) => {
setTheme(value);
}, [setTheme]);
const changeTheme = useCallback(
(value: string) => {
setTheme(value);
},
[setTheme]
);
return (
<Tabs.Content value="general" role="tabpanel" className="w-full md:min-h-[300px]" >
<Tabs.Content value="general" role="tabpanel" className="w-full md:min-h-[300px]">
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<ThemeSelector theme={theme} onChange={changeTheme} />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<ClearChatsButton confirmClear={confirmClear} onClick={clearConvos} showText={true}/>
<ClearChatsButton confirmClear={confirmClear} onClick={clearConvos} showText={true} />
</div>
</div>
</Tabs.Content>

View file

@ -1,6 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useCallback, useEffect, useRef, useState } from 'react';
import { useGetConversationsQuery, useSearchQuery } from '~/data-provider';
import { useGetConversationsQuery, useSearchQuery } from '@librechat/data-provider';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import Conversations from '../Conversations';
@ -9,7 +9,6 @@ import NewChat from './NewChat';
import Pages from '../Conversations/Pages';
import Panel from '../svg/Panel';
import Spinner from '../svg/Spinner';
// import { ThemeContext } from '~/hooks/ThemeContext';
import { cn } from '~/utils/';
import store from '~/store';
import { useAuthContext } from '~/hooks/AuthContext';
@ -38,7 +37,6 @@ import useDebounce from '~/hooks/useDebounce';
export default function Nav({ navVisible, setNavVisible }) {
const [isHovering, setIsHovering] = useState(false);
const { isAuthenticated } = useAuthContext();
// const { theme, } = useContext(ThemeContext);
const containerRef = useRef(null);
const scrollPositionRef = useRef(null);
@ -159,7 +157,8 @@ export default function Nav({ navVisible, setNavVisible }) {
const isMobile = () => {
const userAgent = typeof window.navigator === 'undefined' ? '' : navigator.userAgent;
const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
const mobileRegex =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
return mobileRegex.test(userAgent);
};

View file

@ -1,4 +1,4 @@
import { TPlugin, TPluginAuthConfig } from '~/data-provider';
import { TPlugin, TPluginAuthConfig } from '@librechat/data-provider';
import { Save } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { TPluginAction } from './PluginStoreDialog';

View file

@ -4,7 +4,11 @@ import { useRecoilState } from 'recoil';
import { X } from 'lucide-react';
import store from '~/store';
import { PluginStoreItem, PluginPagination, PluginAuthForm } from '.';
import { useAvailablePluginsQuery, useUpdateUserPluginsMutation, TPlugin } from '~/data-provider';
import {
useAvailablePluginsQuery,
useUpdateUserPluginsMutation,
TPlugin
} from '@librechat/data-provider';
import { useAuthContext } from '~/hooks/AuthContext';
type TPluginStoreDialogProps = {
@ -95,8 +99,7 @@ function PluginStoreDialog({ isOpen, setIsOpen }: TPluginStoreDialogProps) {
if (width < 501) {
setItemsPerPage(8);
return;
} else
if (width < 640) {
} else if (width < 640) {
columns = 2;
} else if (width < 1024) {
columns = 3;
@ -140,7 +143,7 @@ function PluginStoreDialog({ isOpen, setIsOpen }: TPluginStoreDialogProps) {
<div className="fixed inset-0 bg-gray-500/90 transition-opacity dark:bg-gray-800/90" />
{/* Full-screen container to center the panel */}
<div className="fixed inset-0 flex items-center justify-center p-4">
<Dialog.Panel className="relative w-full max-sm:h-full overflow-y-auto transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all dark:bg-gray-900 sm:mx-7 sm:my-8 sm:max-w-2xl lg:max-w-5xl xl:max-w-7xl">
<Dialog.Panel className="relative w-full transform overflow-hidden overflow-y-auto rounded-lg bg-white text-left shadow-xl transition-all dark:bg-gray-900 max-sm:h-full sm:mx-7 sm:my-8 sm:max-w-2xl lg:max-w-5xl xl:max-w-7xl">
<div className="flex items-center justify-between border-b-[1px] border-black/10 px-4 pb-4 pt-5 dark:border-white/10 sm:p-6">
<div className="flex items-center">
<div className="text-center sm:text-left">

View file

@ -1,4 +1,4 @@
import { TPlugin } from '~/data-provider';
import { TPlugin } from '@librechat/data-provider';
import { XCircle, DownloadCloud } from 'lucide-react';
type TPluginStoreItemProps = {

View file

@ -1,9 +1,9 @@
import { render } from 'layout-test-utils';
import PluginStoreDialog from '../PluginStoreDialog';
import userEvent from '@testing-library/user-event';
import * as mockDataProvider from '~/data-provider';
import * as mockDataProvider from '@librechat/data-provider';
jest.mock('~/data-provider');
jest.mock('@librechat/data-provider');
class ResizeObserver {
observe() {

View file

@ -5,7 +5,7 @@ import SunIcon from '../svg/SunIcon';
import LightningIcon from '../svg/LightningIcon';
import CautionIcon from '../svg/CautionIcon';
import store from '~/store';
import { useGetStartupConfig } from '~/data-provider';
import { useGetStartupConfig } from '@librechat/data-provider';
export default function Landing() {
const { data: config } = useGetStartupConfig();