|
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, |
|
}; |
|
|