mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 10:20:15 +01:00
🛠️ fix: Optionally add OpenID Sig. Algo. from Server Discovery (#5398)
* fix: Optionally add OpenID Sig. Algorithm from Server Discovery * chore: bump vite to 5.4.14 for CVE-2025-24010 * chore: remove deprecated code * fix: install missing undici * fix: Add @waylaidwanderer/fetch-event-source package
This commit is contained in:
parent
fa9e778399
commit
199e5e6eaf
11 changed files with 101 additions and 2024 deletions
|
|
@ -1,237 +0,0 @@
|
|||
const crypto = require('crypto');
|
||||
const express = require('express');
|
||||
const { Constants } = require('librechat-data-provider');
|
||||
const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('~/models');
|
||||
const { handleError, sendMessage, createOnProgress, handleText } = require('~/server/utils');
|
||||
const { setHeaders } = require('~/server/middleware');
|
||||
const { browserClient } = require('~/app/');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/', setHeaders, async (req, res) => {
|
||||
const {
|
||||
endpoint,
|
||||
text,
|
||||
overrideParentMessageId = null,
|
||||
parentMessageId,
|
||||
conversationId: oldConversationId,
|
||||
} = req.body;
|
||||
if (text.length === 0) {
|
||||
return handleError(res, { text: 'Prompt empty or too short' });
|
||||
}
|
||||
if (endpoint !== 'chatGPTBrowser') {
|
||||
return handleError(res, { text: 'Illegal request' });
|
||||
}
|
||||
|
||||
// build user message
|
||||
const conversationId = oldConversationId || crypto.randomUUID();
|
||||
const isNewConversation = !oldConversationId;
|
||||
const userMessageId = crypto.randomUUID();
|
||||
const userParentMessageId = parentMessageId || Constants.NO_PARENT;
|
||||
const userMessage = {
|
||||
messageId: userMessageId,
|
||||
sender: 'User',
|
||||
text,
|
||||
parentMessageId: userParentMessageId,
|
||||
conversationId,
|
||||
isCreatedByUser: true,
|
||||
};
|
||||
|
||||
// build endpoint option
|
||||
const endpointOption = {
|
||||
model: req.body?.model ?? 'text-davinci-002-render-sha',
|
||||
key: req.body?.key ?? null,
|
||||
};
|
||||
|
||||
logger.debug('[/ask/chatGPTBrowser]', {
|
||||
userMessage,
|
||||
conversationId,
|
||||
...endpointOption,
|
||||
});
|
||||
|
||||
if (!overrideParentMessageId) {
|
||||
await saveMessage(req, { ...userMessage, user: req.user.id });
|
||||
await saveConvo(req, {
|
||||
...userMessage,
|
||||
...endpointOption,
|
||||
conversationId,
|
||||
endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
return await ask({
|
||||
isNewConversation,
|
||||
userMessage,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
preSendRequest: true,
|
||||
overrideParentMessageId,
|
||||
req,
|
||||
res,
|
||||
});
|
||||
});
|
||||
|
||||
const ask = async ({
|
||||
isNewConversation,
|
||||
userMessage,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
overrideParentMessageId = null,
|
||||
req,
|
||||
res,
|
||||
}) => {
|
||||
let { text, parentMessageId: userParentMessageId, messageId: userMessageId } = userMessage;
|
||||
const user = req.user.id;
|
||||
let responseMessageId = crypto.randomUUID();
|
||||
let getPartialMessage = null;
|
||||
try {
|
||||
let lastSavedTimestamp = 0;
|
||||
const { onProgress: progressCallback, getPartialText } = createOnProgress({
|
||||
onProgress: ({ text }) => {
|
||||
const currentTimestamp = Date.now();
|
||||
if (currentTimestamp - lastSavedTimestamp > 500) {
|
||||
lastSavedTimestamp = currentTimestamp;
|
||||
saveMessage(req, {
|
||||
messageId: responseMessageId,
|
||||
sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI',
|
||||
conversationId,
|
||||
parentMessageId: overrideParentMessageId || userMessageId,
|
||||
text: text,
|
||||
unfinished: true,
|
||||
error: false,
|
||||
isCreatedByUser: false,
|
||||
user,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
getPartialMessage = getPartialText;
|
||||
const abortController = new AbortController();
|
||||
let i = 0;
|
||||
let response = await browserClient({
|
||||
text,
|
||||
parentMessageId: userParentMessageId,
|
||||
conversationId,
|
||||
...endpointOption,
|
||||
abortController,
|
||||
userId: user,
|
||||
onProgress: progressCallback.call(null, { res, text }),
|
||||
onEventMessage: (eventMessage) => {
|
||||
let data = null;
|
||||
try {
|
||||
data = JSON.parse(eventMessage.data);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendMessage(res, {
|
||||
message: { ...userMessage, conversationId: data.conversation_id },
|
||||
created: i === 0,
|
||||
});
|
||||
|
||||
if (i === 0) {
|
||||
i++;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug('[/ask/chatGPTBrowser]', response);
|
||||
|
||||
const newConversationId = response.conversationId || conversationId;
|
||||
const newUserMassageId = response.parentMessageId || userMessageId;
|
||||
const newResponseMessageId = response.messageId;
|
||||
|
||||
// STEP1 generate response message
|
||||
response.text = response.response || '**ChatGPT refused to answer.**';
|
||||
|
||||
let responseMessage = {
|
||||
conversationId: newConversationId,
|
||||
messageId: responseMessageId,
|
||||
newMessageId: newResponseMessageId,
|
||||
parentMessageId: overrideParentMessageId || newUserMassageId,
|
||||
text: await handleText(response),
|
||||
sender: endpointOption?.chatGptLabel || 'ChatGPT',
|
||||
unfinished: false,
|
||||
error: false,
|
||||
isCreatedByUser: false,
|
||||
};
|
||||
|
||||
await saveMessage(req, { ...responseMessage, user });
|
||||
responseMessage.messageId = newResponseMessageId;
|
||||
|
||||
// STEP2 update the conversation
|
||||
|
||||
// First update conversationId if needed
|
||||
let conversationUpdate = { conversationId: newConversationId, endpoint: 'chatGPTBrowser' };
|
||||
if (conversationId != newConversationId) {
|
||||
if (isNewConversation) {
|
||||
// change the conversationId to new one
|
||||
conversationUpdate = {
|
||||
...conversationUpdate,
|
||||
conversationId: conversationId,
|
||||
newConversationId: newConversationId,
|
||||
};
|
||||
} else {
|
||||
// create new conversation
|
||||
conversationUpdate = {
|
||||
...conversationUpdate,
|
||||
...endpointOption,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await saveConvo(req, conversationUpdate);
|
||||
conversationId = newConversationId;
|
||||
|
||||
// STEP3 update the user message
|
||||
userMessage.conversationId = newConversationId;
|
||||
userMessage.messageId = newUserMassageId;
|
||||
|
||||
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
|
||||
if (!overrideParentMessageId) {
|
||||
await saveMessage(req, {
|
||||
...userMessage,
|
||||
user,
|
||||
messageId: userMessageId,
|
||||
newMessageId: newUserMassageId,
|
||||
});
|
||||
}
|
||||
userMessageId = newUserMassageId;
|
||||
|
||||
sendMessage(res, {
|
||||
title: await getConvoTitle(user, conversationId),
|
||||
final: true,
|
||||
conversation: await getConvo(user, conversationId),
|
||||
requestMessage: userMessage,
|
||||
responseMessage: responseMessage,
|
||||
});
|
||||
res.end();
|
||||
|
||||
if (userParentMessageId == Constants.NO_PARENT) {
|
||||
// const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage });
|
||||
const title = await response.details.title;
|
||||
await saveConvo(req, {
|
||||
conversationId: conversationId,
|
||||
title,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = {
|
||||
messageId: responseMessageId,
|
||||
sender: 'ChatGPT',
|
||||
conversationId,
|
||||
parentMessageId: overrideParentMessageId || userMessageId,
|
||||
unfinished: false,
|
||||
error: true,
|
||||
isCreatedByUser: false,
|
||||
text: `${getPartialMessage() ?? ''}\n\nError message: "${error.message}"`,
|
||||
};
|
||||
await saveMessage(req, { ...errorMessage, user });
|
||||
handleError(res, errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -1,297 +0,0 @@
|
|||
const crypto = require('crypto');
|
||||
const express = require('express');
|
||||
const { Constants } = require('librechat-data-provider');
|
||||
const { handleError, sendMessage, createOnProgress, handleText } = require('~/server/utils');
|
||||
const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('~/models');
|
||||
const { setHeaders } = require('~/server/middleware');
|
||||
const { titleConvoBing, askBing } = require('~/app');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/', setHeaders, async (req, res) => {
|
||||
const {
|
||||
endpoint,
|
||||
text,
|
||||
messageId,
|
||||
overrideParentMessageId = null,
|
||||
parentMessageId,
|
||||
conversationId: oldConversationId,
|
||||
} = req.body;
|
||||
if (text.length === 0) {
|
||||
return handleError(res, { text: 'Prompt empty or too short' });
|
||||
}
|
||||
if (endpoint !== 'bingAI') {
|
||||
return handleError(res, { text: 'Illegal request' });
|
||||
}
|
||||
|
||||
// build user message
|
||||
const conversationId = oldConversationId || crypto.randomUUID();
|
||||
const isNewConversation = !oldConversationId;
|
||||
const userMessageId = messageId;
|
||||
const userParentMessageId = parentMessageId || Constants.NO_PARENT;
|
||||
let userMessage = {
|
||||
messageId: userMessageId,
|
||||
sender: 'User',
|
||||
text,
|
||||
parentMessageId: userParentMessageId,
|
||||
conversationId,
|
||||
isCreatedByUser: true,
|
||||
};
|
||||
|
||||
// build endpoint option
|
||||
let endpointOption = {};
|
||||
if (req.body?.jailbreak) {
|
||||
endpointOption = {
|
||||
jailbreak: req.body?.jailbreak ?? false,
|
||||
jailbreakConversationId: req.body?.jailbreakConversationId ?? null,
|
||||
systemMessage: req.body?.systemMessage ?? null,
|
||||
context: req.body?.context ?? null,
|
||||
toneStyle: req.body?.toneStyle ?? 'creative',
|
||||
key: req.body?.key ?? null,
|
||||
};
|
||||
} else {
|
||||
endpointOption = {
|
||||
jailbreak: req.body?.jailbreak ?? false,
|
||||
systemMessage: req.body?.systemMessage ?? null,
|
||||
context: req.body?.context ?? null,
|
||||
conversationSignature: req.body?.conversationSignature ?? null,
|
||||
clientId: req.body?.clientId ?? null,
|
||||
invocationId: req.body?.invocationId ?? null,
|
||||
toneStyle: req.body?.toneStyle ?? 'creative',
|
||||
key: req.body?.key ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
logger.debug('[/ask/bingAI] ask log', {
|
||||
userMessage,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
});
|
||||
|
||||
if (!overrideParentMessageId) {
|
||||
await saveMessage(req, { ...userMessage, user: req.user.id });
|
||||
await saveConvo(req, {
|
||||
...userMessage,
|
||||
...endpointOption,
|
||||
conversationId,
|
||||
endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
return await ask({
|
||||
isNewConversation,
|
||||
userMessage,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
preSendRequest: true,
|
||||
overrideParentMessageId,
|
||||
req,
|
||||
res,
|
||||
});
|
||||
});
|
||||
|
||||
const ask = async ({
|
||||
isNewConversation,
|
||||
userMessage,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
preSendRequest = true,
|
||||
overrideParentMessageId = null,
|
||||
req,
|
||||
res,
|
||||
}) => {
|
||||
let { text, parentMessageId: userParentMessageId, messageId: userMessageId } = userMessage;
|
||||
const user = req.user.id;
|
||||
|
||||
let responseMessageId = crypto.randomUUID();
|
||||
const model = endpointOption?.jailbreak ? 'Sydney' : 'BingAI';
|
||||
|
||||
if (preSendRequest) {
|
||||
sendMessage(res, { message: userMessage, created: true });
|
||||
}
|
||||
|
||||
let lastSavedTimestamp = 0;
|
||||
const { onProgress: progressCallback, getPartialText } = createOnProgress({
|
||||
onProgress: ({ text }) => {
|
||||
const currentTimestamp = Date.now();
|
||||
if (currentTimestamp - lastSavedTimestamp > 500) {
|
||||
lastSavedTimestamp = currentTimestamp;
|
||||
saveMessage(req, {
|
||||
messageId: responseMessageId,
|
||||
sender: model,
|
||||
conversationId,
|
||||
parentMessageId: overrideParentMessageId || userMessageId,
|
||||
model,
|
||||
text: text,
|
||||
unfinished: true,
|
||||
error: false,
|
||||
isCreatedByUser: false,
|
||||
user,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
const abortController = new AbortController();
|
||||
let bingConversationId = null;
|
||||
if (!isNewConversation) {
|
||||
const convo = await getConvo(user, conversationId);
|
||||
bingConversationId = convo.bingConversationId;
|
||||
}
|
||||
|
||||
try {
|
||||
let response = await askBing({
|
||||
text,
|
||||
userId: user,
|
||||
parentMessageId: userParentMessageId,
|
||||
conversationId: bingConversationId ?? conversationId,
|
||||
...endpointOption,
|
||||
onProgress: progressCallback.call(null, {
|
||||
res,
|
||||
text,
|
||||
parentMessageId: overrideParentMessageId || userMessageId,
|
||||
}),
|
||||
abortController,
|
||||
});
|
||||
|
||||
logger.debug('[/ask/bingAI] BING RESPONSE', response);
|
||||
|
||||
if (response.details && response.details.scores) {
|
||||
logger.debug('[/ask/bingAI] SCORES', response.details.scores);
|
||||
}
|
||||
|
||||
const newConversationId = endpointOption?.jailbreak
|
||||
? response.jailbreakConversationId
|
||||
: response.conversationId || conversationId;
|
||||
const newUserMessageId =
|
||||
response.parentMessageId || response.details.requestId || userMessageId;
|
||||
const newResponseMessageId = response.messageId || response.details.messageId;
|
||||
|
||||
// STEP1 generate response message
|
||||
response.text =
|
||||
response.response || response.details.spokenText || '**Bing refused to answer.**';
|
||||
|
||||
const partialText = getPartialText();
|
||||
let unfinished = false;
|
||||
if (partialText?.trim()?.length > response.text.length) {
|
||||
response.text = partialText;
|
||||
unfinished = false;
|
||||
//setting "unfinished" to false fix bing image generation error msg and allows to continue a convo after being triggered by censorship (bing does remember the context after a "censored error" so there is no reason to end the convo)
|
||||
}
|
||||
|
||||
let responseMessage = {
|
||||
conversationId,
|
||||
bingConversationId: newConversationId,
|
||||
messageId: responseMessageId,
|
||||
newMessageId: newResponseMessageId,
|
||||
parentMessageId: overrideParentMessageId || newUserMessageId,
|
||||
sender: model,
|
||||
text: await handleText(response, true),
|
||||
model,
|
||||
suggestions:
|
||||
response.details.suggestedResponses &&
|
||||
response.details.suggestedResponses.map((s) => s.text),
|
||||
unfinished,
|
||||
error: false,
|
||||
isCreatedByUser: false,
|
||||
};
|
||||
|
||||
await saveMessage(req, { ...responseMessage, user });
|
||||
responseMessage.messageId = newResponseMessageId;
|
||||
|
||||
let conversationUpdate = {
|
||||
conversationId,
|
||||
bingConversationId: newConversationId,
|
||||
endpoint: 'bingAI',
|
||||
};
|
||||
|
||||
if (endpointOption?.jailbreak) {
|
||||
conversationUpdate.jailbreak = true;
|
||||
conversationUpdate.jailbreakConversationId = response.jailbreakConversationId;
|
||||
} else {
|
||||
conversationUpdate.jailbreak = false;
|
||||
conversationUpdate.conversationSignature = response.encryptedConversationSignature;
|
||||
conversationUpdate.clientId = response.clientId;
|
||||
conversationUpdate.invocationId = response.invocationId;
|
||||
}
|
||||
|
||||
await saveConvo(req, conversationUpdate);
|
||||
userMessage.messageId = newUserMessageId;
|
||||
|
||||
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
|
||||
if (!overrideParentMessageId) {
|
||||
await saveMessage(req, {
|
||||
...userMessage,
|
||||
user,
|
||||
messageId: userMessageId,
|
||||
newMessageId: newUserMessageId,
|
||||
});
|
||||
}
|
||||
userMessageId = newUserMessageId;
|
||||
|
||||
sendMessage(res, {
|
||||
title: await getConvoTitle(user, conversationId),
|
||||
final: true,
|
||||
conversation: await getConvo(user, conversationId),
|
||||
requestMessage: userMessage,
|
||||
responseMessage: responseMessage,
|
||||
});
|
||||
res.end();
|
||||
|
||||
if (userParentMessageId == Constants.NO_PARENT) {
|
||||
const title = await titleConvoBing({
|
||||
text,
|
||||
response: responseMessage,
|
||||
});
|
||||
|
||||
await saveConvo(req, {
|
||||
conversationId: conversationId,
|
||||
title,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[/ask/bingAI] Error handling BingAI response', error);
|
||||
const partialText = getPartialText();
|
||||
if (partialText?.length > 2) {
|
||||
const responseMessage = {
|
||||
messageId: responseMessageId,
|
||||
sender: model,
|
||||
conversationId,
|
||||
parentMessageId: overrideParentMessageId || userMessageId,
|
||||
text: partialText,
|
||||
model,
|
||||
unfinished: true,
|
||||
error: false,
|
||||
isCreatedByUser: false,
|
||||
};
|
||||
|
||||
saveMessage(req, { ...responseMessage, user });
|
||||
|
||||
return {
|
||||
title: await getConvoTitle(user, conversationId),
|
||||
final: true,
|
||||
conversation: await getConvo(user, conversationId),
|
||||
requestMessage: userMessage,
|
||||
responseMessage: responseMessage,
|
||||
};
|
||||
} else {
|
||||
logger.error('[/ask/bingAI] Error handling BingAI response', error);
|
||||
const errorMessage = {
|
||||
messageId: responseMessageId,
|
||||
sender: model,
|
||||
conversationId,
|
||||
parentMessageId: overrideParentMessageId || userMessageId,
|
||||
unfinished: false,
|
||||
error: true,
|
||||
text: error.message,
|
||||
model,
|
||||
isCreatedByUser: false,
|
||||
};
|
||||
await saveMessage(req, { ...errorMessage, user });
|
||||
handleError(res, errorMessage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -2,10 +2,8 @@ const express = require('express');
|
|||
const openAI = require('./openAI');
|
||||
const custom = require('./custom');
|
||||
const google = require('./google');
|
||||
const bingAI = require('./bingAI');
|
||||
const anthropic = require('./anthropic');
|
||||
const gptPlugins = require('./gptPlugins');
|
||||
const askChatGPTBrowser = require('./askChatGPTBrowser');
|
||||
const { isEnabled } = require('~/server/utils');
|
||||
const { EModelEndpoint } = require('librechat-data-provider');
|
||||
const {
|
||||
|
|
@ -41,11 +39,9 @@ if (isEnabled(LIMIT_MESSAGE_USER)) {
|
|||
router.use(validateConvoAccess);
|
||||
|
||||
router.use([`/${EModelEndpoint.azureOpenAI}`, `/${EModelEndpoint.openAI}`], openAI);
|
||||
router.use(`/${EModelEndpoint.chatGPTBrowser}`, askChatGPTBrowser);
|
||||
router.use(`/${EModelEndpoint.gptPlugins}`, gptPlugins);
|
||||
router.use(`/${EModelEndpoint.anthropic}`, anthropic);
|
||||
router.use(`/${EModelEndpoint.google}`, google);
|
||||
router.use(`/${EModelEndpoint.bingAI}`, bingAI);
|
||||
router.use(`/${EModelEndpoint.custom}`, custom);
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue