diff --git a/client/src/components/Prompts/PromptForm.tsx b/client/src/components/Prompts/PromptForm.tsx index c3c87f642c..9a760dbd7d 100644 --- a/client/src/components/Prompts/PromptForm.tsx +++ b/client/src/components/Prompts/PromptForm.tsx @@ -11,6 +11,7 @@ import { ResourceType, PermissionBits, PermissionTypes, + EToolResources, } from 'librechat-data-provider'; import type { TCreatePrompt, TPrompt, TPromptGroup } from 'librechat-data-provider'; import { @@ -193,16 +194,18 @@ const PromptForm = () => { handleFileRemove, setFiles, } = usePromptFileHandling({ - onFileChange: () => { + onFileChange: (updatedFiles) => { // Auto-save when files are added/removed - console.log('onFileChange called', { canEdit, selectedPrompt: !!selectedPrompt }); + console.log('onFileChange called', { + canEdit, + selectedPrompt: !!selectedPrompt, + updatedFiles, + }); if (canEdit && selectedPrompt) { const currentPromptText = getValues('prompt'); - console.log('Calling onSave with:', currentPromptText); - // Use setTimeout to ensure file state is updated before calling onSave - setTimeout(() => { - onSave(currentPromptText); - }, 100); + console.log('Calling onSave with:', currentPromptText, 'and updated files:', updatedFiles); + // Call onSave with the updated files to ensure correct tool_resources + onSave(currentPromptText, updatedFiles); } }, }); @@ -280,8 +283,33 @@ const PromptForm = () => { }, }); + // Helper function to get tool resources from a specific files array + const getToolResourcesFromFiles = useCallback((files: ExtendedFile[]) => { + if (files.length === 0) { + return undefined; + } + + const toolResources: AgentToolResources = {}; + + files.forEach((file) => { + if (!file.file_id) return; // Skip files that haven't been uploaded yet + + // Initialize the tool resource if it doesn't exist + if (!toolResources[file.tool_resource]) { + toolResources[file.tool_resource] = { file_ids: [] }; + } + + // Add file_id to the appropriate tool resource + if (!toolResources[file.tool_resource]!.file_ids!.includes(file.file_id)) { + toolResources[file.tool_resource]!.file_ids!.push(file.file_id); + } + }); + + return Object.keys(toolResources).length > 0 ? toolResources : undefined; + }, []); + const onSave = useCallback( - (value: string) => { + (value: string, updatedFiles?: ExtendedFile[]) => { if (!canEdit) { return; } @@ -299,7 +327,10 @@ const PromptForm = () => { return; } - const toolResources = getToolResources(); + // Use updated files if provided, otherwise use current hook state + const toolResources = updatedFiles + ? getToolResourcesFromFiles(updatedFiles) + : getToolResources(); const tempPrompt: TCreatePrompt = { prompt: { type: selectedPrompt.type ?? 'text', diff --git a/client/src/hooks/Prompts/usePromptFileHandling.ts b/client/src/hooks/Prompts/usePromptFileHandling.ts index d88340b00f..2b6eaf56ac 100644 --- a/client/src/hooks/Prompts/usePromptFileHandling.ts +++ b/client/src/hooks/Prompts/usePromptFileHandling.ts @@ -4,14 +4,14 @@ import { useToastContext } from '@librechat/client'; import { EModelEndpoint, EToolResources, FileSources } from 'librechat-data-provider'; import type { AgentToolResources, TFile } from 'librechat-data-provider'; import type { ExtendedFile } from '~/common'; -import { useUploadFileMutation, useGetFiles, useDeleteFilesMutation } from '~/data-provider'; +import { useUploadFileMutation, useGetFiles } from '~/data-provider'; import { useAuthContext } from '~/hooks'; import { logger } from '~/utils'; interface UsePromptFileHandling { fileSetter?: (files: ExtendedFile[]) => void; initialFiles?: ExtendedFile[]; - onFileChange?: () => void; // Callback when files are added/removed + onFileChange?: (updatedFiles: ExtendedFile[]) => void; // Callback when files are added/removed } /** @@ -38,16 +38,6 @@ export const usePromptFileHandling = (params?: UsePromptFileHandling) => { const [filesLoading, setFilesLoading] = useState(false); const abortControllerRef = useRef(null); - const deleteFileMutation = useDeleteFilesMutation({ - onSuccess: () => { - params?.onFileChange?.(); - }, - onError: (error) => { - logger.error('Error deleting file:', error); - params?.onFileChange?.(); - }, - }); - const uploadFile = useUploadFileMutation({ onSuccess: (data) => { logger.log('File uploaded successfully', data); @@ -80,8 +70,27 @@ export const usePromptFileHandling = (params?: UsePromptFileHandling) => { status: 'success', }); - // Call the onFileChange callback to trigger save - params?.onFileChange?.(); + // Call the onFileChange callback to trigger save with updated files + const updatedFiles = files.map((file) => { + if (file.temp_file_id === data.temp_file_id) { + return { + ...file, + file_id: data.file_id, + filepath: data.filepath, + progress: 1, + attached: true, + preview: data.filepath || file.preview, + filename: data.filename || file.filename, + type: data.type || file.type, + size: data.bytes || file.size, + width: data.width || file.width, + height: data.height || file.height, + source: data.source || file.source, + }; + } + return file; + }); + params?.onFileChange?.(updatedFiles); }, onError: (error, body) => { logger.error('File upload error:', error); @@ -228,8 +237,8 @@ export const usePromptFileHandling = (params?: UsePromptFileHandling) => { // Handle file removal const handleFileRemove = useCallback( (fileId: string) => { - // Call delete API to remove from database - deleteFileMutation.mutate([fileId]); + // For prompts, we only remove the file from the current prompt's tool_resources + // We don't delete the file from the database to preserve previous versions setFiles((prev) => { return prev.filter((file) => { @@ -243,8 +252,17 @@ export const usePromptFileHandling = (params?: UsePromptFileHandling) => { return true; // Keep this file }); }); + + // Call the onFileChange callback to trigger prompt version update with updated files + const updatedFiles = files.filter((file) => { + if (file.file_id === fileId || file.temp_file_id === fileId) { + return false; // Remove this file + } + return true; // Keep this file + }); + params?.onFileChange?.(updatedFiles); }, - [deleteFileMutation, params?.onFileChange], + [params?.onFileChange], ); // Sync with external fileSetter when files change