From b3aac97710ab9680046eb8089c5fcd4456bd2988 Mon Sep 17 00:00:00 2001 From: Danny Avila <110412045+danny-avila@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:10:23 -0400 Subject: [PATCH] fix(balance/models): request only when authenticated, modelsQuery "optimistic" update (#1031) * fix(balanceQuery/modelsQuery): request only when authenticated * style: match new chat capitalization to official * fix(modelsQuery): update selected model optimistically * ci: update e2e changes, disable title in ci env * fix(ci): get new chat button by data-testid and not text --- .github/workflows/playwright.yml | 1 + client/src/components/Nav/NavLinks.tsx | 6 ++++-- client/src/components/Nav/NewChat.tsx | 1 + client/src/hooks/useConversation.ts | 14 +++++++++++--- client/src/hooks/useServerStream.ts | 6 ++++-- client/src/localization/languages/Eng.tsx | 2 +- client/src/routes/Root.tsx | 6 ++++-- e2e/specs/messages.spec.ts | 4 ++-- packages/data-provider/src/data-service.ts | 2 +- packages/data-provider/src/react-query-service.ts | 7 +++++-- 10 files changed, 34 insertions(+), 15 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f3dbecbd4f..5e1ddba37c 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,6 +34,7 @@ jobs: DOMAIN_SERVER: ${{ secrets.DOMAIN_SERVER }} PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 # Skip downloading during npm install PLAYWRIGHT_BROWSERS_PATH: 0 # Places binaries to node_modules/@playwright/test + TITLE_CONVO: false steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 diff --git a/client/src/components/Nav/NavLinks.tsx b/client/src/components/Nav/NavLinks.tsx index 8335970b63..e04b6ed27c 100644 --- a/client/src/components/Nav/NavLinks.tsx +++ b/client/src/components/Nav/NavLinks.tsx @@ -17,12 +17,14 @@ import { cn } from '~/utils/'; import store from '~/store'; export default function NavLinks() { - const balanceQuery = useGetUserBalance(); + const { user, isAuthenticated } = useAuthContext(); const { data: startupConfig } = useGetStartupConfig(); + const balanceQuery = useGetUserBalance({ + enabled: !!isAuthenticated && startupConfig?.checkBalance, + }); const [showExports, setShowExports] = useState(false); const [showClearConvos, setShowClearConvos] = useState(false); const [showSettings, setShowSettings] = useState(false); - const { user } = useAuthContext(); const localize = useLocalize(); const conversation = useRecoilValue(store.conversation) ?? ({} as TConversation); diff --git a/client/src/components/Nav/NewChat.tsx b/client/src/components/Nav/NewChat.tsx index 20aa9d5ca4..fa2a8aa184 100644 --- a/client/src/components/Nav/NewChat.tsx +++ b/client/src/components/Nav/NewChat.tsx @@ -13,6 +13,7 @@ export default function NewChat() { return ( diff --git a/client/src/hooks/useConversation.ts b/client/src/hooks/useConversation.ts index e97fefc756..6e23ebe3ba 100644 --- a/client/src/hooks/useConversation.ts +++ b/client/src/hooks/useConversation.ts @@ -1,6 +1,12 @@ import { useCallback } from 'react'; import { useSetRecoilState, useResetRecoilState, useRecoilCallback, useRecoilValue } from 'recoil'; -import { TConversation, TMessagesAtom, TSubmission, TPreset } from 'librechat-data-provider'; +import type { + TConversation, + TMessagesAtom, + TSubmission, + TPreset, + TModelsConfig, +} from 'librechat-data-provider'; import { buildDefaultConvo, getDefaultEndpoint } from '~/utils'; import store from '~/store'; @@ -17,8 +23,9 @@ const useConversation = () => { conversation: TConversation, messages: TMessagesAtom = null, preset: TPreset | null = null, + modelsData?: TModelsConfig, ) => { - const modelsConfig = snapshot.getLoadable(store.modelsConfig).contents; + const modelsConfig = modelsData ?? snapshot.getLoadable(store.modelsConfig).contents; const { endpoint = null } = conversation; if (endpoint === null) { @@ -45,7 +52,7 @@ const useConversation = () => { ); const newConversation = useCallback( - (template = {}, preset?: TPreset) => { + (template = {}, preset?: TPreset, modelsData?: TModelsConfig) => { switchToConversation( { conversationId: 'new', @@ -57,6 +64,7 @@ const useConversation = () => { }, [], preset, + modelsData, ); }, [switchToConversation], diff --git a/client/src/hooks/useServerStream.ts b/client/src/hooks/useServerStream.ts index 957d4e8e9f..441616ccee 100644 --- a/client/src/hooks/useServerStream.ts +++ b/client/src/hooks/useServerStream.ts @@ -29,11 +29,13 @@ export default function useServerStream(submission: TSubmission | null) { const setIsSubmitting = useSetRecoilState(store.isSubmitting); const setConversation = useSetRecoilState(store.conversation); const resetLatestMessage = useResetRecoilState(store.latestMessage); - const { token } = useAuthContext(); + const { token, isAuthenticated } = useAuthContext(); const { data: startupConfig } = useGetStartupConfig(); const { refreshConversations } = useConversations(); - const balanceQuery = useGetUserBalance(); + const balanceQuery = useGetUserBalance({ + enabled: !!isAuthenticated && startupConfig?.checkBalance, + }); const messageHandler = (data: string, submission: TSubmission) => { const { diff --git a/client/src/localization/languages/Eng.tsx b/client/src/localization/languages/Eng.tsx index 2e9d4bceb5..5051c73c64 100644 --- a/client/src/localization/languages/Eng.tsx +++ b/client/src/localization/languages/Eng.tsx @@ -2,7 +2,7 @@ export default { com_ui_examples: 'Examples', - com_ui_new_chat: 'New chat', + com_ui_new_chat: 'New Chat', com_ui_example_quantum_computing: 'Explain quantum computing in simple terms', com_ui_example_10_year_old_b_day: 'Got any creative ideas for a 10 year old\'s birthday?', com_ui_example_http_in_js: 'How do I make an HTTP request in Javascript?', diff --git a/client/src/routes/Root.tsx b/client/src/routes/Root.tsx index 3dcf11ea6e..4da530b2d4 100644 --- a/client/src/routes/Root.tsx +++ b/client/src/routes/Root.tsx @@ -10,10 +10,11 @@ import { } from 'librechat-data-provider'; import { Nav, MobileNav } from '~/components/Nav'; -import { useAuthContext, useServerStream } from '~/hooks'; +import { useAuthContext, useServerStream, useConversation } from '~/hooks'; import store from '~/store'; export default function Root() { + const { newConversation } = useConversation(); const { user, isAuthenticated } = useAuthContext(); const [navVisible, setNavVisible] = useState(() => { const savedNavVisible = localStorage.getItem('navVisible'); @@ -30,7 +31,7 @@ export default function Root() { const searchEnabledQuery = useGetSearchEnabledQuery(); const endpointsQuery = useGetEndpointsQuery(); - const modelsQuery = useGetModelsQuery(); + const modelsQuery = useGetModelsQuery({ enabled: isAuthenticated }); const presetsQuery = useGetPresetsQuery({ enabled: !!user }); useEffect(() => { @@ -48,6 +49,7 @@ export default function Root() { useEffect(() => { if (modelsQuery.data) { setModelsConfig(modelsQuery.data); + newConversation(modelsQuery.data); } else if (modelsQuery.isError) { console.error('Failed to get models', modelsQuery.error); } diff --git a/e2e/specs/messages.spec.ts b/e2e/specs/messages.spec.ts index 27f0087ec7..76cf0a5409 100644 --- a/e2e/specs/messages.spec.ts +++ b/e2e/specs/messages.spec.ts @@ -86,7 +86,7 @@ test.describe('Messaging suite', () => { expect(currentUrl).toBe(initialUrl); //cleanup the conversation - await page.getByText('New chat', { exact: true }).click(); + await page.getByTestId('new-chat-button').click(); expect(page.url()).toBe(initialUrl); // Click on the first conversation @@ -166,7 +166,7 @@ test.describe('Messaging suite', () => { const currentUrl = page.url(); const conversationId = currentUrl.split(basePath).pop() ?? ''; expect(isUUID(conversationId)).toBeTruthy(); - await page.getByText('New chat', { exact: true }).click(); + await page.getByTestId('new-chat-button').click(); expect(page.url()).toBe(initialUrl); }); }); diff --git a/packages/data-provider/src/data-service.ts b/packages/data-provider/src/data-service.ts index 048d686746..6ccebb3121 100644 --- a/packages/data-provider/src/data-service.ts +++ b/packages/data-provider/src/data-service.ts @@ -105,7 +105,7 @@ export const getAIEndpoints = () => { return request.get(endpoints.aiEndpoints()); }; -export const getModels = () => { +export const getModels = async (): Promise => { return request.get(endpoints.models()); }; diff --git a/packages/data-provider/src/react-query-service.ts b/packages/data-provider/src/react-query-service.ts index 6aabf01349..064f6567e3 100644 --- a/packages/data-provider/src/react-query-service.ts +++ b/packages/data-provider/src/react-query-service.ts @@ -238,11 +238,14 @@ export const useGetEndpointsQuery = (): QueryObserverResult }); }; -export const useGetModelsQuery = (): QueryObserverResult => { - return useQuery([QueryKeys.models], () => dataService.getModels(), { +export const useGetModelsQuery = ( + config?: UseQueryOptions, +): QueryObserverResult => { + return useQuery([QueryKeys.models], () => dataService.getModels(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, + ...config, }); };