mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-21 02:40:14 +01:00
⬆️ feat: Cancel chat file uploads; fix: Assistant uploads (#4433)
* refactor: move file mutations to dedicated file, improve typing * refactor(ChatForm): utilize FileFormWrapper to consolidate file upload logic/rendering to single parent * refactor: better TSX heirarchies between AttachFile and FileFormWrapper * refactor: `abortUpload` WIP * fix: file debugging and file upload issues * refactor: reject promise outright if axios intercepted error does not include response property * chore: bump data-provider version to 0.7.428 * refactor: Add return type to localize function in Translation.ts * refactor: allow message file attachment upload request cancellations, and add localizations for file upload errors * refactor: include Azure OpenAI in paramEndpoints set * fix: assistant form uploads and better typing * refactor: consolidate logic
This commit is contained in:
parent
0870acd086
commit
65888c274a
20 changed files with 419 additions and 311 deletions
159
client/src/data-provider/Files/mutations.ts
Normal file
159
client/src/data-provider/Files/mutations.ts
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import { EToolResources } from 'librechat-data-provider';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
|
||||
export type TGenTitleMutation = UseMutationResult<
|
||||
t.TGenTitleResponse,
|
||||
unknown,
|
||||
t.TGenTitleRequest,
|
||||
unknown
|
||||
>;
|
||||
|
||||
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') ?? '';
|
||||
|
||||
if (width !== '' && height !== '' && (version !== '' || version.toString() !== '2')) {
|
||||
return dataService.uploadImage(body, signal);
|
||||
}
|
||||
|
||||
return dataService.uploadFile(body, signal);
|
||||
},
|
||||
...options,
|
||||
onSuccess: (data, formData, context) => {
|
||||
queryClient.setQueryData<t.TFile[] | undefined>([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<t.Agent>([QueryKeys.agent, agent_id], (agent) => {
|
||||
if (!agent) {
|
||||
return agent;
|
||||
}
|
||||
|
||||
const update = {};
|
||||
const prevResources = agent.tool_resources ?? {};
|
||||
const prevResource: t.ExecuteCodeResource | t.AgentFileSearchResource = 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,
|
||||
};
|
||||
return {
|
||||
...agent,
|
||||
...update,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (!assistant_id) {
|
||||
onSuccess?.(data, formData, context);
|
||||
return;
|
||||
}
|
||||
|
||||
queryClient.setQueryData<t.AssistantListResponse>(
|
||||
[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 { onSuccess, ...options } = _options || {};
|
||||
return useMutation([MutationKeys.fileDelete], {
|
||||
mutationFn: (body: t.DeleteFilesBody) => dataService.deleteFiles(body),
|
||||
...options,
|
||||
onSuccess: (data, ...args) => {
|
||||
queryClient.setQueryData<t.TFile[] | undefined>([QueryKeys.files], (cachefiles) => {
|
||||
const { files: filesDeleted } = args[0];
|
||||
|
||||
const fileMap = filesDeleted.reduce((acc, file) => {
|
||||
acc.set(file.file_id, file);
|
||||
return acc;
|
||||
}, new Map<string, t.BatchFile>());
|
||||
|
||||
return (cachefiles ?? []).filter((file) => !fileMap.has(file.file_id));
|
||||
});
|
||||
onSuccess?.(data, ...args);
|
||||
},
|
||||
});
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue