LibreChat/api/server/middleware/abortRun.js
Danny Avila 1dbe6ee75d
feat: Add Current Datetime to Assistants (v1/v2) (#4952)
* Feature: Added ability to send current date and time to v1 and v2 assistants

* remove date_feature.patch

* fix: rename append_today_date to append_current_datetime

* feat: Refactor time handling in chatV1 and chatV2, add date and time utility functions

* fix: Add warning log and response for missing run values in abortRun middleware

---------

Co-authored-by: Max Sanna <max@maxsanna.com>
2024-12-11 15:26:18 -05:00

104 lines
3.2 KiB
JavaScript

const { CacheKeys, RunStatus, isUUID } = require('librechat-data-provider');
const { initializeClient } = require('~/server/services/Endpoints/assistants');
const { checkMessageGaps, recordUsage } = require('~/server/services/Threads');
const { deleteMessages } = require('~/models/Message');
const { getConvo } = require('~/models/Conversation');
const getLogStores = require('~/cache/getLogStores');
const { sendMessage } = require('~/server/utils');
const { logger } = require('~/config');
const three_minutes = 1000 * 60 * 3;
async function abortRun(req, res) {
res.setHeader('Content-Type', 'application/json');
const { abortKey, endpoint } = req.body;
const [conversationId, latestMessageId] = abortKey.split(':');
const conversation = await getConvo(req.user.id, conversationId);
if (conversation?.model) {
req.body.model = conversation.model;
}
if (!isUUID.safeParse(conversationId).success) {
logger.error('[abortRun] Invalid conversationId', { conversationId });
return res.status(400).send({ message: 'Invalid conversationId' });
}
const cacheKey = `${req.user.id}:${conversationId}`;
const cache = getLogStores(CacheKeys.ABORT_KEYS);
const runValues = await cache.get(cacheKey);
if (!runValues) {
logger.warn('[abortRun] Run not found in cache', { cacheKey });
return res.status(204).send({ message: 'Run not found' });
}
const [thread_id, run_id] = runValues.split(':');
if (!run_id) {
logger.warn('[abortRun] Couldn\'t find run for cancel request', { thread_id });
return res.status(204).send({ message: 'Run not found' });
} else if (run_id === 'cancelled') {
logger.warn('[abortRun] Run already cancelled', { thread_id });
return res.status(204).send({ message: 'Run already cancelled' });
}
let runMessages = [];
/** @type {{ openai: OpenAI }} */
const { openai } = await initializeClient({ req, res });
try {
await cache.set(cacheKey, 'cancelled', three_minutes);
const cancelledRun = await openai.beta.threads.runs.cancel(thread_id, run_id);
logger.debug('[abortRun] Cancelled run:', cancelledRun);
} catch (error) {
logger.error('[abortRun] Error cancelling run', error);
if (
error?.message?.includes(RunStatus.CANCELLED) ||
error?.message?.includes(RunStatus.CANCELLING)
) {
return res.end();
}
}
try {
const run = await openai.beta.threads.runs.retrieve(thread_id, run_id);
await recordUsage({
...run.usage,
model: run.model,
user: req.user.id,
conversationId,
});
} catch (error) {
logger.error('[abortRun] Error fetching or processing run', error);
}
/* TODO: a reconciling strategy between the existing intermediate message would be more optimal than deleting it */
await deleteMessages({
user: req.user.id,
unfinished: true,
conversationId,
});
runMessages = await checkMessageGaps({
openai,
run_id,
endpoint,
thread_id,
conversationId,
latestMessageId,
});
const finalEvent = {
final: true,
conversation,
runMessages,
};
if (res.headersSent && finalEvent) {
return sendMessage(res, finalEvent);
}
res.json(finalEvent);
}
module.exports = {
abortRun,
};