mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🕐 feat: Configurable Retention Period for Temporary Chats (#8056)
* feat: Add configurable retention period for temporary chats * Addressing eslint errors * Fix: failing test due to missing registration * Update: variable name and use hours instead of days for chat retention * Addressing comments * chore: fix import order in Conversation.js * chore: import order in Message.js * chore: fix import order in config.ts * chore: move common methods to packages/api to reduce potential for circular dependencies * refactor: update temp chat retention config type to Partial<TCustomConfig> * refactor: remove unused config variable from AppService and update loadCustomConfig tests with logger mock * refactor: handle model undefined edge case by moving Session model initialization inside methods --------- Co-authored-by: Rakshit Tiwari <rak1729e@gmail.com>
This commit is contained in:
parent
3ab1bd65e5
commit
cbda3cb529
35 changed files with 372 additions and 135 deletions
|
|
@ -7,9 +7,9 @@ const {
|
|||
defaultAssistantsVersion,
|
||||
defaultAgentCapabilities,
|
||||
} = require('librechat-data-provider');
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { Providers } = require('@librechat/agents');
|
||||
const partialRight = require('lodash/partialRight');
|
||||
const { sendMessage } = require('./streamResponse');
|
||||
|
||||
/** Helper function to escape special characters in regex
|
||||
* @param {string} string - The string to escape.
|
||||
|
|
@ -37,7 +37,7 @@ const createOnProgress = (
|
|||
basePayload.text = basePayload.text + chunk;
|
||||
|
||||
const payload = Object.assign({}, basePayload, rest);
|
||||
sendMessage(res, payload);
|
||||
sendEvent(res, payload);
|
||||
if (_onProgress) {
|
||||
_onProgress(payload);
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ const createOnProgress = (
|
|||
const sendIntermediateMessage = (res, payload, extraTokens = '') => {
|
||||
basePayload.text = basePayload.text + extraTokens;
|
||||
const message = Object.assign({}, basePayload, payload);
|
||||
sendMessage(res, message);
|
||||
sendEvent(res, message);
|
||||
if (i === 0) {
|
||||
basePayload.initial = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
const streamResponse = require('./streamResponse');
|
||||
const removePorts = require('./removePorts');
|
||||
const countTokens = require('./countTokens');
|
||||
const handleText = require('./handleText');
|
||||
const sendEmail = require('./sendEmail');
|
||||
const queue = require('./queue');
|
||||
const files = require('./files');
|
||||
const math = require('./math');
|
||||
|
||||
/**
|
||||
* Check if email configuration is set
|
||||
|
|
@ -28,7 +26,6 @@ function checkEmailConfig() {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
...streamResponse,
|
||||
checkEmailConfig,
|
||||
...handleText,
|
||||
countTokens,
|
||||
|
|
@ -36,5 +33,4 @@ module.exports = {
|
|||
sendEmail,
|
||||
...files,
|
||||
...queue,
|
||||
math,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* Evaluates a mathematical expression provided as a string and returns the result.
|
||||
*
|
||||
* If the input is already a number, it returns the number as is.
|
||||
* If the input is not a string or contains invalid characters, an error is thrown.
|
||||
* If the evaluated result is not a number, an error is thrown.
|
||||
*
|
||||
* @param {string|number} str - The mathematical expression to evaluate, or a number.
|
||||
* @param {number} [fallbackValue] - The default value to return if the input is not a string or number, or if the evaluated result is not a number.
|
||||
*
|
||||
* @returns {number} The result of the evaluated expression or the input number.
|
||||
*
|
||||
* @throws {Error} Throws an error if the input is not a string or number, contains invalid characters, or does not evaluate to a number.
|
||||
*/
|
||||
function math(str, fallbackValue) {
|
||||
const fallback = typeof fallbackValue !== 'undefined' && typeof fallbackValue === 'number';
|
||||
if (typeof str !== 'string' && typeof str === 'number') {
|
||||
return str;
|
||||
} else if (typeof str !== 'string') {
|
||||
if (fallback) {
|
||||
return fallbackValue;
|
||||
}
|
||||
throw new Error(`str is ${typeof str}, but should be a string`);
|
||||
}
|
||||
|
||||
const validStr = /^[+\-\d.\s*/%()]+$/.test(str);
|
||||
|
||||
if (!validStr) {
|
||||
if (fallback) {
|
||||
return fallbackValue;
|
||||
}
|
||||
throw new Error('Invalid characters in string');
|
||||
}
|
||||
|
||||
const value = eval(str);
|
||||
|
||||
if (typeof value !== 'number') {
|
||||
if (fallback) {
|
||||
return fallbackValue;
|
||||
}
|
||||
throw new Error(`[math] str did not evaluate to a number but to a ${typeof value}`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
module.exports = math;
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
const crypto = require('crypto');
|
||||
const { parseConvo } = require('librechat-data-provider');
|
||||
const { saveMessage, getMessages } = require('~/models/Message');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* Sends error data in Server Sent Events format and ends the response.
|
||||
* @param {object} res - The server response.
|
||||
* @param {string} message - The error message.
|
||||
*/
|
||||
const handleError = (res, message) => {
|
||||
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
|
||||
res.end();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends message data in Server Sent Events format.
|
||||
* @param {Express.Response} res - - The server response.
|
||||
* @param {string | Object} message - The message to be sent.
|
||||
* @param {'message' | 'error' | 'cancel'} event - [Optional] The type of event. Default is 'message'.
|
||||
*/
|
||||
const sendMessage = (res, message, event = 'message') => {
|
||||
if (typeof message === 'string' && message.length === 0) {
|
||||
return;
|
||||
}
|
||||
res.write(`event: ${event}\ndata: ${JSON.stringify(message)}\n\n`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes an error with provided options, saves the error message and sends a corresponding SSE response
|
||||
* @async
|
||||
* @param {object} req - The request.
|
||||
* @param {object} res - The response.
|
||||
* @param {object} options - The options for handling the error containing message properties.
|
||||
* @param {object} options.user - The user ID.
|
||||
* @param {string} options.sender - The sender of the message.
|
||||
* @param {string} options.conversationId - The conversation ID.
|
||||
* @param {string} options.messageId - The message ID.
|
||||
* @param {string} options.parentMessageId - The parent message ID.
|
||||
* @param {string} options.text - The error message.
|
||||
* @param {boolean} options.shouldSaveMessage - [Optional] Whether the message should be saved. Default is true.
|
||||
* @param {function} callback - [Optional] The callback function to be executed.
|
||||
*/
|
||||
const sendError = async (req, res, options, callback) => {
|
||||
const {
|
||||
user,
|
||||
sender,
|
||||
conversationId,
|
||||
messageId,
|
||||
parentMessageId,
|
||||
text,
|
||||
shouldSaveMessage,
|
||||
...rest
|
||||
} = options;
|
||||
const errorMessage = {
|
||||
sender,
|
||||
messageId: messageId ?? crypto.randomUUID(),
|
||||
conversationId,
|
||||
parentMessageId,
|
||||
unfinished: false,
|
||||
error: true,
|
||||
final: true,
|
||||
text,
|
||||
isCreatedByUser: false,
|
||||
...rest,
|
||||
};
|
||||
if (callback && typeof callback === 'function') {
|
||||
await callback();
|
||||
}
|
||||
|
||||
if (shouldSaveMessage) {
|
||||
await saveMessage(
|
||||
req,
|
||||
{ ...errorMessage, user },
|
||||
{
|
||||
context: 'api/server/utils/streamResponse.js - sendError',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!errorMessage.error) {
|
||||
const requestMessage = { messageId: parentMessageId, conversationId };
|
||||
let query = [],
|
||||
convo = {};
|
||||
try {
|
||||
query = await getMessages(requestMessage);
|
||||
convo = await getConvo(user, conversationId);
|
||||
} catch (err) {
|
||||
logger.error('[sendError] Error retrieving conversation data:', err);
|
||||
convo = parseConvo(errorMessage);
|
||||
}
|
||||
|
||||
return sendMessage(res, {
|
||||
final: true,
|
||||
requestMessage: query?.[0] ? query[0] : requestMessage,
|
||||
responseMessage: errorMessage,
|
||||
conversation: convo,
|
||||
});
|
||||
}
|
||||
|
||||
handleError(res, errorMessage);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends the response based on whether headers have been sent or not.
|
||||
* @param {Express.Request} req - The server response.
|
||||
* @param {Express.Response} res - The server response.
|
||||
* @param {Object} data - The data to be sent.
|
||||
* @param {string} [errorMessage] - The error message, if any.
|
||||
*/
|
||||
const sendResponse = (req, res, data, errorMessage) => {
|
||||
if (!res.headersSent) {
|
||||
if (errorMessage) {
|
||||
return res.status(500).json({ error: errorMessage });
|
||||
}
|
||||
return res.json(data);
|
||||
}
|
||||
|
||||
if (errorMessage) {
|
||||
return sendError(req, res, { ...data, text: errorMessage });
|
||||
}
|
||||
return sendMessage(res, data);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
sendResponse,
|
||||
handleError,
|
||||
sendMessage,
|
||||
sendError,
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue