🔧 feat: deleteRagFile utility for Consistent RAG API document deletion (#11493)

* 🔧 feat: Implement deleteRagFile utility for RAG API document deletion across storage strategies

* chore: import order

* chore: import order & remove unnecessary comments

---------

Co-authored-by: Danny Avila <danacordially@gmail.com>
This commit is contained in:
ethanlaj 2026-02-14 13:49:36 -05:00 committed by Danny Avila
parent a89945c24b
commit 2513e0a423
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
7 changed files with 221 additions and 46 deletions

View file

@ -4,7 +4,7 @@ const mime = require('mime');
const axios = require('axios');
const fetch = require('node-fetch');
const { logger } = require('@librechat/data-schemas');
const { getAzureContainerClient } = require('@librechat/api');
const { getAzureContainerClient, deleteRagFile } = require('@librechat/api');
const defaultBasePath = 'images';
const { AZURE_STORAGE_PUBLIC_ACCESS = 'true', AZURE_CONTAINER_NAME = 'files' } = process.env;
@ -102,6 +102,8 @@ async function getAzureURL({ fileName, basePath = defaultBasePath, userId, conta
* @param {MongoFile} params.file - The file object.
*/
async function deleteFileFromAzure(req, file) {
await deleteRagFile({ userId: req.user.id, file });
try {
const containerClient = await getAzureContainerClient(AZURE_CONTAINER_NAME);
const blobPath = file.filepath.split(`${AZURE_CONTAINER_NAME}/`)[1];

View file

@ -3,7 +3,7 @@ const path = require('path');
const axios = require('axios');
const fetch = require('node-fetch');
const { logger } = require('@librechat/data-schemas');
const { getFirebaseStorage } = require('@librechat/api');
const { getFirebaseStorage, deleteRagFile } = require('@librechat/api');
const { ref, uploadBytes, getDownloadURL, deleteObject } = require('firebase/storage');
const { getBufferMetadata } = require('~/server/utils');
@ -167,27 +167,7 @@ function extractFirebaseFilePath(urlString) {
* Throws an error if there is an issue with deletion.
*/
const deleteFirebaseFile = async (req, file) => {
if (file.embedded && process.env.RAG_API_URL) {
const jwtToken = req.headers.authorization.split(' ')[1];
try {
await axios.delete(`${process.env.RAG_API_URL}/documents`, {
headers: {
Authorization: `Bearer ${jwtToken}`,
'Content-Type': 'application/json',
accept: 'application/json',
},
data: [file.file_id],
});
} catch (error) {
if (error.response?.status === 404) {
logger.warn(
`[deleteFirebaseFile] Document ${file.file_id} not found in RAG API, may have been deleted already`,
);
} else {
logger.error('[deleteFirebaseFile] Error deleting document from RAG API:', error);
}
}
}
await deleteRagFile({ userId: req.user.id, file });
const fileName = extractFirebaseFilePath(file.filepath);
if (!fileName.includes(req.user.id)) {

View file

@ -1,9 +1,9 @@
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const { deleteRagFile } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const { EModelEndpoint } = require('librechat-data-provider');
const { generateShortLivedToken } = require('@librechat/api');
const { resizeImageBuffer } = require('~/server/services/Files/images/resize');
const { getBufferMetadata } = require('~/server/utils');
const paths = require('~/config/paths');
@ -213,27 +213,7 @@ const deleteLocalFile = async (req, file) => {
/** Filepath stripped of query parameters (e.g., ?manual=true) */
const cleanFilepath = file.filepath.split('?')[0];
if (file.embedded && process.env.RAG_API_URL) {
const jwtToken = generateShortLivedToken(req.user.id);
try {
await axios.delete(`${process.env.RAG_API_URL}/documents`, {
headers: {
Authorization: `Bearer ${jwtToken}`,
'Content-Type': 'application/json',
accept: 'application/json',
},
data: [file.file_id],
});
} catch (error) {
if (error.response?.status === 404) {
logger.warn(
`[deleteLocalFile] Document ${file.file_id} not found in RAG API, may have been deleted already`,
);
} else {
logger.error('[deleteLocalFile] Error deleting document from RAG API:', error);
}
}
}
await deleteRagFile({ userId: req.user.id, file });
if (cleanFilepath.startsWith(`/uploads/${req.user.id}`)) {
const userUploadDir = path.join(uploads, req.user.id);

View file

@ -1,9 +1,9 @@
const fs = require('fs');
const fetch = require('node-fetch');
const { initializeS3 } = require('@librechat/api');
const { logger } = require('@librechat/data-schemas');
const { FileSources } = require('librechat-data-provider');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
const { initializeS3, deleteRagFile } = require('@librechat/api');
const {
PutObjectCommand,
GetObjectCommand,
@ -142,6 +142,8 @@ async function saveURLToS3({ userId, URL, fileName, basePath = defaultBasePath }
* @returns {Promise<void>}
*/
async function deleteFileFromS3(req, file) {
await deleteRagFile({ userId: req.user.id, file });
const key = extractKeyFromS3Url(file.filepath);
const params = { Bucket: bucketName, Key: key };
if (!key.includes(req.user.id)) {