| | import { useCallback } from 'react'; |
| | import { useNavigate, useSearchParams } from 'react-router-dom'; |
| | import { useGetModelsQuery } from 'librechat-data-provider/react-query'; |
| | import { useRecoilState, useRecoilValue, useSetRecoilState, useRecoilCallback } from 'recoil'; |
| | import { |
| | Constants, |
| | FileSources, |
| | EModelEndpoint, |
| | isParamEndpoint, |
| | getEndpointField, |
| | LocalStorageKeys, |
| | isAssistantsEndpoint, |
| | } from 'librechat-data-provider'; |
| | import type { |
| | TPreset, |
| | TSubmission, |
| | TModelsConfig, |
| | TConversation, |
| | TEndpointsConfig, |
| | } from 'librechat-data-provider'; |
| | import type { AssistantListItem } from '~/common'; |
| | import { |
| | updateLastSelectedModel, |
| | getDefaultModelSpec, |
| | getDefaultEndpoint, |
| | getModelSpecPreset, |
| | buildDefaultConvo, |
| | logger, |
| | } from '~/utils'; |
| | import { useDeleteFilesMutation, useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider'; |
| | import useAssistantListMap from './Assistants/useAssistantListMap'; |
| | import { useResetChatBadges } from './useChatBadges'; |
| | import { useApplyModelSpecEffects } from './Agents'; |
| | import { usePauseGlobalAudio } from './Audio'; |
| | import store from '~/store'; |
| |
|
| | const useNewConvo = (index = 0) => { |
| | const navigate = useNavigate(); |
| | const [searchParams] = useSearchParams(); |
| | const { data: startupConfig } = useGetStartupConfig(); |
| | const applyModelSpecEffects = useApplyModelSpecEffects(); |
| | const clearAllConversations = store.useClearConvoState(); |
| | const defaultPreset = useRecoilValue(store.defaultPreset); |
| | const { setConversation } = store.useCreateConversationAtom(index); |
| | const [files, setFiles] = useRecoilState(store.filesByIndex(index)); |
| | const saveBadgesState = useRecoilValue<boolean>(store.saveBadgesState); |
| | const clearAllLatestMessages = store.useClearLatestMessages(`useNewConvo ${index}`); |
| | const setSubmission = useSetRecoilState<TSubmission | null>(store.submissionByIndex(index)); |
| | const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery(); |
| |
|
| | const modelsQuery = useGetModelsQuery(); |
| | const assistantsListMap = useAssistantListMap(); |
| | const { pauseGlobalAudio } = usePauseGlobalAudio(index); |
| | const saveDrafts = useRecoilValue<boolean>(store.saveDrafts); |
| | const resetBadges = useResetChatBadges(); |
| |
|
| | const { mutateAsync } = useDeleteFilesMutation({ |
| | onSuccess: () => { |
| | console.log('Files deleted'); |
| | }, |
| | onError: (error) => { |
| | console.log('Error deleting files:', error); |
| | }, |
| | }); |
| |
|
| | const switchToConversation = useRecoilCallback( |
| | () => |
| | async ( |
| | conversation: TConversation, |
| | preset: Partial<TPreset> | null = null, |
| | modelsData?: TModelsConfig, |
| | buildDefault?: boolean, |
| | keepLatestMessage?: boolean, |
| | keepAddedConvos?: boolean, |
| | disableFocus?: boolean, |
| | _disableParams?: boolean, |
| | ) => { |
| | const modelsConfig = modelsData ?? modelsQuery.data; |
| | const { endpoint = null } = conversation; |
| | const buildDefaultConversation = (endpoint === null || buildDefault) ?? false; |
| | const activePreset = |
| | |
| | |
| | |
| | |
| | defaultPreset && |
| | !preset && |
| | (defaultPreset.endpoint === endpoint || !endpoint) && |
| | buildDefaultConversation |
| | ? defaultPreset |
| | : preset; |
| |
|
| | const disableParams = |
| | _disableParams ?? |
| | (activePreset?.presetId != null && |
| | activePreset.presetId && |
| | activePreset.presetId === defaultPreset?.presetId); |
| |
|
| | if (buildDefaultConversation) { |
| | let defaultEndpoint = getDefaultEndpoint({ |
| | convoSetup: activePreset ?? conversation, |
| | endpointsConfig, |
| | }); |
| |
|
| | if (!defaultEndpoint) { |
| | defaultEndpoint = Object.keys(endpointsConfig ?? {})[0] as EModelEndpoint; |
| | } |
| |
|
| | const endpointType = getEndpointField(endpointsConfig, defaultEndpoint, 'type'); |
| | if (!conversation.endpointType && endpointType) { |
| | conversation.endpointType = endpointType; |
| | } else if (conversation.endpointType && !endpointType) { |
| | conversation.endpointType = undefined; |
| | } |
| |
|
| | const isAssistantEndpoint = isAssistantsEndpoint(defaultEndpoint); |
| | const assistants: AssistantListItem[] = assistantsListMap[defaultEndpoint] ?? []; |
| | const currentAssistantId = conversation.assistant_id ?? ''; |
| | const currentAssistant = assistantsListMap[defaultEndpoint]?.[currentAssistantId] as |
| | | AssistantListItem |
| | | undefined; |
| |
|
| | if (currentAssistantId && !currentAssistant) { |
| | conversation.assistant_id = undefined; |
| | } |
| |
|
| | if (!currentAssistantId && isAssistantEndpoint) { |
| | conversation.assistant_id = |
| | localStorage.getItem( |
| | `${LocalStorageKeys.ASST_ID_PREFIX}${index}${defaultEndpoint}`, |
| | ) ?? assistants[0]?.id; |
| | } |
| |
|
| | if ( |
| | currentAssistantId && |
| | isAssistantEndpoint && |
| | conversation.conversationId === Constants.NEW_CONVO |
| | ) { |
| | const assistant = assistants.find((asst) => asst.id === currentAssistantId); |
| | conversation.model = assistant?.model; |
| | updateLastSelectedModel({ |
| | endpoint: defaultEndpoint, |
| | model: conversation.model, |
| | }); |
| | } |
| |
|
| | if (currentAssistantId && !isAssistantEndpoint) { |
| | conversation.assistant_id = undefined; |
| | } |
| |
|
| | const models = modelsConfig?.[defaultEndpoint] ?? []; |
| | conversation = buildDefaultConvo({ |
| | conversation, |
| | lastConversationSetup: activePreset as TConversation, |
| | endpoint: defaultEndpoint, |
| | models, |
| | }); |
| | } |
| |
|
| | if (disableParams === true) { |
| | conversation.disableParams = true; |
| | } |
| |
|
| | if (!(keepAddedConvos ?? false)) { |
| | clearAllConversations(true); |
| | } |
| | const isCancelled = conversation.conversationId?.startsWith('_'); |
| | if (isCancelled) { |
| | logger.log( |
| | 'conversation', |
| | 'Cancelled conversation, setting to `new` in `useNewConvo`', |
| | conversation, |
| | ); |
| | setConversation({ |
| | ...conversation, |
| | conversationId: Constants.NEW_CONVO as string, |
| | }); |
| | } else { |
| | logger.log('conversation', 'Setting conversation from `useNewConvo`', conversation); |
| | setConversation(conversation); |
| | } |
| | setSubmission({} as TSubmission); |
| | if (!(keepLatestMessage ?? false)) { |
| | logger.log('latest_message', 'Clearing all latest messages'); |
| | clearAllLatestMessages(); |
| | } |
| | if (isCancelled) { |
| | return; |
| | } |
| |
|
| | const searchParamsString = searchParams?.toString(); |
| | const getParams = () => (searchParamsString ? `?${searchParamsString}` : ''); |
| |
|
| | if (conversation.conversationId === Constants.NEW_CONVO && !modelsData) { |
| | const appTitle = localStorage.getItem(LocalStorageKeys.APP_TITLE) ?? ''; |
| | if (appTitle) { |
| | document.title = appTitle; |
| | } |
| | const path = `/c/${Constants.NEW_CONVO}${getParams()}`; |
| | navigate(path, { state: { focusChat: true } }); |
| | return; |
| | } |
| |
|
| | const path = `/c/${conversation.conversationId}${getParams()}`; |
| | navigate(path, { |
| | replace: true, |
| | state: disableFocus ? {} : { focusChat: true }, |
| | }); |
| | }, |
| | [endpointsConfig, defaultPreset, assistantsListMap, modelsQuery.data], |
| | ); |
| |
|
| | const newConversation = useCallback( |
| | function createNewConvo({ |
| | template: _template = {}, |
| | preset: _preset, |
| | modelsData, |
| | disableFocus, |
| | buildDefault = true, |
| | keepLatestMessage = false, |
| | keepAddedConvos = false, |
| | disableParams, |
| | }: { |
| | template?: Partial<TConversation>; |
| | preset?: Partial<TPreset>; |
| | modelsData?: TModelsConfig; |
| | buildDefault?: boolean; |
| | disableFocus?: boolean; |
| | keepLatestMessage?: boolean; |
| | keepAddedConvos?: boolean; |
| | disableParams?: boolean; |
| | } = {}) { |
| | pauseGlobalAudio(); |
| | if (!saveBadgesState) { |
| | resetBadges(); |
| | } |
| |
|
| | const templateConvoId = _template.conversationId ?? ''; |
| | const paramEndpoint = |
| | isParamEndpoint(_template.endpoint ?? '', _template.endpointType ?? '') === true || |
| | isParamEndpoint(_preset?.endpoint ?? '', _preset?.endpointType ?? ''); |
| | const template = |
| | paramEndpoint === true && templateConvoId && templateConvoId === Constants.NEW_CONVO |
| | ? { endpoint: _template.endpoint } |
| | : _template; |
| |
|
| | const conversation = { |
| | conversationId: Constants.NEW_CONVO as string, |
| | title: 'New Chat', |
| | endpoint: null, |
| | ...template, |
| | createdAt: '', |
| | updatedAt: '', |
| | }; |
| |
|
| | let preset = _preset; |
| | const result = getDefaultModelSpec(startupConfig); |
| | const defaultModelSpec = result?.default ?? result?.last; |
| | if ( |
| | !preset && |
| | startupConfig && |
| | (startupConfig.modelSpecs?.prioritize === true || |
| | (startupConfig.interface?.modelSelect ?? true) !== true || |
| | (result?.last != null && Object.keys(_template).length === 0)) && |
| | defaultModelSpec |
| | ) { |
| | preset = getModelSpecPreset(defaultModelSpec); |
| | } |
| |
|
| | applyModelSpecEffects({ |
| | startupConfig, |
| | specName: preset?.spec, |
| | convoId: conversation.conversationId, |
| | }); |
| |
|
| | if (conversation.conversationId === Constants.NEW_CONVO && !modelsData) { |
| | const filesToDelete = Array.from(files.values()) |
| | .filter( |
| | (file) => |
| | file.filepath != null && |
| | file.filepath !== '' && |
| | file.source && |
| | !(file.embedded ?? false) && |
| | file.temp_file_id, |
| | ) |
| | .map((file) => ({ |
| | file_id: file.file_id, |
| | embedded: !!(file.embedded ?? false), |
| | filepath: file.filepath as string, |
| | source: file.source as FileSources, |
| | })); |
| |
|
| | setFiles(new Map()); |
| | localStorage.setItem(LocalStorageKeys.FILES_TO_DELETE, JSON.stringify({})); |
| |
|
| | if (!saveDrafts && filesToDelete.length > 0) { |
| | mutateAsync({ files: filesToDelete }); |
| | } |
| | } |
| |
|
| | switchToConversation( |
| | conversation, |
| | preset, |
| | modelsData, |
| | buildDefault, |
| | keepLatestMessage, |
| | keepAddedConvos, |
| | disableFocus, |
| | disableParams, |
| | ); |
| | }, |
| | [ |
| | files, |
| | setFiles, |
| | saveDrafts, |
| | mutateAsync, |
| | resetBadges, |
| | startupConfig, |
| | saveBadgesState, |
| | pauseGlobalAudio, |
| | switchToConversation, |
| | applyModelSpecEffects, |
| | ], |
| | ); |
| |
|
| | return { |
| | switchToConversation, |
| | newConversation, |
| | }; |
| | }; |
| |
|
| | export default useNewConvo; |
| |
|