diff --git a/api/app/clients/bingai.js b/api/app/clients/bingai.js index 5dbc377b63..c6463e6de1 100644 --- a/api/app/clients/bingai.js +++ b/api/app/clients/bingai.js @@ -30,23 +30,38 @@ const askBing = async ({ proxy: process.env.PROXY || null }); - let options = { - jailbreakConversationId: jailbreakConversationId || jailbreak, - context, - systemMessage, - parentMessageId, - conversationId: jailbreakConversationId ? jailbreakConversationId : conversationId, - toneStyle, - onProgress - }; + let options = {}; - if (conversationSignature) options.conversationSignature = conversationSignature; - if (conversationSignature) options.clientId = clientId; - if (conversationSignature) options.invocationId = invocationId; - if (conversationSignature) options.toneStyle = toneStyle; + if (jailbreakConversationId == 'false') { + jailbreakConversationId = false; + } - if (options?.jailbreakConversationId == 'false') { - options.jailbreakConversationId = false; + if (jailbreak) + options = { + jailbreakConversationId: jailbreakConversationId || jailbreak, + context, + systemMessage, + parentMessageId, + toneStyle, + onProgress + }; + else { + options = { + conversationId, + context, + systemMessage, + parentMessageId, + toneStyle, + onProgress + }; + + // don't give those parameters for new conversation + // for new conversation, conversationSignature always is null + if (conversationSignature) { + options.conversationSignature = conversationSignature; + options.clientId = clientId; + options.invocationId = invocationId; + } } console.log('bing options', options); diff --git a/api/models/Message.js b/api/models/Message.js index a3749f6f26..c0b280a85b 100644 --- a/api/models/Message.js +++ b/api/models/Message.js @@ -31,36 +31,6 @@ module.exports = { return { message: 'Error saving message' }; } }, - saveBingMessage: async ({ - messageId, - newMessageId, - conversationId, - parentMessageId, - sender, - text, - isCreatedByUser = false, - error - }) => { - try { - await Message.findOneAndUpdate( - { messageId }, - { - messageId: newMessageId || messageId, - conversationId, - parentMessageId, - sender, - text, - isCreatedByUser, - error - }, - { upsert: true, new: true } - ); - return { messageId, conversationId, parentMessageId, sender, text, isCreatedByUser }; - } catch (error) { - console.error(error); - return { message: 'Error saving message' }; - } - }, deleteMessagesSince: async ({ messageId, conversationId }) => { try { const message = await Message.findOne({ messageId }).exec(); diff --git a/api/models/index.js b/api/models/index.js index e72b81846e..a1694cde36 100644 --- a/api/models/index.js +++ b/api/models/index.js @@ -1,10 +1,4 @@ -const { - getMessages, - saveMessage, - saveBingMessage, - deleteMessagesSince, - deleteMessages -} = require('./Message'); +const { getMessages, saveMessage, deleteMessagesSince, deleteMessages } = require('./Message'); const { getCustomGpts, updateCustomGpt, updateByLabel, deleteCustomGpts } = require('./CustomGpt'); const { getConvoTitle, getConvo, saveConvo, updateConvo } = require('./Conversation'); const { getPreset, getPresets, savePreset, deletePresets } = require('./Preset'); @@ -12,7 +6,6 @@ const { getPreset, getPresets, savePreset, deletePresets } = require('./Preset') module.exports = { getMessages, saveMessage, - saveBingMessage, deleteMessagesSince, deleteMessages, diff --git a/api/models/schema/conversationPreset.js b/api/models/schema/conversationPreset.js index 219c5c5045..f64d6658cd 100644 --- a/api/models/schema/conversationPreset.js +++ b/api/models/schema/conversationPreset.js @@ -47,14 +47,6 @@ module.exports = { type: Boolean, default: false }, - jailbreakConversationId: { - type: String, - default: null - }, - conversationSignature: { - type: String, - default: null - }, context: { type: String, default: null @@ -63,14 +55,6 @@ module.exports = { type: String, default: null }, - clientId: { - type: String, - default: null - }, - invocationId: { - type: Number, - default: 1 - }, toneStyle: { type: String, default: null diff --git a/api/models/schema/convoSchema.js b/api/models/schema/convoSchema.js index fc29b55fb2..d73a0bde0d 100644 --- a/api/models/schema/convoSchema.js +++ b/api/models/schema/convoSchema.js @@ -20,7 +20,24 @@ const convoSchema = mongoose.Schema( default: null }, messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }], - ...conversationPreset + ...conversationPreset, + // for bingAI only + jailbreakConversationId: { + type: String, + default: null + }, + conversationSignature: { + type: String, + default: null + }, + clientId: { + type: String, + default: null + }, + invocationId: { + type: Number, + default: 1 + } }, { timestamps: true } ); diff --git a/api/server/routes/askBingAI.js b/api/server/routes/askBingAI.js index 50dd517433..3f01b69b84 100644 --- a/api/server/routes/askBingAI.js +++ b/api/server/routes/askBingAI.js @@ -2,7 +2,7 @@ const express = require('express'); const crypto = require('crypto'); const router = express.Router(); const { titleConvo, askBing } = require('../../app'); -const { saveBingMessage, getConvoTitle, saveConvo, getConvo } = require('../../models'); +const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('../../models'); const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers'); router.post('/', async (req, res) => { @@ -32,17 +32,25 @@ router.post('/', async (req, res) => { }; // build endpoint option - const endpointOption = { - jailbreak: req.body?.jailbreak || false, - jailbreakConversationId: req.body?.jailbreakConversationId || null, - 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 || 'fast', - suggestions: req.body?.suggestions || [] - }; + 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 || 'fast' + }; + 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 || 'fast' + }; console.log('ask log', { userMessage, @@ -51,7 +59,7 @@ router.post('/', async (req, res) => { }); if (!overrideParentMessageId) { - await saveBingMessage(userMessage); + await saveMessage(userMessage); await saveConvo(req?.session?.user?.username, { ...userMessage, ...endpointOption, @@ -114,57 +122,81 @@ const ask = async ({ console.log('BING RESPONSE', response); - // STEP1 update the convosation + // STEP1 generate response message + response.text = response.response || response.details.spokenText || '**Bing refused to answer.**'; + + let responseMessage = { + text: await handleText(response, true), + suggestions: + response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text), + jailbreak: endpointOption?.jailbreak + }; + // // response.text = await handleText(response, true); + // response.suggestions = + // response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text); + + if (endpointOption?.jailbreak) { + responseMessage.conversationId = response.jailbreakConversationId; + responseMessage.messageId = response.messageId || response.details.messageId; + responseMessage.parentMessageId = overrideParentMessageId || response.parentMessageId || userMessageId; + responseMessage.sender = 'Sydney'; + } else { + responseMessage.conversationId = response.conversationId; + responseMessage.messageId = response.messageId || response.details.messageId; + response.parentMessageId = + overrideParentMessageId || response.parentMessageId || response.details.requestId || userMessageId; + responseMessage.sender = 'BingAI'; + } + + await saveMessage(responseMessage); + + // STEP2 update the convosation. + + // First update conversationId if needed + // Note! // Bing API will not use our conversationId at the first time, // so change the placeholder conversationId to the real one. // Attition: the api will also create new conversationId while using invalid userMessage.parentMessageId, // but in this situation, don't change the conversationId, but create new convo. - if (conversationId != response.conversationId && isNewConversation) - await saveConvo(req?.session?.user?.username, { - conversationId: conversationId, - newConversationId: response.conversationId || conversationId - }); - conversationId = response.conversationId || conversationId; - // STEP2 update the user message - userMessage.conversationSignature = - endpointOption.conversationSignature || response.conversationSignature; + let conversationUpdate = { conversationId, endpoint: 'bingAI' }; + if (conversationId != responseMessage.conversationId && isNewConversation) + conversationUpdate = { + ...conversationUpdate, + conversationId: conversationId, + newConversationId: responseMessage.conversationId || conversationId + }; + conversationId = responseMessage.conversationId || conversationId; + + if (endpointOption?.jailbreak) { + conversationUpdate.jailbreak = true; + conversationUpdate.jailbreakConversationId = response.jailbreakConversationId; + } else { + conversationUpdate.jailbreak = false; + conversationUpdate.conversationSignature = response.conversationSignature; + conversationUpdate.clientId = response.clientId; + conversationUpdate.invocationId = response.invocationId; + } + + await saveConvo(req?.session?.user?.username, conversationUpdate); + + // STEP3 update the user message userMessage.conversationId = conversationId; - userMessage.invocationId = endpointOption.invocationId; - userMessage.messageId = response.details.requestId || userMessageId; + userMessage.messageId = responseMessage.parentMessageId; // If response has parentMessageId, the fake userMessage.messageId should be updated to the real one. if (!overrideParentMessageId) { const oldUserMessageId = userMessageId; - userMessageId = response.details.requestId; - await saveBingMessage({ ...userMessage, messageId: oldUserMessageId, newMessageId: userMessageId }); + await saveMessage({ ...userMessage, messageId: oldUserMessageId, newMessageId: userMessage.messageId }); } - - response.text = response.response || response.details.spokenText || '**Bing refused to answer.**'; - // delete response.response; - // response.id = response.details.messageId; - response.suggestions = - response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text); - response.sender = endpointOption?.jailbreak ? 'Sydney' : 'BingAI'; - // response.final = true; - - response.messageId = response.details.messageId; - // override the parentMessageId, for the regeneration. - response.parentMessageId = overrideParentMessageId || response.details.requestId || userMessageId; - - response.text = await handleText(response, true); - await saveBingMessage(response); - await saveConvo(req?.session?.user?.username, { - ...endpointOption, - ...response - }); + userMessageId = userMessage.messageId; sendMessage(res, { title: await getConvoTitle(req?.session?.user?.username, conversationId), final: true, conversation: await getConvo(req?.session?.user?.username, conversationId), requestMessage: userMessage, - responseMessage: response + responseMessage: responseMessage }); res.end(); diff --git a/api/server/routes/askOpenAI.js b/api/server/routes/askOpenAI.js index 2d27f263ce..c8f60ae056 100644 --- a/api/server/routes/askOpenAI.js +++ b/api/server/routes/askOpenAI.js @@ -95,47 +95,57 @@ const ask = async ({ const progressCallback = createOnProgress(); const abortController = new AbortController(); res.on('close', () => abortController.abort()); - let gptResponse = await client({ + let response = await client({ text, parentMessageId: userParentMessageId, conversationId, ...endpointOption, - onProgress: progressCallback.call(null, { res, text }), + onProgress: progressCallback.call(null, { + res, + text, + parentMessageId: overrideParentMessageId || userMessageId + }), abortController }); - gptResponse.text = gptResponse.response; - console.log('CLIENT RESPONSE', gptResponse); + console.log('CLIENT RESPONSE', response); - if (gptResponse.parentMessageId) { - // If gptResponse has parentMessageId, the fake userMessage.messageId should be updated to the real one. - if (!overrideParentMessageId) { - const oldUserMessageId = userMessageId; - userMessageId = gptResponse.parentMessageId; - userMessage.messageId = userMessageId; - await saveMessage({ ...userMessage, messageId: oldUserMessageId, newMessageId: userMessageId }); - } - } else { - delete gptResponse.response; + // STEP1 generate response message + response.text = response.response || '**ChatGPT refused to answer.**'; + + let responseMessage = { + conversationId: response.conversationId, + messageId: response.messageId, + parentMessageId: overrideParentMessageId || userMessageId, + text: await handleText(response), + sender: endpointOption?.chatGptLabel || 'ChatGPT' + }; + + await saveMessage(responseMessage); + + // STEP2 update the conversation + conversationId = responseMessage.conversationId || conversationId; + // it seems openAI will not change the conversationId. + // let conversationUpdate = { conversationId, endpoint: 'openAI' }; + // await saveConvo(req?.session?.user?.username, conversationUpdate); + + // STEP3 update the user message + userMessage.conversationId = conversationId; + userMessage.messageId = responseMessage.parentMessageId; + + // If gptResponse has parentMessageId, the fake userMessage.messageId should be updated to the real one. + if (!overrideParentMessageId) { + const oldUserMessageId = userMessageId; + await saveMessage({ ...userMessage, messageId: oldUserMessageId, newMessageId: userMessage.messageId }); } - - gptResponse.parentMessageId = overrideParentMessageId || userMessageId; - gptResponse.sender = endpointOption?.chatGptLabel || 'ChatGPT'; - // gptResponse.model = model; - gptResponse.text = await handleText(gptResponse); - - await saveMessage(gptResponse); - await updateConvo(req?.session?.user?.username, { - ...gptResponse, - oldConvoId: conversationId - }); + userMessageId = userMessage.messageId; sendMessage(res, { title: await getConvoTitle(req?.session?.user?.username, conversationId), final: true, conversation: await getConvo(req?.session?.user?.username, conversationId), requestMessage: userMessage, - responseMessage: gptResponse + responseMessage: responseMessage }); res.end(); @@ -147,6 +157,7 @@ const ask = async ({ }); } } catch (error) { + console.error(error); const errorMessage = { messageId: crypto.randomUUID(), sender: endpointOption?.chatGptLabel || 'ChatGPT', diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js deleted file mode 100644 index d74e4a3afe..0000000000 --- a/api/server/routes/askSydney.js +++ /dev/null @@ -1,198 +0,0 @@ -const express = require('express'); -const crypto = require('crypto'); -const router = express.Router(); -const { titleConvo, askSydney } = require('../../app/'); -const { saveBingMessage, saveConvo, updateConvo, getConvoTitle } = require('../../models'); -const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers'); - -router.post('/', async (req, res) => { - const { - model, - text, - overrideParentMessageId=null, - parentMessageId, - conversationId: oldConversationId, - ...convo - } = req.body; - if (text.length === 0) { - return handleError(res, { text: 'Prompt empty or too short' }); - } - - const conversationId = oldConversationId || crypto.randomUUID(); - const isNewConversation = !oldConversationId; - - const userMessageId = convo.messageId; - const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'; - let userMessage = { - messageId: userMessageId, - sender: 'User', - text, - parentMessageId: userParentMessageId, - conversationId, - isCreatedByUser: true - }; - - console.log('ask log', { - model, - ...convo, - ...userMessage - }); - - if (!overrideParentMessageId) { - await saveBingMessage(userMessage); - await saveConvo(req?.session?.user?.username, { model, ...convo, ...userMessage }); - } - - return await ask({ - isNewConversation, - userMessage, - model, - convo, - preSendRequest: true, - overrideParentMessageId, - req, - res - }); -}); - -const ask = async ({ - isNewConversation, - overrideParentMessageId = null, - userMessage, - model, - convo, - preSendRequest = true, - req, - res -}) => { - let { - text, - parentMessageId: userParentMessageId, - conversationId, - messageId: userMessageId - } = userMessage; - - res.writeHead(200, { - Connection: 'keep-alive', - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache, no-transform', - 'Access-Control-Allow-Origin': '*', - 'X-Accel-Buffering': 'no' - }); - - if (preSendRequest) sendMessage(res, { message: userMessage, created: true }); - - try { - const progressCallback = createOnProgress(); - - const abortController = new AbortController(); - res.on('close', () => { - console.log('The client has disconnected.'); - // 执行其他操作 - abortController.abort(); - }) - - let response = await askSydney({ - text, - onProgress: progressCallback.call(null, model, { - res, - text, - parentMessageId: overrideParentMessageId || userMessageId - }), - convo: { - ...convo, - parentMessageId: userParentMessageId, - conversationId - }, - abortController - }); - - console.log('SYDNEY RESPONSE', response); - // console.dir(response, { depth: null }); - - userMessage.conversationSignature = - convo.conversationSignature || response.conversationSignature; - userMessage.conversationId = response.conversationId || conversationId; - userMessage.invocationId = response.invocationId; - // Unlike gpt and bing, Sydney will never accept our given userMessage.messageId, it will generate its own one. - userMessage.messageId = response.parentMessageId || userMessageId; - - // Save sydney response - // response.id = response.messageId; - response.invocationId = convo.invocationId ? convo.invocationId + 1 : 1; - response.conversationId = conversationId ? conversationId : crypto.randomUUID(); - response.conversationSignature = convo.conversationSignature - ? convo.conversationSignature - : crypto.randomUUID(); - response.text = response.response || response.details.spokenText || '**Bing refused to answer.**'; - // delete response.response; - response.suggestions = - response.details.suggestedResponses && - response.details.suggestedResponses.map((s) => s.text); - response.sender = model; - // response.final = true; - - // override the parentMessageId, for the regeneration. - response.parentMessageId = - overrideParentMessageId || response.parentMessageId || userMessageId; - - // Save user message - userMessage.conversationId = response.conversationId || conversationId; - if (!overrideParentMessageId) - await saveBingMessage({ oldMessageId: userMessageId, ...userMessage }); - - // Bing API will not use our conversationId at the first time, - // so change the placeholder conversationId to the real one. - // Attition: the api will also create new conversationId while using invalid userMessage.parentMessageId, - // but in this situation, don't change the conversationId, but create new convo. - if (conversationId != userMessage.conversationId && isNewConversation) - await updateConvo( - req?.session?.user?.username, - { - conversationId: conversationId, - newConversationId: userMessage.conversationId - } - ); - conversationId = userMessage.conversationId; - - response.text = await handleText(response, true); - // Save sydney response & convo, then send - await saveBingMessage(response); - await updateConvo(req?.session?.user?.username, { model, chatGptLabel: null, promptPrefix: null, ...convo, ...response }); - - sendMessage(res, { - title: await getConvoTitle(req?.session?.user?.username, conversationId), - final: true, - requestMessage: userMessage, - responseMessage: response - }); - res.end(); - - if (userParentMessageId == '00000000-0000-0000-0000-000000000000') { - const title = await titleConvo({ model, text, response }); - - await updateConvo( - req?.session?.user?.username, - { - conversationId, - title - } - ); - } - } catch (error) { - console.log(error); - // await deleteMessages({ messageId: userMessageId }); - const errorMessage = { - messageId: crypto.randomUUID(), - sender: model, - conversationId, - parentMessageId: overrideParentMessageId || userMessageId, - error: true, - text: error.message - }; - await saveBingMessage(errorMessage); - handleError(res, errorMessage); - } -}; - -module.exports = router;