diff --git a/api/models/Conversation.js b/api/models/Conversation.js index 1515a28259..73841fa9b7 100644 --- a/api/models/Conversation.js +++ b/api/models/Conversation.js @@ -192,7 +192,9 @@ module.exports = { try { const convos = await Conversation.find(query) - .select('conversationId endpoint title createdAt updatedAt user') + .select( + 'conversationId endpoint title createdAt updatedAt user model agent_id assistant_id', + ) .sort({ updatedAt: order === 'asc' ? 1 : -1 }) .limit(limit + 1) .lean(); diff --git a/client/src/components/Nav/SettingsTabs/Speech/Speech.tsx b/client/src/components/Nav/SettingsTabs/Speech/Speech.tsx index 11743cb0ed..0eaf2ffd0c 100644 --- a/client/src/components/Nav/SettingsTabs/Speech/Speech.tsx +++ b/client/src/components/Nav/SettingsTabs/Speech/Speech.tsx @@ -136,6 +136,14 @@ function Speech() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [data]); + // Reset engineTTS if it is set to a removed/invalid value (e.g., 'edge') + useEffect(() => { + const validEngines = ['browser', 'external']; + if (!validEngines.includes(engineTTS)) { + setEngineTTS('browser'); + } + }, [engineTTS, setEngineTTS]); + logger.log({ sttExternal, ttsExternal }); const contentRef = useRef(null); diff --git a/client/src/components/Nav/SettingsTabs/Speech/TTS/VoiceDropdown.tsx b/client/src/components/Nav/SettingsTabs/Speech/TTS/VoiceDropdown.tsx index 3645474115..1e4dd2ecfe 100644 --- a/client/src/components/Nav/SettingsTabs/Speech/TTS/VoiceDropdown.tsx +++ b/client/src/components/Nav/SettingsTabs/Speech/TTS/VoiceDropdown.tsx @@ -12,5 +12,9 @@ export default function VoiceDropdown() { const engineTTS = useRecoilValue(store.engineTTS); const VoiceDropdownComponent = voiceDropdownComponentsMap[engineTTS]; + if (!VoiceDropdownComponent) { + return null; + } + return ; } diff --git a/client/src/hooks/Agents/useSelectAgent.ts b/client/src/hooks/Agents/useSelectAgent.ts index 1c785c0d98..abeda4c17c 100644 --- a/client/src/hooks/Agents/useSelectAgent.ts +++ b/client/src/hooks/Agents/useSelectAgent.ts @@ -4,8 +4,9 @@ import { EModelEndpoint, isAgentsEndpoint, Constants, QueryKeys } from 'librecha import type { TConversation, TPreset, Agent } from 'librechat-data-provider'; import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo'; import { useAgentsMapContext } from '~/Providers/AgentsMapContext'; -import { useGetAgentByIdQuery } from '~/data-provider'; import { useChatContext } from '~/Providers/ChatContext'; +import { useGetAgentByIdQuery } from '~/data-provider'; +import { logger } from '~/utils'; export default function useSelectAgent() { const queryClient = useQueryClient(); @@ -22,6 +23,7 @@ export default function useSelectAgent() { const updateConversation = useCallback( (agent: Partial, template: Partial) => { + logger.log('conversation', 'Updating conversation with agent', agent); if (isAgentsEndpoint(conversation?.endpoint)) { const currentConvo = getDefaultConversation({ conversation: { ...(conversation ?? {}), agent_id: agent.id }, diff --git a/client/src/hooks/Assistants/useSelectAssistant.ts b/client/src/hooks/Assistants/useSelectAssistant.ts index 5f3a5e9cee..c3710503e8 100644 --- a/client/src/hooks/Assistants/useSelectAssistant.ts +++ b/client/src/hooks/Assistants/useSelectAssistant.ts @@ -4,7 +4,7 @@ import type { AssistantsEndpoint, TConversation, TPreset } from 'librechat-data- import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo'; import { useChatContext } from '~/Providers/ChatContext'; import useAssistantListMap from './useAssistantListMap'; -import { mapAssistants } from '~/utils'; +import { mapAssistants, logger } from '~/utils'; export default function useSelectAssistant(endpoint: AssistantsEndpoint) { const getDefaultConversation = useDefaultConvo(); @@ -24,6 +24,7 @@ export default function useSelectAssistant(endpoint: AssistantsEndpoint) { conversationId: 'new', }; + logger.log('conversation', 'Updating conversation with assistant', assistant); if (isAssistantsEndpoint(conversation?.endpoint)) { const currentConvo = getDefaultConversation({ conversation: { ...(conversation ?? {}) }, diff --git a/client/src/hooks/Conversations/useGenerateConvo.ts b/client/src/hooks/Conversations/useGenerateConvo.ts index 8f545161a2..0c0728ddec 100644 --- a/client/src/hooks/Conversations/useGenerateConvo.ts +++ b/client/src/hooks/Conversations/useGenerateConvo.ts @@ -11,7 +11,7 @@ import type { } from 'librechat-data-provider'; import type { SetterOrUpdater } from 'recoil'; import type { AssistantListItem } from '~/common'; -import { getEndpointField, buildDefaultConvo, getDefaultEndpoint } from '~/utils'; +import { getEndpointField, buildDefaultConvo, getDefaultEndpoint, logger } from '~/utils'; import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap'; import { useGetEndpointsQuery } from '~/data-provider'; import { mainTextareaId } from '~/common'; @@ -44,6 +44,7 @@ const useGenerateConvo = ({ conversationId: rootConvo.conversationId, } as TConversation; + logger.log('conversation', 'Setting conversation from `useNewConvo`', update); return update; }); } diff --git a/client/src/hooks/Conversations/useNavigateToConvo.tsx b/client/src/hooks/Conversations/useNavigateToConvo.tsx index a329a7ca67..88c6eaeda7 100644 --- a/client/src/hooks/Conversations/useNavigateToConvo.tsx +++ b/client/src/hooks/Conversations/useNavigateToConvo.tsx @@ -1,7 +1,13 @@ import { useSetRecoilState } from 'recoil'; import { useNavigate } from 'react-router-dom'; import { useQueryClient } from '@tanstack/react-query'; -import { QueryKeys, EModelEndpoint, LocalStorageKeys, Constants } from 'librechat-data-provider'; +import { + QueryKeys, + Constants, + dataService, + EModelEndpoint, + LocalStorageKeys, +} from 'librechat-data-provider'; import type { TConversation, TEndpointsConfig, TModelsConfig } from 'librechat-data-provider'; import { buildDefaultConvo, getDefaultEndpoint, getEndpointField, logger } from '~/utils'; import store from '~/store'; @@ -14,6 +20,21 @@ const useNavigateToConvo = (index = 0) => { const setSubmission = useSetRecoilState(store.submissionByIndex(index)); const { hasSetConversation, setConversation } = store.useCreateConversationAtom(index); + const fetchFreshData = async (conversationId?: string | null) => { + if (!conversationId) { + return; + } + try { + const data = await queryClient.fetchQuery([QueryKeys.conversation, conversationId], () => + dataService.getConversationById(conversationId), + ); + logger.log('conversation', 'Fetched fresh conversation data', data); + setConversation(data); + } catch (error) { + console.error('Error fetching conversation data on navigation', error); + } + }; + const navigateToConvo = ( conversation?: TConversation | null, _resetLatestMessage = true, @@ -23,6 +44,7 @@ const useNavigateToConvo = (index = 0) => { logger.warn('conversation', 'Conversation not provided to `navigateToConvo`'); return; } + logger.log('conversation', 'Navigating to conversation', conversation); hasSetConversation.current = true; setSubmission(null); if (_resetLatestMessage) { @@ -60,6 +82,10 @@ const useNavigateToConvo = (index = 0) => { clearAllConversations(true); setConversation(convo); navigate(`/c/${convo.conversationId ?? Constants.NEW_CONVO}`); + if (convo.conversationId !== Constants.NEW_CONVO && convo.conversationId) { + queryClient.invalidateQueries([QueryKeys.conversation, convo.conversationId]); + fetchFreshData(convo.conversationId); + } }; const navigateWithLastTools = ( diff --git a/client/src/hooks/Input/useQueryParams.ts b/client/src/hooks/Input/useQueryParams.ts index d6bb90b210..a2f7344f0d 100644 --- a/client/src/hooks/Input/useQueryParams.ts +++ b/client/src/hooks/Input/useQueryParams.ts @@ -11,7 +11,7 @@ import { } from 'librechat-data-provider'; import type { TPreset, TEndpointsConfig, TStartupConfig } from 'librechat-data-provider'; import type { ZodAny } from 'zod'; -import { getConvoSwitchLogic, getModelSpecIconURL, removeUnavailableTools } from '~/utils'; +import { getConvoSwitchLogic, getModelSpecIconURL, removeUnavailableTools, logger } from '~/utils'; import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo'; import { useChatContext, useChatFormContext } from '~/Providers'; import useSubmitMessage from '~/hooks/Messages/useSubmitMessage'; @@ -159,6 +159,7 @@ export default function useQueryParams({ }); /* We don't reset the latest message, only when changing settings mid-converstion */ + logger.log('conversation', 'Switching conversation from query params', currentConvo); newConversation({ template: currentConvo, preset: newPreset, diff --git a/client/src/hooks/Input/useSelectMention.ts b/client/src/hooks/Input/useSelectMention.ts index ce5d2ed55f..af61447480 100644 --- a/client/src/hooks/Input/useSelectMention.ts +++ b/client/src/hooks/Input/useSelectMention.ts @@ -9,7 +9,7 @@ import type { TEndpointsConfig, } from 'librechat-data-provider'; import type { MentionOption, ConvoGenerator } from '~/common'; -import { getConvoSwitchLogic, getModelSpecIconURL, removeUnavailableTools } from '~/utils'; +import { getConvoSwitchLogic, getModelSpecIconURL, removeUnavailableTools, logger } from '~/utils'; import { useChatContext } from '~/Providers'; import { useDefaultConvo } from '~/hooks'; import store from '~/store'; @@ -86,6 +86,7 @@ export default function useSelectMention({ }); /* We don't reset the latest message, only when changing settings mid-converstion */ + logger.info('conversation', 'Switching conversation to new spec (modular)', conversation); newConversation({ template: currentConvo, preset, @@ -95,6 +96,7 @@ export default function useSelectMention({ return; } + logger.info('conversation', 'Switching conversation to new spec', conversation); newConversation({ template: { ...(template as Partial) }, preset, @@ -172,6 +174,11 @@ export default function useSelectMention({ }); /* We don't reset the latest message, only when changing settings mid-converstion */ + logger.info( + 'conversation', + 'Switching conversation to new endpoint/model (modular)', + currentConvo, + ); newConversation({ template: currentConvo, preset: currentConvo, @@ -181,6 +188,7 @@ export default function useSelectMention({ return; } + logger.info('conversation', 'Switching conversation to new endpoint/model', template); newConversation({ template: { ...(template as Partial) }, preset: { ...kwargs, spec: null, iconURL: null, modelLabel: null, endpoint: newEndpoint }, @@ -230,6 +238,7 @@ export default function useSelectMention({ }); /* We don't reset the latest message, only when changing settings mid-converstion */ + logger.info('conversation', 'Switching conversation to new preset (modular)', currentConvo); newConversation({ template: currentConvo, preset: newPreset, @@ -239,6 +248,7 @@ export default function useSelectMention({ return; } + logger.info('conversation', 'Switching conversation to new preset', template); newConversation({ preset: newPreset, keepAddedConvos: isModular }); }, [ diff --git a/client/src/hooks/useNewConvo.ts b/client/src/hooks/useNewConvo.ts index 3054f879e7..f5933cc547 100644 --- a/client/src/hooks/useNewConvo.ts +++ b/client/src/hooks/useNewConvo.ts @@ -31,6 +31,7 @@ import useAssistantListMap from './Assistants/useAssistantListMap'; import { useResetChatBadges } from './useChatBadges'; import { usePauseGlobalAudio } from './Audio'; import { mainTextareaId } from '~/common'; +import { logger } from '~/utils'; import store from '~/store'; const useNewConvo = (index = 0) => { @@ -151,6 +152,7 @@ const useNewConvo = (index = 0) => { if (!(keepAddedConvos ?? false)) { clearAllConversations(true); } + logger.log('conversation', 'Setting conversation from `useNewConvo`', conversation); setConversation(conversation); setSubmission({} as TSubmission); if (!(keepLatestMessage ?? false)) { diff --git a/client/src/routes/ChatRoute.tsx b/client/src/routes/ChatRoute.tsx index 3e66ed8c13..4ce9cdef9e 100644 --- a/client/src/routes/ChatRoute.tsx +++ b/client/src/routes/ChatRoute.tsx @@ -1,16 +1,16 @@ -import { useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { Constants, EModelEndpoint } from 'librechat-data-provider'; import { useGetModelsQuery } from 'librechat-data-provider/react-query'; import type { TPreset } from 'librechat-data-provider'; import { - useGetConvoIdQuery, useHealthCheck, - useGetEndpointsQuery, + useGetConvoIdQuery, useGetStartupConfig, + useGetEndpointsQuery, } from '~/data-provider'; import { useNewConvo, useAppStartup, useAssistantListMap } from '~/hooks'; -import { getDefaultModelSpec, getModelSpecIconURL } from '~/utils'; +import { getDefaultModelSpec, getModelSpecIconURL, logger } from '~/utils'; import { ToolCallsMapProvider } from '~/Providers'; import ChatView from '~/components/Chat/ChatView'; import useAuthRedirect from './useAuthRedirect'; @@ -38,11 +38,6 @@ export default function ChatRoute() { const { hasSetConversation, conversation } = store.useCreateConversationAtom(index); const { newConversation } = useNewConvo(); - // Reset the guard flag whenever conversationId changes - useEffect(() => { - hasSetConversation.current = false; - }, [conversationId]); - const modelsQuery = useGetModelsQuery({ enabled: isAuthenticated, refetchOnMount: 'always', @@ -53,6 +48,9 @@ export default function ChatRoute() { const endpointsQuery = useGetEndpointsQuery({ enabled: isAuthenticated }); const assistantListMap = useAssistantListMap(); + /** This effect is mainly for the first conversation state change on first load of the page. + * Adjusting this may have unintended consequences on the conversation state. + */ useEffect(() => { const shouldSetConvo = (startupConfig && !hasSetConversation.current && !modelsQuery.data?.initial) ?? false; @@ -63,7 +61,7 @@ export default function ChatRoute() { if (conversationId === Constants.NEW_CONVO && endpointsQuery.data && modelsQuery.data) { const spec = getDefaultModelSpec(startupConfig); - + logger.log('conversation', 'ChatRoute, new convo effect', conversation); newConversation({ modelsData: modelsQuery.data, template: conversation ? conversation : undefined, @@ -80,6 +78,7 @@ export default function ChatRoute() { hasSetConversation.current = true; } else if (initialConvoQuery.data && endpointsQuery.data && modelsQuery.data) { + logger.log('conversation', 'ChatRoute initialConvoQuery', initialConvoQuery.data); newConversation({ template: initialConvoQuery.data, /* this is necessary to load all existing settings */ @@ -94,6 +93,7 @@ export default function ChatRoute() { assistantListMap[EModelEndpoint.azureAssistants] ) { const spec = getDefaultModelSpec(startupConfig); + logger.log('conversation', 'ChatRoute new convo, assistants effect', conversation); newConversation({ modelsData: modelsQuery.data, template: conversation ? conversation : undefined, @@ -112,6 +112,7 @@ export default function ChatRoute() { assistantListMap[EModelEndpoint.assistants] && assistantListMap[EModelEndpoint.azureAssistants] ) { + logger.log('conversation', 'ChatRoute convo, assistants effect', initialConvoQuery.data); newConversation({ template: initialConvoQuery.data, preset: initialConvoQuery.data as TPreset, @@ -127,7 +128,6 @@ export default function ChatRoute() { endpointsQuery.data, modelsQuery.data, assistantListMap, - conversationId, ]); if (endpointsQuery.isLoading || modelsQuery.isLoading) {