feat: merge all message.id into message.messageId

feat: the first message will have a parentMessageId as 00000000-0000-0000-0000-000000000000 (in order not to create new convo when resubmit)
feat: ask will return the userMessage as well, to send back the messageId

TODO: comment out the title generation.
TODO: bing version need to be test

fix: never use the same messageId
fix: never delete exist messages
fix: connect response.parentMessageId to the userMessage.messageId
fix: set default convo title as new Chat
This commit is contained in:
Wentao Lyu 2023-03-13 12:35:55 +08:00
parent be71140dd4
commit 0ed8a40a41
7 changed files with 159 additions and 85 deletions

View file

@ -13,7 +13,7 @@ const convoSchema = mongoose.Schema({
}, },
title: { title: {
type: String, type: String,
default: 'New conversation' default: 'New Chat'
}, },
jailbreakConversationId: { jailbreakConversationId: {
type: String type: String

View file

@ -1,7 +1,7 @@
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const messageSchema = mongoose.Schema({ const messageSchema = mongoose.Schema({
id: { messageId: {
type: String, type: String,
unique: true, unique: true,
required: true required: true
@ -42,24 +42,24 @@ const messageSchema = mongoose.Schema({
const Message = mongoose.models.Message || mongoose.model('Message', messageSchema); const Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
module.exports = { module.exports = {
saveMessage: async ({ id, conversationId, parentMessageId, sender, text, isCreatedByUser=false }) => { saveMessage: async ({ messageId, conversationId, parentMessageId, sender, text, isCreatedByUser=false }) => {
try { try {
await Message.findOneAndUpdate({ id }, { await Message.findOneAndUpdate({ messageId }, {
conversationId, conversationId,
parentMessageId, parentMessageId,
sender, sender,
text, text,
isCreatedByUser isCreatedByUser
}, { upsert: true, new: true }); }, { upsert: true, new: true });
return { id, conversationId, parentMessageId, sender, text, isCreatedByUser }; return { messageId, conversationId, parentMessageId, sender, text, isCreatedByUser };
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return { message: 'Error saving message' }; return { message: 'Error saving message' };
} }
}, },
deleteMessagesSince: async ({ id, conversationId }) => { deleteMessagesSince: async ({ messageId, conversationId }) => {
try { try {
message = await Message.findOne({ id }).exec() message = await Message.findOne({ messageId }).exec()
if (message) if (message)
return await Message.find({ conversationId }).deleteMany({ createdAt: { $gt: message.createdAt } }).exec(); return await Message.find({ conversationId }).deleteMany({ createdAt: { $gt: message.createdAt } }).exec();

View file

@ -17,18 +17,26 @@ router.use('/bing', askBing);
router.use('/sydney', askSydney); router.use('/sydney', askSydney);
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
let { id, model, text, parentMessageId, conversationId, chatGptLabel, promptPrefix } = req.body; let { model, text, parentMessageId, conversationId, chatGptLabel, promptPrefix } = req.body;
if (text.length === 0) { if (text.length === 0) {
return handleError(res, 'Prompt empty or too short'); return handleError(res, 'Prompt empty or too short');
} }
const userMessageId = id || crypto.randomUUID(); const userMessageId = crypto.randomUUID();
let userMessage = { id: userMessageId, sender: 'User', text, parentMessageId, conversationId, isCreatedByUser: true }; const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'
let userMessage = {
messageId: userMessageId,
sender: 'User',
text,
parentMessageId: userParentMessageId,
conversationId,
isCreatedByUser: true
};
console.log('ask log', { console.log('ask log', {
model, model,
...userMessage, ...userMessage,
parentMessageId, parentMessageId: userParentMessageId,
conversationId, conversationId,
chatGptLabel, chatGptLabel,
promptPrefix promptPrefix
@ -53,11 +61,11 @@ router.post('/', async (req, res) => {
} }
} }
if (id) { // if (messageId) {
// existing conversation // // existing conversation
await saveMessage(userMessage); // await saveMessage(userMessage);
await deleteMessagesSince(userMessage); // await deleteMessagesSince(userMessage);
} else {} // } else {}
res.writeHead(200, { res.writeHead(200, {
Connection: 'keep-alive', Connection: 'keep-alive',
@ -72,7 +80,6 @@ router.post('/', async (req, res) => {
let tokens = ''; let tokens = '';
const progressCallback = async (partial) => { const progressCallback = async (partial) => {
if (i === 0 && typeof partial === 'object') { if (i === 0 && typeof partial === 'object') {
userMessage.parentMessageId = parentMessageId ? parentMessageId : partial.id;
userMessage.conversationId = conversationId ? conversationId : partial.conversationId; userMessage.conversationId = conversationId ? conversationId : partial.conversationId;
await saveMessage(userMessage); await saveMessage(userMessage);
sendMessage(res, { ...partial, initial: true }); sendMessage(res, { ...partial, initial: true });
@ -101,7 +108,7 @@ router.post('/', async (req, res) => {
text, text,
progressCallback, progressCallback,
convo: { convo: {
parentMessageId, parentMessageId: userParentMessageId,
conversationId conversationId
}, },
chatGptLabel, chatGptLabel,
@ -112,9 +119,8 @@ router.post('/', async (req, res) => {
if (!gptResponse.parentMessageId) { if (!gptResponse.parentMessageId) {
gptResponse.text = gptResponse.response; gptResponse.text = gptResponse.response;
gptResponse.id = gptResponse.messageId; // gptResponse.id = gptResponse.messageId;
gptResponse.parentMessageId = gptResponse.messageId; gptResponse.parentMessageId = userMessage.messageId;
userMessage.parentMessageId = parentMessageId ? parentMessageId : gptResponse.messageId;
userMessage.conversationId = conversationId userMessage.conversationId = conversationId
? conversationId ? conversationId
: gptResponse.conversationId; : gptResponse.conversationId;
@ -130,15 +136,15 @@ router.post('/', async (req, res) => {
return handleError(res, 'Prompt empty or too short'); return handleError(res, 'Prompt empty or too short');
} }
if (!parentMessageId) { // if (!parentMessageId) {
gptResponse.title = await titleConvo({ // gptResponse.title = await titleConvo({
model, // model,
message: text, // message: text,
response: JSON.stringify(gptResponse.text) // response: JSON.stringify(gptResponse.text)
}); // });
} // }
gptResponse.sender = model === 'chatgptCustom' ? chatGptLabel : model; gptResponse.sender = model === 'chatgptCustom' ? chatGptLabel : model;
gptResponse.final = true; // gptResponse.final = true;
gptResponse.text = await detectCode(gptResponse.text); gptResponse.text = await detectCode(gptResponse.text);
if (chatGptLabel?.length > 0 && model === 'chatgptCustom') { if (chatGptLabel?.length > 0 && model === 'chatgptCustom') {
@ -151,11 +157,15 @@ router.post('/', async (req, res) => {
await saveMessage(gptResponse); await saveMessage(gptResponse);
await saveConvo(gptResponse); await saveConvo(gptResponse);
sendMessage(res, gptResponse); sendMessage(res, {
final: true,
requestMessage: userMessage,
responseMessage: gptResponse
});
res.end(); res.end();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
await deleteMessages({ id: userMessageId }); await deleteMessages({ messageId: userMessageId });
handleError(res, error.message); handleError(res, error.message);
} }
}); });

View file

@ -7,22 +7,36 @@ const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g; const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
const { id, model, text, ...convo } = req.body; const { model, text, parentMessageId, conversationId, ...convo } = req.body;
if (text.length === 0) { if (text.length === 0) {
return handleError(res, 'Prompt empty or too short'); return handleError(res, 'Prompt empty or too short');
} }
const userMessageId = id || crypto.randomUUID(); const userMessageId = messageId;
let userMessage = { id: userMessageId, sender: 'User', text, isCreatedByUser: true }; 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, ...userMessage, ...convo }); console.log('ask log', {
model,
...userMessage,
parentMessageId: userParentMessageId,
conversationId,
...convo
});
// if (messageId) {
// // existing conversation
// await saveMessage(userMessage);
// await deleteMessagesSince(userMessage);
// } else {}
if (id) {
// existing conversation
await saveMessage(userMessage);
await deleteMessagesSince(userMessage);
} else {}
res.writeHead(200, { res.writeHead(200, {
Connection: 'keep-alive', Connection: 'keep-alive',
'Content-Type': 'text/event-stream', 'Content-Type': 'text/event-stream',
@ -43,7 +57,11 @@ router.post('/', async (req, res) => {
let response = await askBing({ let response = await askBing({
text, text,
progressCallback, progressCallback,
convo convo: {
parentMessageId: userParentMessageId,
conversationId,
...convo
},
}); });
console.log('BING RESPONSE'); console.log('BING RESPONSE');
@ -52,26 +70,27 @@ router.post('/', async (req, res) => {
userMessage.conversationSignature = userMessage.conversationSignature =
convo.conversationSignature || response.conversationSignature; convo.conversationSignature || response.conversationSignature;
userMessage.conversationId = convo.conversationId || response.conversationId; userMessage.conversationId = conversationId || response.conversationId;
userMessage.invocationId = response.invocationId; userMessage.invocationId = response.invocationId;
await saveMessage(userMessage); await saveMessage(userMessage);
if (!convo.conversationSignature) { // if (!convo.conversationSignature) {
response.title = await titleConvo({ // response.title = await titleConvo({
model, // model,
message: text, // message: text,
response: JSON.stringify(response.response) // response: JSON.stringify(response.response)
}); // });
} // }
response.text = response.response; response.text = response.response;
delete response.response; delete response.response;
response.id = response.details.messageId; // response.id = response.details.messageId;
response.suggestions = response.suggestions =
response.details.suggestedResponses && response.details.suggestedResponses &&
response.details.suggestedResponses.map((s) => s.text); response.details.suggestedResponses.map((s) => s.text);
response.sender = model; response.sender = model;
response.final = true; response.parentMessageId = gptResponse.parentMessageId || userMessage.messageId
// response.final = true;
const links = getCitations(response); const links = getCitations(response);
response.text = response.text =
@ -80,11 +99,15 @@ router.post('/', async (req, res) => {
await saveMessage(response); await saveMessage(response);
await saveConvo(response); await saveConvo(response);
sendMessage(res, response); sendMessage(res, {
final: true,
requestMessage: userMessage,
responseMessage: gptResponse
});
res.end(); res.end();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
await deleteMessages({ id: userMessageId }); await deleteMessages({ messageId: userMessageId });
handleError(res, error.message); handleError(res, error.message);
} }
}); });

View file

@ -7,22 +7,37 @@ const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g; const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
const { id, model, text, ...convo } = req.body; const { model, text, parentMessageId, conversationId, ...convo } = req.body;
if (text.length === 0) { if (text.length === 0) {
return handleError(res, 'Prompt empty or too short'); return handleError(res, 'Prompt empty or too short');
} }
const userMessageId = id || crypto.randomUUID(); const userMessageId = messageId;
let userMessage = { id: userMessageId, sender: 'User', text, isCreatedByUser: true }; 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, ...userMessage, ...convo });
if (id) { console.log('ask log', {
// existing conversation model,
await saveMessage(userMessage); ...userMessage,
await deleteMessagesSince(userMessage); parentMessageId: userParentMessageId,
} else {} conversationId,
...convo
});
// if (messageId) {
// // existing conversation
// await saveMessage(userMessage);
// await deleteMessagesSince(userMessage);
// } else {}
res.writeHead(200, { res.writeHead(200, {
Connection: 'keep-alive', Connection: 'keep-alive',
'Content-Type': 'text/event-stream', 'Content-Type': 'text/event-stream',
@ -43,7 +58,11 @@ router.post('/', async (req, res) => {
let response = await askSydney({ let response = await askSydney({
text, text,
progressCallback, progressCallback,
convo convo: {
parentMessageId: userParentMessageId,
conversationId,
...convo
},
}); });
console.log('SYDNEY RESPONSE'); console.log('SYDNEY RESPONSE');
@ -52,19 +71,19 @@ router.post('/', async (req, res) => {
const hasCitations = response.response.match(citationRegex)?.length > 0; const hasCitations = response.response.match(citationRegex)?.length > 0;
// Save sydney response // Save sydney response
response.id = response.messageId; // response.id = response.messageId;
// response.parentMessageId = convo.parentMessageId ? convo.parentMessageId : response.messageId; // response.parentMessageId = convo.parentMessageId ? convo.parentMessageId : response.messageId;
response.parentMessageId = response.messageId; response.parentMessageId = response.messageId;
response.invocationId = convo.invocationId ? convo.invocationId + 1 : 1; response.invocationId = convo.invocationId ? convo.invocationId + 1 : 1;
response.title = convo.jailbreakConversationId response.title = convo.jailbreakConversationId
? await getConvoTitle(convo.conversationId) ? await getConvoTitle(conversationId)
: await titleConvo({ : await titleConvo({
model, model,
message: text, message: text,
response: JSON.stringify(response.response) response: JSON.stringify(response.response)
}); });
response.conversationId = convo.conversationId response.conversationId = conversationId
? convo.conversationId ? conversationId
: crypto.randomUUID(); : crypto.randomUUID();
response.conversationSignature = convo.conversationSignature response.conversationSignature = convo.conversationSignature
? convo.conversationSignature ? convo.conversationSignature
@ -75,7 +94,8 @@ router.post('/', async (req, res) => {
response.details.suggestedResponses && response.details.suggestedResponses &&
response.details.suggestedResponses.map((s) => s.text); response.details.suggestedResponses.map((s) => s.text);
response.sender = model; response.sender = model;
response.final = true; response.parentMessageId = gptResponse.parentMessageId || userMessage.messageId
// response.final = true;
const links = getCitations(response); const links = getCitations(response);
response.text = response.text =
@ -84,17 +104,20 @@ router.post('/', async (req, res) => {
// Save user message // Save user message
userMessage.conversationId = response.conversationId; userMessage.conversationId = response.conversationId;
userMessage.parentMessageId = response.parentMessageId;
await saveMessage(userMessage); await saveMessage(userMessage);
// Save sydney response & convo, then send // Save sydney response & convo, then send
await saveMessage(response); await saveMessage(response);
await saveConvo(response); await saveConvo(response);
sendMessage(res, response); sendMessage(res, {
final: true,
requestMessage: userMessage,
responseMessage: gptResponse
});
res.end(); res.end();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
await deleteMessages({ id: userMessageId }); await deleteMessages({ messageId: userMessageId });
handleError(res, error.message); handleError(res, error.message);
} }
}); });

View file

@ -34,22 +34,36 @@ export default function TextChat({ messages }) {
}; };
const convoHandler = (data, currentState) => { const convoHandler = (data, currentState) => {
const { requestMessage, responseMessage } = data;
const { messages, currentMsg, message, isCustomModel, sender } = const { messages, currentMsg, message, isCustomModel, sender } =
currentState; currentState;
const { model, chatGptLabel, promptPrefix } = message; const { model, chatGptLabel, promptPrefix } = message;
dispatch( dispatch(
setMessages([...messages, currentMsg, { sender, text: data.text || data.response }]) setMessages([...messages,
{
...requestMessage,
// messageId: data?.parentMessageId,
},
{
...responseMessage,
// sender,
// text: data.text || data.response,
}
])
); );
const isBing = model === 'bingai' || model === 'sydney'; const isBing = model === 'bingai' || model === 'sydney';
// if (!message.messageId)
if (!isBing && convo.conversationId === null && convo.parentMessageId === null) { if (!isBing && convo.conversationId === null && convo.parentMessageId === null) {
const { title, conversationId, id } = data; const { title } = data;
const { conversationId, messageId } = responseMessage;
dispatch( dispatch(
setConversation({ setConversation({
title, title,
conversationId, conversationId,
parentMessageId: id, parentMessageId: messageId,
jailbreakConversationId: null, jailbreakConversationId: null,
conversationSignature: null, conversationSignature: null,
clientId: null, clientId: null,
@ -64,7 +78,8 @@ export default function TextChat({ messages }) {
convo.invocationId === null convo.invocationId === null
) { ) {
console.log('Bing data:', data); console.log('Bing data:', data);
const { title, conversationSignature, clientId, conversationId, invocationId } = data; const { title } = data;
const { conversationSignature, clientId, conversationId, invocationId } = responseMessage;
dispatch( dispatch(
setConversation({ setConversation({
title, title,
@ -76,15 +91,15 @@ export default function TextChat({ messages }) {
}) })
); );
} else if (model === 'sydney') { } else if (model === 'sydney') {
const { title } = data;
const { const {
title,
jailbreakConversationId, jailbreakConversationId,
parentMessageId, parentMessageId,
conversationSignature, conversationSignature,
clientId, clientId,
conversationId, conversationId,
invocationId invocationId
} = data; } = responseMessage;
dispatch( dispatch(
setConversation({ setConversation({
title, title,
@ -202,16 +217,19 @@ export default function TextChat({ messages }) {
} }
const data = JSON.parse(e.data); const data = JSON.parse(e.data);
let text = data.text || data.response;
console.log(data) // if (data.message) {
if (data.message) { // messageHandler(text, currentState);
messageHandler(text, currentState); // }
}
if (data.final) { if (data.final) {
convoHandler(data, currentState); convoHandler(data, currentState);
console.log('final', data); console.log('final', data);
} else { } else {
let text = data.text || data.response;
if (data.message) {
messageHandler(text, currentState);
}
// console.log('dataStream', data); // console.log('dataStream', data);
} }
}; };

View file

@ -14,7 +14,7 @@ const currentSlice = createSlice({
setEmptyMessage: (state) => { setEmptyMessage: (state) => {
state.messages = [ state.messages = [
{ {
id: '1', messageId: '1',
conversationId: '1', conversationId: '1',
parentMessageId: '1', parentMessageId: '1',
sender: '', sender: '',