🔧 fix: Resizable Panel Unmount Error & Code Env. File Re-Upload (#5947)

* 🔧 refactor: handle full path for code env. file re-upload

* fix: update react-resizable-panels to version 2.1.7 to resolve error thrown on unmount of artifacts; ref: https://github.com/bvaughn/react-resizable-panels/issues/372

* refactor: replace promptPrefix with systemMessage in GoogleClient for improved clarity, and to prevent saving LibreChat feature-specific instructions to the user's custom instructions
This commit is contained in:
Danny Avila 2025-02-19 14:53:22 -05:00 committed by GitHub
parent 538a2a144a
commit fdb3cf3f58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 48 additions and 25 deletions

View file

@ -51,7 +51,7 @@ class GoogleClient extends BaseClient {
const serviceKey = creds[AuthKeys.GOOGLE_SERVICE_KEY] ?? {}; const serviceKey = creds[AuthKeys.GOOGLE_SERVICE_KEY] ?? {};
this.serviceKey = this.serviceKey =
serviceKey && typeof serviceKey === 'string' ? JSON.parse(serviceKey) : serviceKey ?? {}; serviceKey && typeof serviceKey === 'string' ? JSON.parse(serviceKey) : (serviceKey ?? {});
/** @type {string | null | undefined} */ /** @type {string | null | undefined} */
this.project_id = this.serviceKey.project_id; this.project_id = this.serviceKey.project_id;
this.client_email = this.serviceKey.client_email; this.client_email = this.serviceKey.client_email;
@ -73,6 +73,8 @@ class GoogleClient extends BaseClient {
* @type {string} */ * @type {string} */
this.outputTokensKey = 'output_tokens'; this.outputTokensKey = 'output_tokens';
this.visionMode = VisionModes.generative; this.visionMode = VisionModes.generative;
/** @type {string} */
this.systemMessage;
if (options.skipSetOptions) { if (options.skipSetOptions) {
return; return;
} }
@ -184,7 +186,7 @@ class GoogleClient extends BaseClient {
if (typeof this.options.artifactsPrompt === 'string' && this.options.artifactsPrompt) { if (typeof this.options.artifactsPrompt === 'string' && this.options.artifactsPrompt) {
promptPrefix = `${promptPrefix ?? ''}\n${this.options.artifactsPrompt}`.trim(); promptPrefix = `${promptPrefix ?? ''}\n${this.options.artifactsPrompt}`.trim();
} }
this.options.promptPrefix = promptPrefix; this.systemMessage = promptPrefix;
this.initializeClient(); this.initializeClient();
return this; return this;
} }
@ -314,7 +316,7 @@ class GoogleClient extends BaseClient {
} }
this.augmentedPrompt = await this.contextHandlers.createContext(); this.augmentedPrompt = await this.contextHandlers.createContext();
this.options.promptPrefix = this.augmentedPrompt + this.options.promptPrefix; this.systemMessage = this.augmentedPrompt + this.systemMessage;
} }
} }
@ -361,8 +363,8 @@ class GoogleClient extends BaseClient {
throw new Error('[GoogleClient] PaLM 2 and Codey models are no longer supported.'); throw new Error('[GoogleClient] PaLM 2 and Codey models are no longer supported.');
} }
if (this.options.promptPrefix) { if (this.systemMessage) {
const instructionsTokenCount = this.getTokenCount(this.options.promptPrefix); const instructionsTokenCount = this.getTokenCount(this.systemMessage);
this.maxContextTokens = this.maxContextTokens - instructionsTokenCount; this.maxContextTokens = this.maxContextTokens - instructionsTokenCount;
if (this.maxContextTokens < 0) { if (this.maxContextTokens < 0) {
@ -417,8 +419,8 @@ class GoogleClient extends BaseClient {
], ],
}; };
if (this.options.promptPrefix) { if (this.systemMessage) {
payload.instances[0].context = this.options.promptPrefix; payload.instances[0].context = this.systemMessage;
} }
logger.debug('[GoogleClient] buildMessages', payload); logger.debug('[GoogleClient] buildMessages', payload);
@ -464,7 +466,7 @@ class GoogleClient extends BaseClient {
identityPrefix = `${identityPrefix}\nYou are ${this.options.modelLabel}`; identityPrefix = `${identityPrefix}\nYou are ${this.options.modelLabel}`;
} }
let promptPrefix = (this.options.promptPrefix ?? '').trim(); let promptPrefix = (this.systemMessage ?? '').trim();
if (identityPrefix) { if (identityPrefix) {
promptPrefix = `${identityPrefix}${promptPrefix}`; promptPrefix = `${identityPrefix}${promptPrefix}`;
@ -648,7 +650,7 @@ class GoogleClient extends BaseClient {
generationConfig: googleGenConfigSchema.parse(this.modelOptions), generationConfig: googleGenConfigSchema.parse(this.modelOptions),
}; };
const promptPrefix = (this.options.promptPrefix ?? '').trim(); const promptPrefix = (this.systemMessage ?? '').trim();
if (promptPrefix.length) { if (promptPrefix.length) {
requestOptions.systemInstruction = { requestOptions.systemInstruction = {
parts: [ parts: [

View file

@ -209,7 +209,7 @@ const primeFiles = async (options, apiKey) => {
const { handleFileUpload: uploadCodeEnvFile } = getStrategyFunctions( const { handleFileUpload: uploadCodeEnvFile } = getStrategyFunctions(
FileSources.execute_code, FileSources.execute_code,
); );
const stream = await getDownloadStream(file.filepath); const stream = await getDownloadStream(options.req, file.filepath);
const fileIdentifier = await uploadCodeEnvFile({ const fileIdentifier = await uploadCodeEnvFile({
req: options.req, req: options.req,
stream, stream,

View file

@ -224,10 +224,11 @@ async function uploadFileToFirebase({ req, file, file_id }) {
/** /**
* Retrieves a readable stream for a file from Firebase storage. * Retrieves a readable stream for a file from Firebase storage.
* *
* @param {ServerRequest} _req
* @param {string} filepath - The filepath. * @param {string} filepath - The filepath.
* @returns {Promise<ReadableStream>} A readable stream of the file. * @returns {Promise<ReadableStream>} A readable stream of the file.
*/ */
async function getFirebaseFileStream(filepath) { async function getFirebaseFileStream(_req, filepath) {
try { try {
const storage = getFirebaseStorage(); const storage = getFirebaseStorage();
if (!storage) { if (!storage) {

View file

@ -286,11 +286,31 @@ async function uploadLocalFile({ req, file, file_id }) {
/** /**
* Retrieves a readable stream for a file from local storage. * Retrieves a readable stream for a file from local storage.
* *
* @param {ServerRequest} req - The request object from Express
* @param {string} filepath - The filepath. * @param {string} filepath - The filepath.
* @returns {ReadableStream} A readable stream of the file. * @returns {ReadableStream} A readable stream of the file.
*/ */
function getLocalFileStream(filepath) { function getLocalFileStream(req, filepath) {
try { try {
if (filepath.includes('/uploads/')) {
const basePath = filepath.split('/uploads/')[1];
if (!basePath) {
logger.warn(`Invalid base path: ${filepath}`);
throw new Error(`Invalid file path: ${filepath}`);
}
const fullPath = path.join(req.app.locals.paths.uploads, basePath);
const uploadsDir = req.app.locals.paths.uploads;
const rel = path.relative(uploadsDir, fullPath);
if (rel.startsWith('..') || path.isAbsolute(rel) || rel.includes(`..${path.sep}`)) {
logger.warn(`Invalid relative file path: ${filepath}`);
throw new Error(`Invalid file path: ${filepath}`);
}
return fs.createReadStream(fullPath);
}
return fs.createReadStream(filepath); return fs.createReadStream(filepath);
} catch (error) { } catch (error) {
logger.error('Error getting local file stream:', error); logger.error('Error getting local file stream:', error);

View file

@ -66,8 +66,8 @@
"html-to-image": "^1.11.11", "html-to-image": "^1.11.11",
"i18next": "^24.2.2", "i18next": "^24.2.2",
"i18next-browser-languagedetector": "^8.0.3", "i18next-browser-languagedetector": "^8.0.3",
"js-cookie": "^3.0.5",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"js-cookie": "^3.0.5",
"librechat-data-provider": "*", "librechat-data-provider": "*",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lucide-react": "^0.394.0", "lucide-react": "^0.394.0",
@ -86,7 +86,7 @@
"react-i18next": "^15.4.0", "react-i18next": "^15.4.0",
"react-lazy-load-image-component": "^1.6.0", "react-lazy-load-image-component": "^1.6.0",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
"react-resizable-panels": "^2.1.1", "react-resizable-panels": "^2.1.7",
"react-router-dom": "^6.11.2", "react-router-dom": "^6.11.2",
"react-speech-recognition": "^3.10.0", "react-speech-recognition": "^3.10.0",
"react-textarea-autosize": "^8.4.0", "react-textarea-autosize": "^8.4.0",

20
package-lock.json generated
View file

@ -1095,7 +1095,7 @@
"react-i18next": "^15.4.0", "react-i18next": "^15.4.0",
"react-lazy-load-image-component": "^1.6.0", "react-lazy-load-image-component": "^1.6.0",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
"react-resizable-panels": "^2.1.1", "react-resizable-panels": "^2.1.7",
"react-router-dom": "^6.11.2", "react-router-dom": "^6.11.2",
"react-speech-recognition": "^3.10.0", "react-speech-recognition": "^3.10.0",
"react-textarea-autosize": "^8.4.0", "react-textarea-autosize": "^8.4.0",
@ -1609,6 +1609,15 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}, },
"client/node_modules/react-resizable-panels": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.7.tgz",
"integrity": "sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==",
"peerDependencies": {
"react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
"client/node_modules/rollup": { "client/node_modules/rollup": {
"version": "4.34.6", "version": "4.34.6",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz",
@ -31919,15 +31928,6 @@
} }
} }
}, },
"node_modules/react-resizable-panels": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.1.tgz",
"integrity": "sha512-+cUV/yZBYfiBj+WJtpWDJ3NtR4zgDZfHt3+xtaETKE+FCvp+RK/NJxacDQKxMHgRUTSkfA6AnGljQ5QZNsCQoA==",
"peerDependencies": {
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-router": { "node_modules/react-router": {
"version": "6.22.0", "version": "6.22.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz",