import { QueryKeys, dataService, defaultOrderQuery, defaultAssistantsVersion, } from 'librechat-data-provider'; import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import type { UseInfiniteQueryOptions, QueryObserverResult, UseQueryOptions, UseQueryResult, } from '@tanstack/react-query'; import type t from 'librechat-data-provider'; import type { Action, TPreset, TFile, TPlugin, FileConfig, ConversationListResponse, ConversationListParams, Assistant, AssistantListParams, AssistantListResponse, AssistantDocument, TEndpointsConfig, TCheckUserKeyResponse, SharedLinkListParams, SharedLinksResponse, } from 'librechat-data-provider'; import { findPageForConversation, addFileToCache } from '~/utils'; export const useGetFiles = ( config?: UseQueryOptions, ): QueryObserverResult => { return useQuery([QueryKeys.files], () => dataService.getFiles(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }); }; export const useGetFileConfig = ( config?: UseQueryOptions, ): QueryObserverResult => { return useQuery( [QueryKeys.fileConfig], () => dataService.getFileConfig(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }, ); }; export const useGetPresetsQuery = ( config?: UseQueryOptions, ): QueryObserverResult => { return useQuery([QueryKeys.presets], () => dataService.getPresets(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }); }; export const useGetEndpointsConfigOverride = ( config?: UseQueryOptions, ): QueryObserverResult => { return useQuery( [QueryKeys.endpointsConfigOverride], () => dataService.getEndpointsConfigOverride(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }, ); }; export const useGetConvoIdQuery = ( id: string, config?: UseQueryOptions, ): QueryObserverResult => { const queryClient = useQueryClient(); return useQuery( [QueryKeys.conversation, id], () => { const defaultQuery = () => dataService.getConversationById(id); const convosQuery = queryClient.getQueryData([ QueryKeys.allConversations, ]); if (!convosQuery) { return defaultQuery(); } const { pageIndex, index } = findPageForConversation(convosQuery, { conversationId: id }); if (pageIndex > -1 && index > -1) { return convosQuery.pages[pageIndex].conversations[index]; } return defaultQuery(); }, { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }, ); }; export const useSearchInfiniteQuery = ( params?: ConversationListParams & { searchQuery?: string }, config?: UseInfiniteQueryOptions, ) => { return useInfiniteQuery( [QueryKeys.searchConversations, params], // Include the searchQuery in the query key ({ pageParam = '1' }) => dataService.listConversationsByQuery({ ...params, pageNumber: pageParam }), { getNextPageParam: (lastPage) => { const currentPageNumber = Number(lastPage.pageNumber); const totalPages = Number(lastPage.pages); return currentPageNumber < totalPages ? currentPageNumber + 1 : undefined; }, refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }, ); }; export const useConversationsInfiniteQuery = ( params?: ConversationListParams, config?: UseInfiniteQueryOptions, ) => { return useInfiniteQuery( params?.isArchived ? [QueryKeys.archivedConversations] : [QueryKeys.allConversations], ({ pageParam = '' }) => dataService.listConversations({ ...params, pageNumber: pageParam?.toString(), isArchived: params?.isArchived || false, }), { getNextPageParam: (lastPage) => { const currentPageNumber = Number(lastPage.pageNumber); const totalPages = Number(lastPage.pages); // Convert totalPages to a number // If the current page number is less than total pages, return the next page number return currentPageNumber < totalPages ? currentPageNumber + 1 : undefined; }, refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }, ); }; export const useSharedLinksInfiniteQuery = ( params?: SharedLinkListParams, config?: UseInfiniteQueryOptions, ) => { return useInfiniteQuery( [QueryKeys.sharedLinks], ({ pageParam = '' }) => dataService.listSharedLinks({ ...params, pageNumber: pageParam?.toString(), isPublic: params?.isPublic || true, }), { getNextPageParam: (lastPage) => { const currentPageNumber = Number(lastPage.pageNumber); const totalPages = Number(lastPage.pages); // Convert totalPages to a number // If the current page number is less than total pages, return the next page number return currentPageNumber < totalPages ? currentPageNumber + 1 : undefined; }, refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }, ); }; /** * ASSISTANTS */ /** * Hook for getting all available tools for Assistants */ export const useAvailableToolsQuery = ( endpoint: t.AssistantsEndpoint, ): QueryObserverResult => { const queryClient = useQueryClient(); const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]); const keyExpiry = queryClient.getQueryData([QueryKeys.name, endpoint]); const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide; const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true; const enabled = !!endpointsConfig?.[endpoint] && keyProvided; const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint]; return useQuery( [QueryKeys.tools], () => dataService.getAvailableTools(version, endpoint), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, enabled, }, ); }; /** * Hook for listing all assistants, with optional parameters provided for pagination and sorting */ export const useListAssistantsQuery = ( endpoint: t.AssistantsEndpoint, params: Omit = defaultOrderQuery, config?: UseQueryOptions, ): QueryObserverResult => { const queryClient = useQueryClient(); const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]); const keyExpiry = queryClient.getQueryData([QueryKeys.name, endpoint]); const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide; const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true; const enabled = !!endpointsConfig?.[endpoint] && keyProvided; const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint]; return useQuery( [QueryKeys.assistants, endpoint, params], () => dataService.listAssistants({ ...params, endpoint }, version), { // Example selector to sort them by created_at // select: (res) => { // return res.data.sort((a, b) => a.created_at - b.created_at); // }, refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, retry: false, ...config, enabled: config?.enabled !== undefined ? config?.enabled && enabled : enabled, }, ); }; /* export const useListAssistantsInfiniteQuery = ( params?: AssistantListParams, config?: UseInfiniteQueryOptions, ) => { const queryClient = useQueryClient(); const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]); const keyExpiry = queryClient.getQueryData([ QueryKeys.name, EModelEndpoint.assistants, ]); const userProvidesKey = !!endpointsConfig?.[EModelEndpoint.assistants]?.userProvide; const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true; const enabled = !!endpointsConfig?.[EModelEndpoint.assistants] && keyProvided; return useInfiniteQuery( ['assistantsList', params], ({ pageParam = '' }) => dataService.listAssistants({ ...params, after: pageParam }), { getNextPageParam: (lastPage) => { // lastPage is of type AssistantListResponse, you can use the has_more and last_id from it directly if (lastPage.has_more) { return lastPage.last_id; } return undefined; }, ...config, enabled: config?.enabled !== undefined ? config?.enabled && enabled : enabled, }, ); }; */ /** * Hook for retrieving details about a single assistant */ export const useGetAssistantByIdQuery = ( endpoint: t.AssistantsEndpoint, assistant_id: string, config?: UseQueryOptions, ): QueryObserverResult => { const queryClient = useQueryClient(); const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]); const keyExpiry = queryClient.getQueryData([QueryKeys.name, endpoint]); const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide; const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true; const enabled = !!endpointsConfig?.[endpoint] && keyProvided; const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint]; return useQuery( [QueryKeys.assistant, assistant_id], () => dataService.getAssistantById({ endpoint, assistant_id, version, }), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, retry: false, ...config, // Query will not execute until the assistant_id exists enabled: config?.enabled !== undefined ? config?.enabled && enabled : enabled, }, ); }; /** * Hook for retrieving user's saved Assistant Actions */ export const useGetActionsQuery = ( endpoint: t.AssistantsEndpoint, config?: UseQueryOptions, ): QueryObserverResult => { const queryClient = useQueryClient(); const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]); const keyExpiry = queryClient.getQueryData([QueryKeys.name, endpoint]); const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide; const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true; const enabled = !!endpointsConfig?.[endpoint] && keyProvided; const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint]; return useQuery( [QueryKeys.actions], () => dataService.getActions({ endpoint, version, }), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, enabled: config?.enabled !== undefined ? config?.enabled && enabled : enabled, }, ); }; /** * Hook for retrieving user's saved Assistant Documents (metadata saved to Database) */ export const useGetAssistantDocsQuery = ( endpoint: t.AssistantsEndpoint, config?: UseQueryOptions, ): QueryObserverResult => { const queryClient = useQueryClient(); const endpointsConfig = queryClient.getQueryData([QueryKeys.endpoints]); const keyExpiry = queryClient.getQueryData([QueryKeys.name, endpoint]); const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide; const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true; const enabled = !!endpointsConfig?.[endpoint] && keyProvided; const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint]; return useQuery( [QueryKeys.assistantDocs], () => dataService.getAssistantDocs({ endpoint, version, }), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, enabled: config?.enabled !== undefined ? config?.enabled && enabled : enabled, }, ); }; export const useFileDownload = (userId?: string, file_id?: string): QueryObserverResult => { const queryClient = useQueryClient(); return useQuery( [QueryKeys.fileDownload, file_id], async () => { if (!userId || !file_id) { console.warn('No user ID provided for file download'); return; } const response = await dataService.getFileDownload(userId, file_id); const blob = response.data; const downloadURL = window.URL.createObjectURL(blob); try { const metadata: TFile | undefined = JSON.parse(response.headers['x-file-metadata']); if (!metadata) { console.warn('No metadata found for file download', response.headers); return downloadURL; } addFileToCache(queryClient, metadata); } catch (e) { console.error('Error parsing file metadata, skipped updating file query cache', e); } return downloadURL; }, { enabled: false, retry: false, }, ); }; /** STT/TTS */ /* Text to speech voices */ export const useVoicesQuery = (): UseQueryResult => { return useQuery([QueryKeys.voices], () => dataService.getVoices()); }; /* Custom config speech */ export const useCustomConfigSpeechQuery = (): UseQueryResult => { return useQuery([QueryKeys.customConfigSpeech], () => dataService.getCustomConfigSpeech()); }; /** Prompt */ export const usePromptGroupsInfiniteQuery = ( params?: t.TPromptGroupsWithFilterRequest, config?: UseInfiniteQueryOptions, ) => { const { name, pageSize, category, ...rest } = params || {}; return useInfiniteQuery( [QueryKeys.promptGroups, name, category, pageSize], ({ pageParam = '1' }) => dataService.getPromptGroups({ ...rest, name, category: category || '', pageNumber: pageParam?.toString(), pageSize: (pageSize || 10).toString(), }), { getNextPageParam: (lastPage) => { const currentPageNumber = Number(lastPage.pageNumber); const totalPages = Number(lastPage.pages); return currentPageNumber < totalPages ? currentPageNumber + 1 : undefined; }, refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, }, ); }; export const useGetPromptGroup = ( id: string, config?: UseQueryOptions, ): QueryObserverResult => { return useQuery( [QueryKeys.promptGroup, id], () => dataService.getPromptGroup(id), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, retry: false, ...config, enabled: config?.enabled !== undefined ? config?.enabled : true, }, ); }; export const useGetPrompts = ( filter: t.TPromptsWithFilterRequest, config?: UseQueryOptions, ): QueryObserverResult => { return useQuery( [QueryKeys.prompts, filter.groupId ?? ''], () => dataService.getPrompts(filter), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, retry: false, ...config, enabled: config?.enabled !== undefined ? config?.enabled : true, }, ); }; export const useGetAllPromptGroups = ( filter?: t.AllPromptGroupsFilterRequest, config?: UseQueryOptions, ): QueryObserverResult => { return useQuery( [QueryKeys.allPromptGroups], () => dataService.getAllPromptGroups(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, retry: false, ...config, }, ); }; export const useGetCategories = ( config?: UseQueryOptions, ): QueryObserverResult => { return useQuery( [QueryKeys.categories], () => dataService.getCategories(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, retry: false, ...config, enabled: config?.enabled !== undefined ? config?.enabled : true, }, ); }; export const useGetRandomPrompts = ( filter: t.TGetRandomPromptsRequest, config?: UseQueryOptions, ): QueryObserverResult => { return useQuery( [QueryKeys.randomPrompts], () => dataService.getRandomPrompts(filter), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, retry: false, ...config, enabled: config?.enabled !== undefined ? config?.enabled : true, }, ); };