import { EToolResources } from 'librechat-data-provider'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { QueryKeys, dataService, MutationKeys, defaultOrderQuery, isAssistantsEndpoint, } from 'librechat-data-provider'; import type * as t from 'librechat-data-provider'; import type { UseMutationResult } from '@tanstack/react-query'; import { useToastContext } from '~/Providers'; import { useLocalize } from '~/hooks'; export const useUploadFileMutation = ( _options?: t.UploadMutationOptions, signal?: AbortSignal | null, ): UseMutationResult< t.TFileUpload, // response data unknown, // error FormData, // request unknown // context > => { const queryClient = useQueryClient(); const { onSuccess, ...options } = _options || {}; return useMutation([MutationKeys.fileUpload], { mutationFn: (body: FormData) => { const width = body.get('width') ?? ''; const height = body.get('height') ?? ''; const version = body.get('version') ?? ''; const endpoint = (body.get('endpoint') ?? '') as string; if (isAssistantsEndpoint(endpoint) && version === '2') { return dataService.uploadFile(body, signal); } if (width !== '' && height !== '') { return dataService.uploadImage(body, signal); } return dataService.uploadFile(body, signal); }, ...options, onSuccess: (data, formData, context) => { queryClient.setQueryData([QueryKeys.files], (_files) => [ data, ...(_files ?? []), ]); const endpoint = formData.get('endpoint'); const message_file = formData.get('message_file'); const agent_id = (formData.get('agent_id') as string | undefined) ?? ''; const assistant_id = (formData.get('assistant_id') as string | undefined) ?? ''; const tool_resource = (formData.get('tool_resource') as string | undefined) ?? ''; if (message_file === 'true') { onSuccess?.(data, formData, context); return; } if (agent_id && tool_resource) { queryClient.setQueryData([QueryKeys.agent, agent_id], (agent) => { if (!agent) { return agent; } const update = {}; const prevResources = agent.tool_resources ?? {}; const prevResource: t.ExecuteCodeResource | t.AgentFileResource = agent.tool_resources?.[ tool_resource ] ?? { file_ids: [], }; if (!prevResource.file_ids) { prevResource.file_ids = []; } prevResource.file_ids.push(data.file_id); update['tool_resources'] = { ...prevResources, [tool_resource]: prevResource, }; if (!agent.tools?.includes(tool_resource)) { update['tools'] = [...(agent.tools ?? []), tool_resource]; } return { ...agent, ...update, }; }); } if (!assistant_id) { onSuccess?.(data, formData, context); return; } queryClient.setQueryData( [QueryKeys.assistants, endpoint, defaultOrderQuery], (prev) => { if (!prev) { return prev; } return { ...prev, data: prev.data.map((assistant) => { if (assistant.id !== assistant_id) { return assistant; } const update = {}; if (!tool_resource) { update['file_ids'] = [...(assistant.file_ids ?? []), data.file_id]; } if (tool_resource === EToolResources.code_interpreter) { const prevResources = assistant.tool_resources ?? {}; const prevResource = assistant.tool_resources?.[tool_resource] ?? { file_ids: [], }; if (!prevResource.file_ids) { prevResource.file_ids = []; } prevResource.file_ids.push(data.file_id); update['tool_resources'] = { ...prevResources, [tool_resource]: prevResource, }; } return { ...assistant, ...update, }; }), }; }, ); onSuccess?.(data, formData, context); }, }); }; export const useDeleteFilesMutation = ( _options?: t.DeleteMutationOptions, ): UseMutationResult< t.DeleteFilesResponse, // response data unknown, // error t.DeleteFilesBody, // request unknown // context > => { const queryClient = useQueryClient(); const { showToast } = useToastContext(); const localize = useLocalize(); const { onSuccess, onError, ...options } = _options || {}; return useMutation([MutationKeys.fileDelete], { mutationFn: (body: t.DeleteFilesBody) => dataService.deleteFiles(body), ...options, onError: (error, vars, context) => { if (error && typeof error === 'object' && 'response' in error) { const errorWithResponse = error as { response?: { status?: number } }; if (errorWithResponse.response?.status === 403) { showToast({ message: localize('com_ui_delete_not_allowed'), status: 'error', }); } } onError?.(error, vars, context); }, onSuccess: (data, vars, context) => { queryClient.setQueryData([QueryKeys.files], (cachefiles) => { const { files: filesDeleted } = vars; const fileMap = filesDeleted.reduce((acc, file) => { acc.set(file.file_id, file); return acc; }, new Map()); return (cachefiles ?? []).filter((file) => !fileMap.has(file.file_id)); }); showToast({ message: localize('com_ui_delete_success'), status: 'success', }); onSuccess?.(data, vars, context); if (vars.agent_id != null && vars.agent_id) { queryClient.refetchQueries([QueryKeys.agent, vars.agent_id]); } }, }); };