const { sendMessage, sendError, countTokens, isEnabled } = require('../utils'); const { saveMessage, getConvo, getConvoTitle } = require('../../models'); const clearPendingReq = require('../../cache/clearPendingReq'); const spendTokens = require('../../models/spendTokens'); const abortControllers = require('./abortControllers'); async function abortMessage(req, res) { const { abortKey } = req.body; if (!abortControllers.has(abortKey) && !res.headersSent) { return res.status(404).send('Request not found'); } const { abortController } = abortControllers.get(abortKey); const ret = await abortController.abortCompletion(); console.log('Aborted request', abortKey); abortControllers.delete(abortKey); res.send(JSON.stringify(ret)); } const handleAbort = () => { return async (req, res) => { try { if (isEnabled(process.env.LIMIT_CONCURRENT_MESSAGES)) { await clearPendingReq({ userId: req.user.id }); } return await abortMessage(req, res); } catch (err) { console.error(err); } }; }; const createAbortController = (req, res, getAbortData) => { const abortController = new AbortController(); const { endpointOption } = req.body; const onStart = (userMessage) => { sendMessage(res, { message: userMessage, created: true }); const abortKey = userMessage?.conversationId ?? req.user.id; abortControllers.set(abortKey, { abortController, ...endpointOption }); res.on('finish', function () { abortControllers.delete(abortKey); }); }; abortController.abortCompletion = async function () { abortController.abort(); const { conversationId, userMessage, promptTokens, ...responseData } = getAbortData(); const completionTokens = await countTokens(responseData?.text ?? ''); const user = req.user.id; const responseMessage = { ...responseData, conversationId, finish_reason: 'incomplete', model: endpointOption.modelOptions.model, unfinished: false, cancelled: true, error: false, isCreatedByUser: false, tokenCount: completionTokens, }; await spendTokens( { ...responseMessage, context: 'incomplete', user }, { promptTokens, completionTokens }, ); saveMessage({ ...responseMessage, user }); return { title: await getConvoTitle(user, conversationId), final: true, conversation: await getConvo(user, conversationId), requestMessage: userMessage, responseMessage: responseMessage, }; }; return { abortController, onStart }; }; const handleAbortError = async (res, req, error, data) => { console.error(error); const { sender, conversationId, messageId, parentMessageId, partialText } = data; const respondWithError = async () => { const options = { sender, messageId, conversationId, parentMessageId, text: error.message, shouldSaveMessage: true, user: req.user.id, }; const callback = async () => { if (abortControllers.has(conversationId)) { const { abortController } = abortControllers.get(conversationId); abortController.abort(); abortControllers.delete(conversationId); } }; await sendError(res, options, callback); }; if (partialText && partialText.length > 5) { try { return await abortMessage(req, res); } catch (err) { console.error(err); return respondWithError(); } } else { return respondWithError(); } }; module.exports = { handleAbort, createAbortController, handleAbortError, };