⬇️ feat: Assistant File Downloads (#2234)

* WIP: basic route for file downloads and file strategy for generating readablestream to pipe as res

* chore(DALLE3): add typing for OpenAI client

* chore: add `CONSOLE_JSON` notes to dotenv.md

* WIP: first pass OpenAI Assistants File Output handling

* feat: first pass assistants output file download from openai

* chore: yml vs. yaml variation to .gitignore for `librechat.yml`

* refactor(retrieveAndProcessFile): remove redundancies

* fix(syncMessages): explicit sort of apiMessages to fix message order on abort

* chore: add logs for warnings and errors, show toast on frontend

* chore: add logger where console was still being used
This commit is contained in:
Danny Avila 2024-03-29 08:23:38 -04:00 committed by GitHub
parent 7945fea0f9
commit a00756c469
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 555 additions and 248 deletions

View file

@ -2,9 +2,10 @@ const fs = require('fs');
const path = require('path');
const axios = require('axios');
const fetch = require('node-fetch');
const { ref, uploadBytes, getDownloadURL, deleteObject } = require('firebase/storage');
const { ref, uploadBytes, getDownloadURL, getStream, deleteObject } = require('firebase/storage');
const { getBufferMetadata } = require('~/server/utils');
const { getFirebaseStorage } = require('./initialize');
const { logger } = require('~/config');
/**
* Deletes a file from Firebase Storage.
@ -15,7 +16,7 @@ const { getFirebaseStorage } = require('./initialize');
async function deleteFile(basePath, fileName) {
const storage = getFirebaseStorage();
if (!storage) {
console.error('Firebase is not initialized. Cannot delete file from Firebase Storage.');
logger.error('Firebase is not initialized. Cannot delete file from Firebase Storage.');
throw new Error('Firebase is not initialized');
}
@ -23,9 +24,9 @@ async function deleteFile(basePath, fileName) {
try {
await deleteObject(storageRef);
console.log('File deleted successfully from Firebase Storage');
logger.debug('File deleted successfully from Firebase Storage');
} catch (error) {
console.error('Error deleting file from Firebase Storage:', error.message);
logger.error('Error deleting file from Firebase Storage:', error.message);
throw error;
}
}
@ -51,7 +52,7 @@ async function deleteFile(basePath, fileName) {
async function saveURLToFirebase({ userId, URL, fileName, basePath = 'images' }) {
const storage = getFirebaseStorage();
if (!storage) {
console.error('Firebase is not initialized. Cannot save file to Firebase Storage.');
logger.error('Firebase is not initialized. Cannot save file to Firebase Storage.');
return null;
}
@ -63,7 +64,7 @@ async function saveURLToFirebase({ userId, URL, fileName, basePath = 'images' })
await uploadBytes(storageRef, buffer);
return await getBufferMetadata(buffer);
} catch (error) {
console.error('Error uploading file to Firebase Storage:', error.message);
logger.error('Error uploading file to Firebase Storage:', error.message);
return null;
}
}
@ -87,7 +88,7 @@ async function saveURLToFirebase({ userId, URL, fileName, basePath = 'images' })
async function getFirebaseURL({ fileName, basePath = 'images' }) {
const storage = getFirebaseStorage();
if (!storage) {
console.error('Firebase is not initialized. Cannot get image URL from Firebase Storage.');
logger.error('Firebase is not initialized. Cannot get image URL from Firebase Storage.');
return null;
}
@ -96,7 +97,7 @@ async function getFirebaseURL({ fileName, basePath = 'images' }) {
try {
return await getDownloadURL(storageRef);
} catch (error) {
console.error('Error fetching file URL from Firebase Storage:', error.message);
logger.error('Error fetching file URL from Firebase Storage:', error.message);
return null;
}
}
@ -212,6 +213,26 @@ async function uploadFileToFirebase({ req, file, file_id }) {
return { filepath: downloadURL, bytes };
}
/**
* Retrieves a readable stream for a file from Firebase storage.
*
* @param {string} filepath - The filepath.
* @returns {ReadableStream} A readable stream of the file.
*/
function getFirebaseFileStream(filepath) {
try {
const storage = getFirebaseStorage();
if (!storage) {
throw new Error('Firebase is not initialized');
}
const fileRef = ref(storage, filepath);
return getStream(fileRef);
} catch (error) {
logger.error('Error getting Firebase file stream:', error);
throw error;
}
}
module.exports = {
deleteFile,
getFirebaseURL,
@ -219,4 +240,5 @@ module.exports = {
deleteFirebaseFile,
uploadFileToFirebase,
saveBufferToFirebase,
getFirebaseFileStream,
};