From dd1f74da72ff44d58e992e6f67f6c5cd4fa7ebab Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 05:20:35 +0800
Subject: [PATCH 01/47] replace all created to timestamps: true in db
---
api/models/Conversation.js | 10 +++-------
api/models/CustomGpt.js | 6 +-----
api/models/Message.js | 11 ++++++-----
api/models/Prompt.js | 6 +-----
4 files changed, 11 insertions(+), 22 deletions(-)
diff --git a/api/models/Conversation.js b/api/models/Conversation.js
index 1ff778e957..0ae336641c 100644
--- a/api/models/Conversation.js
+++ b/api/models/Conversation.js
@@ -38,11 +38,7 @@ const convoSchema = mongoose.Schema({
},
suggestions: [{ type: String }],
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
- created: {
- type: Date,
- default: Date.now
- }
-});
+}, { timestamps: true });
const Conversation =
mongoose.models.Conversation || mongoose.model('Conversation', convoSchema);
@@ -85,14 +81,14 @@ module.exports = {
return { message: 'Error updating conversation' };
}
},
- // getConvos: async () => await Conversation.find({}).sort({ created: -1 }).exec(),
+ // getConvos: async () => await Conversation.find({}).sort({ createdAt: -1 }).exec(),
getConvos: async (pageNumber = 1, pageSize = 12) => {
try {
const skip = (pageNumber - 1) * pageSize;
// const limit = pageNumber * pageSize;
const conversations = await Conversation.find({})
- .sort({ created: -1 })
+ .sort({ createdAt: -1 })
.skip(skip)
// .limit(limit)
.limit(pageSize)
diff --git a/api/models/CustomGpt.js b/api/models/CustomGpt.js
index 33bb75b124..1f02bdc4db 100644
--- a/api/models/CustomGpt.js
+++ b/api/models/CustomGpt.js
@@ -12,11 +12,7 @@ const customGptSchema = mongoose.Schema({
type: String,
required: true
},
- created: {
- type: Date,
- default: Date.now
- }
-});
+}, { timestamps: true });
const CustomGpt = mongoose.models.CustomGpt || mongoose.model('CustomGpt', customGptSchema);
diff --git a/api/models/Message.js b/api/models/Message.js
index e6657c060a..b2017c2698 100644
--- a/api/models/Message.js
+++ b/api/models/Message.js
@@ -32,11 +32,12 @@ const messageSchema = mongoose.Schema({
type: String,
required: true
},
- created: {
- type: Date,
- default: Date.now
+ isCreatedByUser: {
+ type: Boolean,
+ required: true,
+ default: false
}
-});
+}, { timestamps: true });
const Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
@@ -58,7 +59,7 @@ module.exports = {
},
getMessages: async (filter) => {
try {
- return await Message.find(filter).exec()
+ return await Message.find(filter).sort({createdAt: 1}).exec()
} catch (error) {
console.error(error);
return { message: 'Error getting messages' };
diff --git a/api/models/Prompt.js b/api/models/Prompt.js
index 612278d038..f122d5af40 100644
--- a/api/models/Prompt.js
+++ b/api/models/Prompt.js
@@ -12,11 +12,7 @@ const promptSchema = mongoose.Schema({
category: {
type: String,
},
- created: {
- type: Date,
- default: Date.now
- }
-});
+}, { timestamps: true });
const Prompt = mongoose.models.Prompt || mongoose.model('Prompt', promptSchema);
From b9975ac283c1bb82a253118fe53b920d8019b2ed Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 05:21:30 +0800
Subject: [PATCH 02/47] fix: missing setSubmission
---
client/src/components/Nav/ClearConvos.jsx | 1 +
client/src/components/Nav/MobileNav.jsx | 1 +
2 files changed, 2 insertions(+)
diff --git a/client/src/components/Nav/ClearConvos.jsx b/client/src/components/Nav/ClearConvos.jsx
index 9df2e9344e..c9a9b379ba 100644
--- a/client/src/components/Nav/ClearConvos.jsx
+++ b/client/src/components/Nav/ClearConvos.jsx
@@ -5,6 +5,7 @@ import manualSWR from '~/utils/fetchers';
import { useDispatch } from 'react-redux';
import { setNewConvo, removeAll } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
+import { setSubmission } from '~/store/submitSlice';
export default function ClearConvos() {
const dispatch = useDispatch();
diff --git a/client/src/components/Nav/MobileNav.jsx b/client/src/components/Nav/MobileNav.jsx
index 2a5123560d..e4107e9ac6 100644
--- a/client/src/components/Nav/MobileNav.jsx
+++ b/client/src/components/Nav/MobileNav.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setNewConvo } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
+import { setSubmission } from '~/store/submitSlice';
import { setText } from '~/store/textSlice';
export default function MobileNav({ setNavVisible }) {
From bdfc895800b27a9713118861d2892969c3c7b805 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 05:26:17 +0800
Subject: [PATCH 03/47] feat: support resubmit.
TODO: basic implementation. should add multi-path record in future.
feat: add deleteMessahesSince
feat: saveMessage will do createOrSave
feat: reorginazed submission
---
api/models/Message.js | 23 +++-
api/models/index.js | 3 +-
api/server/routes/ask.js | 14 +-
api/server/routes/askBing.js | 14 +-
api/server/routes/askSydney.js | 14 +-
client/src/components/Main/TextChat.jsx | 48 ++++---
.../src/components/Messages/HoverButtons.jsx | 4 +-
client/src/components/Messages/Message.jsx | 124 +++++++++++++++---
client/src/components/Messages/index.jsx | 10 +-
client/src/store/convoSlice.js | 2 +-
10 files changed, 189 insertions(+), 67 deletions(-)
diff --git a/api/models/Message.js b/api/models/Message.js
index b2017c2698..0e091e416c 100644
--- a/api/models/Message.js
+++ b/api/models/Message.js
@@ -42,21 +42,32 @@ const messageSchema = mongoose.Schema({
const Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
module.exports = {
- saveMessage: async ({ id, conversationId, parentMessageId, sender, text }) => {
+ saveMessage: async ({ id, conversationId, parentMessageId, sender, text, isCreatedByUser=false }) => {
try {
- await Message.create({
- id,
+ await Message.findOneAndUpdate({ id }, {
conversationId,
parentMessageId,
sender,
- text
- });
- return { id, conversationId, parentMessageId, sender, text };
+ text,
+ isCreatedByUser
+ }, { upsert: true, new: true });
+ return { id, conversationId, parentMessageId, sender, text, isCreatedByUser };
} catch (error) {
console.error(error);
return { message: 'Error saving message' };
}
},
+ deleteMessagesSince: async ({ id, conversationId }) => {
+ try {
+ message = await Message.findOne({ id }).exec()
+
+ if (message)
+ return await Message.find({ conversationId }).deleteMany({ createdAt: { $gt: message.createdAt } }).exec();
+ } catch (error) {
+ console.error(error);
+ return { message: 'Error deleting messages' };
+ }
+ },
getMessages: async (filter) => {
try {
return await Message.find(filter).sort({createdAt: 1}).exec()
diff --git a/api/models/index.js b/api/models/index.js
index 8af5a1aa9c..4ed1285bbf 100644
--- a/api/models/index.js
+++ b/api/models/index.js
@@ -1,9 +1,10 @@
-const { saveMessage, deleteMessages } = require('./Message');
+const { saveMessage, deleteMessagesSince, deleteMessages } = require('./Message');
const { getCustomGpts, updateCustomGpt, updateByLabel, deleteCustomGpts } = require('./CustomGpt');
const { getConvoTitle, getConvo, saveConvo } = require('./Conversation');
module.exports = {
saveMessage,
+ deleteMessagesSince,
deleteMessages,
getConvoTitle,
getConvo,
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 1f18082dd4..9296f895d6 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -10,20 +10,20 @@ const {
customClient,
detectCode
} = require('../../app/');
-const { getConvo, saveMessage, deleteMessages, saveConvo } = require('../../models');
+const { getConvo, saveMessage, deleteMessagesSince, deleteMessages, saveConvo } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
router.use('/bing', askBing);
router.use('/sydney', askSydney);
router.post('/', async (req, res) => {
- let { model, text, parentMessageId, conversationId, chatGptLabel, promptPrefix } = req.body;
+ let { id, model, text, parentMessageId, conversationId, chatGptLabel, promptPrefix } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
- const userMessageId = crypto.randomUUID();
- let userMessage = { id: userMessageId, sender: 'User', text };
+ const userMessageId = id || crypto.randomUUID();
+ let userMessage = { id: userMessageId, sender: 'User', text, parentMessageId, conversationId, isCreatedByUser: true };
console.log('ask log', {
model,
@@ -53,6 +53,12 @@ router.post('/', async (req, res) => {
}
}
+ if (id) {
+ // existing conversation
+ await saveMessage(userMessage);
+ await deleteMessagesSince(userMessage);
+ } else {}
+
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index 76c39f6804..1e670b6d41 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -2,21 +2,27 @@ const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
-const { saveMessage, deleteMessages, saveConvo } = require('../../models');
+const { saveMessage, deleteMessages, deleteMessagesSince, saveConvo } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { model, text, ...convo } = req.body;
+ const { id, model, text, ...convo } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
- const userMessageId = crypto.randomUUID();
- let userMessage = { id: userMessageId, sender: 'User', text };
+ const userMessageId = id || crypto.randomUUID();
+ let userMessage = { id: userMessageId, sender: 'User', text, isCreatedByUser: true };
console.log('ask log', { model, ...userMessage, ...convo });
+ if (id) {
+ // existing conversation
+ await saveMessage(userMessage);
+ await deleteMessagesSince(userMessage);
+ } else {}
+
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 219f9b2437..8bc4b8c54f 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -2,21 +2,27 @@ const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askSydney } = require('../../app/');
-const { saveMessage, deleteMessages, saveConvo, getConvoTitle } = require('../../models');
+const { saveMessage, deleteMessages, saveConvo, deleteMessagesSince, getConvoTitle } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { model, text, ...convo } = req.body;
+ const { id, model, text, ...convo } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
- const userMessageId = crypto.randomUUID();
- let userMessage = { id: userMessageId, sender: 'User', text };
+ const userMessageId = id || crypto.randomUUID();
+ let userMessage = { id: userMessageId, sender: 'User', text, isCreatedByUser: true };
console.log('ask log', { model, ...userMessage, ...convo });
+ if (id) {
+ // existing conversation
+ await saveMessage(userMessage);
+ await deleteMessagesSince(userMessage);
+ } else {}
+
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index 12efc7c123..e73becc743 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -29,13 +29,14 @@ export default function TextChat({ messages }) {
}, [convo?.conversationId, ])
const messageHandler = (data, currentState) => {
- const { messages, currentMsg, sender } = currentState;
+ const { messages, currentMsg, message, sender } = currentState;
dispatch(setMessages([...messages, currentMsg, { sender, text: data }]));
};
const convoHandler = (data, currentState) => {
- const { messages, currentMsg, sender, isCustomModel, model, chatGptLabel, promptPrefix } =
+ const { messages, currentMsg, message, isCustomModel, sender } =
currentState;
+ const { model, chatGptLabel, promptPrefix } = message;
dispatch(
setMessages([...messages, currentMsg, { sender, text: data.text || data.response }])
);
@@ -111,7 +112,7 @@ export default function TextChat({ messages }) {
setErrorMessage(event.data);
dispatch(setSubmitState(false));
dispatch(setMessages([...messages.slice(0, -2), currentMsg, errorResponse]));
- dispatch(setText(message));
+ dispatch(setText(message?.text));
dispatch(setError(true));
return;
};
@@ -127,7 +128,7 @@ export default function TextChat({ messages }) {
const isCustomModel = model === 'chatgptCustom' || !initial[model];
const message = text.trim();
- const currentMsg = { sender: 'User', text: message, current: true };
+ const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true };
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
const initialResponse = { sender, text: '' };
@@ -136,36 +137,41 @@ export default function TextChat({ messages }) {
dispatch(setText(''));
const submission = {
- model,
- text: message,
- convo,
- chatGptLabel,
- promptPrefix,
isCustomModel,
- message,
+ message: {
+ sender: 'User',
+ text: message,
+ isCreatedByUser: true,
+ model,
+ chatGptLabel,
+ promptPrefix,
+ },
messages,
currentMsg,
+ initialResponse,
sender,
- initialResponse
};
console.log('User Input:', message);
// handleSubmit(submission);
dispatch(setSubmission(submission));
};
- const createPayload = ({ model, text, convo, chatGptLabel, promptPrefix }) => {
+ const createPayload = ({ convo, message }) => {
const endpoint = `/api/ask`;
- let payload = { model, text, chatGptLabel, promptPrefix };
- if (convo.conversationId && convo.parentMessageId) {
- payload = {
- ...payload,
- conversationId: convo.conversationId,
- parentMessageId: convo.parentMessageId
- };
- }
+ let payload = { ...message };
+ const { model } = message
+
+ if (!payload.conversationId)
+ if (convo?.conversationId && convo?.parentMessageId) {
+ payload = {
+ ...payload,
+ conversationId: convo.conversationId,
+ parentMessageId: convo.parentMessageId
+ };
+ }
const isBing = model === 'bingai' || model === 'sydney';
- if (isBing && convo.conversationId) {
+ if (isBing && convo?.conversationId) {
payload = {
...payload,
jailbreakConversationId: convo.jailbreakConversationId,
diff --git a/client/src/components/Messages/HoverButtons.jsx b/client/src/components/Messages/HoverButtons.jsx
index 860023ed2f..35c5f14821 100644
--- a/client/src/components/Messages/HoverButtons.jsx
+++ b/client/src/components/Messages/HoverButtons.jsx
@@ -2,11 +2,11 @@ import React from 'react';
// import Clipboard from '../svg/Clipboard';
import EditIcon from '../svg/EditIcon';
-export default function HoverButtons({ user }) {
+export default function HoverButtons({ user, onClick }) {
return (
{user && (
-
From be71140dd49404fb68f5f005e4f999f47de1b328 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 10:20:07 +0800
Subject: [PATCH 05/47] fix: new message should append to the exist one
---
client/src/components/Main/TextChat.jsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index e73becc743..bd127a34d2 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -137,6 +137,7 @@ export default function TextChat({ messages }) {
dispatch(setText(''));
const submission = {
+ convo,
isCustomModel,
message: {
sender: 'User',
@@ -202,6 +203,7 @@ export default function TextChat({ messages }) {
const data = JSON.parse(e.data);
let text = data.text || data.response;
+ console.log(data)
if (data.message) {
messageHandler(text, currentState);
}
From 0ed8a40a41ecb31d9a2bcfd4bec4f8df9e875c9a Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 12:35:55 +0800
Subject: [PATCH 06/47] 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
---
api/models/Conversation.js | 2 +-
api/models/Message.js | 12 ++---
api/server/routes/ask.js | 58 ++++++++++++---------
api/server/routes/askBing.js | 69 ++++++++++++++++---------
api/server/routes/askSydney.js | 61 +++++++++++++++-------
client/src/components/Main/TextChat.jsx | 40 ++++++++++----
client/src/store/messageSlice.js | 2 +-
7 files changed, 159 insertions(+), 85 deletions(-)
diff --git a/api/models/Conversation.js b/api/models/Conversation.js
index 0ae336641c..c8daf78c4a 100644
--- a/api/models/Conversation.js
+++ b/api/models/Conversation.js
@@ -13,7 +13,7 @@ const convoSchema = mongoose.Schema({
},
title: {
type: String,
- default: 'New conversation'
+ default: 'New Chat'
},
jailbreakConversationId: {
type: String
diff --git a/api/models/Message.js b/api/models/Message.js
index 0e091e416c..db1746924b 100644
--- a/api/models/Message.js
+++ b/api/models/Message.js
@@ -1,7 +1,7 @@
const mongoose = require('mongoose');
const messageSchema = mongoose.Schema({
- id: {
+ messageId: {
type: String,
unique: true,
required: true
@@ -42,24 +42,24 @@ const messageSchema = mongoose.Schema({
const Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
module.exports = {
- saveMessage: async ({ id, conversationId, parentMessageId, sender, text, isCreatedByUser=false }) => {
+ saveMessage: async ({ messageId, conversationId, parentMessageId, sender, text, isCreatedByUser=false }) => {
try {
- await Message.findOneAndUpdate({ id }, {
+ await Message.findOneAndUpdate({ messageId }, {
conversationId,
parentMessageId,
sender,
text,
isCreatedByUser
}, { upsert: true, new: true });
- return { id, conversationId, parentMessageId, sender, text, isCreatedByUser };
+ return { messageId, conversationId, parentMessageId, sender, text, isCreatedByUser };
} catch (error) {
console.error(error);
return { message: 'Error saving message' };
}
},
- deleteMessagesSince: async ({ id, conversationId }) => {
+ deleteMessagesSince: async ({ messageId, conversationId }) => {
try {
- message = await Message.findOne({ id }).exec()
+ message = await Message.findOne({ messageId }).exec()
if (message)
return await Message.find({ conversationId }).deleteMany({ createdAt: { $gt: message.createdAt } }).exec();
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 9296f895d6..ea43392551 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -17,18 +17,26 @@ router.use('/bing', askBing);
router.use('/sydney', askSydney);
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) {
return handleError(res, 'Prompt empty or too short');
}
- const userMessageId = id || crypto.randomUUID();
- let userMessage = { id: userMessageId, sender: 'User', text, parentMessageId, conversationId, isCreatedByUser: true };
+ const userMessageId = crypto.randomUUID();
+ 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,
- parentMessageId,
+ parentMessageId: userParentMessageId,
conversationId,
chatGptLabel,
promptPrefix
@@ -53,11 +61,11 @@ router.post('/', async (req, res) => {
}
}
- if (id) {
- // existing conversation
- await saveMessage(userMessage);
- await deleteMessagesSince(userMessage);
- } else {}
+ // if (messageId) {
+ // // existing conversation
+ // await saveMessage(userMessage);
+ // await deleteMessagesSince(userMessage);
+ // } else {}
res.writeHead(200, {
Connection: 'keep-alive',
@@ -72,7 +80,6 @@ router.post('/', async (req, res) => {
let tokens = '';
const progressCallback = async (partial) => {
if (i === 0 && typeof partial === 'object') {
- userMessage.parentMessageId = parentMessageId ? parentMessageId : partial.id;
userMessage.conversationId = conversationId ? conversationId : partial.conversationId;
await saveMessage(userMessage);
sendMessage(res, { ...partial, initial: true });
@@ -101,7 +108,7 @@ router.post('/', async (req, res) => {
text,
progressCallback,
convo: {
- parentMessageId,
+ parentMessageId: userParentMessageId,
conversationId
},
chatGptLabel,
@@ -112,9 +119,8 @@ router.post('/', async (req, res) => {
if (!gptResponse.parentMessageId) {
gptResponse.text = gptResponse.response;
- gptResponse.id = gptResponse.messageId;
- gptResponse.parentMessageId = gptResponse.messageId;
- userMessage.parentMessageId = parentMessageId ? parentMessageId : gptResponse.messageId;
+ // gptResponse.id = gptResponse.messageId;
+ gptResponse.parentMessageId = userMessage.messageId;
userMessage.conversationId = conversationId
? conversationId
: gptResponse.conversationId;
@@ -130,15 +136,15 @@ router.post('/', async (req, res) => {
return handleError(res, 'Prompt empty or too short');
}
- if (!parentMessageId) {
- gptResponse.title = await titleConvo({
- model,
- message: text,
- response: JSON.stringify(gptResponse.text)
- });
- }
+ // if (!parentMessageId) {
+ // gptResponse.title = await titleConvo({
+ // model,
+ // message: text,
+ // response: JSON.stringify(gptResponse.text)
+ // });
+ // }
gptResponse.sender = model === 'chatgptCustom' ? chatGptLabel : model;
- gptResponse.final = true;
+ // gptResponse.final = true;
gptResponse.text = await detectCode(gptResponse.text);
if (chatGptLabel?.length > 0 && model === 'chatgptCustom') {
@@ -151,11 +157,15 @@ router.post('/', async (req, res) => {
await saveMessage(gptResponse);
await saveConvo(gptResponse);
- sendMessage(res, gptResponse);
+ sendMessage(res, {
+ final: true,
+ requestMessage: userMessage,
+ responseMessage: gptResponse
+ });
res.end();
} catch (error) {
console.log(error);
- await deleteMessages({ id: userMessageId });
+ await deleteMessages({ messageId: userMessageId });
handleError(res, error.message);
}
});
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index 1e670b6d41..08c086597d 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -7,22 +7,36 @@ const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { id, model, text, ...convo } = req.body;
+ const { model, text, parentMessageId, conversationId, ...convo } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
- const userMessageId = id || crypto.randomUUID();
- let userMessage = { id: userMessageId, sender: 'User', text, isCreatedByUser: true };
+ const userMessageId = 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, ...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, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
@@ -43,7 +57,11 @@ router.post('/', async (req, res) => {
let response = await askBing({
text,
progressCallback,
- convo
+ convo: {
+ parentMessageId: userParentMessageId,
+ conversationId,
+ ...convo
+ },
});
console.log('BING RESPONSE');
@@ -52,26 +70,27 @@ router.post('/', async (req, res) => {
userMessage.conversationSignature =
convo.conversationSignature || response.conversationSignature;
- userMessage.conversationId = convo.conversationId || response.conversationId;
+ userMessage.conversationId = conversationId || response.conversationId;
userMessage.invocationId = response.invocationId;
await saveMessage(userMessage);
- if (!convo.conversationSignature) {
- response.title = await titleConvo({
- model,
- message: text,
- response: JSON.stringify(response.response)
- });
- }
+ // if (!convo.conversationSignature) {
+ // response.title = await titleConvo({
+ // model,
+ // message: text,
+ // response: JSON.stringify(response.response)
+ // });
+ // }
response.text = response.response;
delete response.response;
- response.id = response.details.messageId;
+ // response.id = response.details.messageId;
response.suggestions =
response.details.suggestedResponses &&
response.details.suggestedResponses.map((s) => s.text);
response.sender = model;
- response.final = true;
+ response.parentMessageId = gptResponse.parentMessageId || userMessage.messageId
+ // response.final = true;
const links = getCitations(response);
response.text =
@@ -80,11 +99,15 @@ router.post('/', async (req, res) => {
await saveMessage(response);
await saveConvo(response);
- sendMessage(res, response);
+ sendMessage(res, {
+ final: true,
+ requestMessage: userMessage,
+ responseMessage: gptResponse
+ });
res.end();
} catch (error) {
console.log(error);
- await deleteMessages({ id: userMessageId });
+ await deleteMessages({ messageId: userMessageId });
handleError(res, error.message);
}
});
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 8bc4b8c54f..27284ec526 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -7,22 +7,37 @@ const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { id, model, text, ...convo } = req.body;
+ const { model, text, parentMessageId, conversationId, ...convo } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
- const userMessageId = id || crypto.randomUUID();
- let userMessage = { id: userMessageId, sender: 'User', text, isCreatedByUser: true };
+ const userMessageId = 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, ...userMessage, ...convo });
- if (id) {
- // existing conversation
- await saveMessage(userMessage);
- await deleteMessagesSince(userMessage);
- } else {}
-
+ console.log('ask log', {
+ model,
+ ...userMessage,
+ parentMessageId: userParentMessageId,
+ conversationId,
+ ...convo
+ });
+
+ // if (messageId) {
+ // // existing conversation
+ // await saveMessage(userMessage);
+ // await deleteMessagesSince(userMessage);
+ // } else {}
+
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
@@ -43,7 +58,11 @@ router.post('/', async (req, res) => {
let response = await askSydney({
text,
progressCallback,
- convo
+ convo: {
+ parentMessageId: userParentMessageId,
+ conversationId,
+ ...convo
+ },
});
console.log('SYDNEY RESPONSE');
@@ -52,19 +71,19 @@ router.post('/', async (req, res) => {
const hasCitations = response.response.match(citationRegex)?.length > 0;
// Save sydney response
- response.id = response.messageId;
+ // response.id = response.messageId;
// response.parentMessageId = convo.parentMessageId ? convo.parentMessageId : response.messageId;
response.parentMessageId = response.messageId;
response.invocationId = convo.invocationId ? convo.invocationId + 1 : 1;
response.title = convo.jailbreakConversationId
- ? await getConvoTitle(convo.conversationId)
+ ? await getConvoTitle(conversationId)
: await titleConvo({
model,
message: text,
response: JSON.stringify(response.response)
});
- response.conversationId = convo.conversationId
- ? convo.conversationId
+ response.conversationId = conversationId
+ ? conversationId
: crypto.randomUUID();
response.conversationSignature = convo.conversationSignature
? convo.conversationSignature
@@ -75,7 +94,8 @@ router.post('/', async (req, res) => {
response.details.suggestedResponses &&
response.details.suggestedResponses.map((s) => s.text);
response.sender = model;
- response.final = true;
+ response.parentMessageId = gptResponse.parentMessageId || userMessage.messageId
+ // response.final = true;
const links = getCitations(response);
response.text =
@@ -84,17 +104,20 @@ router.post('/', async (req, res) => {
// Save user message
userMessage.conversationId = response.conversationId;
- userMessage.parentMessageId = response.parentMessageId;
await saveMessage(userMessage);
// Save sydney response & convo, then send
await saveMessage(response);
await saveConvo(response);
- sendMessage(res, response);
+ sendMessage(res, {
+ final: true,
+ requestMessage: userMessage,
+ responseMessage: gptResponse
+ });
res.end();
} catch (error) {
console.log(error);
- await deleteMessages({ id: userMessageId });
+ await deleteMessages({ messageId: userMessageId });
handleError(res, error.message);
}
});
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index bd127a34d2..460d6954f3 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -34,22 +34,36 @@ export default function TextChat({ messages }) {
};
const convoHandler = (data, currentState) => {
+ const { requestMessage, responseMessage } = data;
const { messages, currentMsg, message, isCustomModel, sender } =
currentState;
const { model, chatGptLabel, promptPrefix } = message;
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';
+ // if (!message.messageId)
+
if (!isBing && convo.conversationId === null && convo.parentMessageId === null) {
- const { title, conversationId, id } = data;
+ const { title } = data;
+ const { conversationId, messageId } = responseMessage;
dispatch(
setConversation({
title,
conversationId,
- parentMessageId: id,
+ parentMessageId: messageId,
jailbreakConversationId: null,
conversationSignature: null,
clientId: null,
@@ -64,7 +78,8 @@ export default function TextChat({ messages }) {
convo.invocationId === null
) {
console.log('Bing data:', data);
- const { title, conversationSignature, clientId, conversationId, invocationId } = data;
+ const { title } = data;
+ const { conversationSignature, clientId, conversationId, invocationId } = responseMessage;
dispatch(
setConversation({
title,
@@ -76,15 +91,15 @@ export default function TextChat({ messages }) {
})
);
} else if (model === 'sydney') {
+ const { title } = data;
const {
- title,
jailbreakConversationId,
parentMessageId,
conversationSignature,
clientId,
conversationId,
invocationId
- } = data;
+ } = responseMessage;
dispatch(
setConversation({
title,
@@ -202,16 +217,19 @@ export default function TextChat({ messages }) {
}
const data = JSON.parse(e.data);
- let text = data.text || data.response;
- console.log(data)
- if (data.message) {
- messageHandler(text, currentState);
- }
+
+ // if (data.message) {
+ // messageHandler(text, currentState);
+ // }
if (data.final) {
convoHandler(data, currentState);
console.log('final', data);
} else {
+ let text = data.text || data.response;
+ if (data.message) {
+ messageHandler(text, currentState);
+ }
// console.log('dataStream', data);
}
};
diff --git a/client/src/store/messageSlice.js b/client/src/store/messageSlice.js
index c93f70048e..137c329822 100644
--- a/client/src/store/messageSlice.js
+++ b/client/src/store/messageSlice.js
@@ -14,7 +14,7 @@ const currentSlice = createSlice({
setEmptyMessage: (state) => {
state.messages = [
{
- id: '1',
+ messageId: '1',
conversationId: '1',
parentMessageId: '1',
sender: '',
From 5a409ccfa62041c154b0e5735d59b117b8d3dfeb Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 12:36:00 +0800
Subject: [PATCH 07/47] fix: don't resubmit html label fix: hide the resubmit
editor border
---
client/src/components/Messages/Message.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx
index 6b313961f0..2d6df0360e 100644
--- a/client/src/components/Messages/Message.jsx
+++ b/client/src/components/Messages/Message.jsx
@@ -95,7 +95,7 @@ export default function Message({
const wrapText = (text) => ;
const resubmitMessage = () => {
- const text = textEditor.current.innerHTML
+ const text = textEditor.current.innerText
if (convoError) {
dispatch(setError(false));
@@ -163,7 +163,7 @@ export default function Message({
{/*
*/}
-
{text}
From 8773878be2c72c733f763b59c324c4e7480d1565 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 13:11:53 +0800
Subject: [PATCH 08/47] feat: create conversation at the beginning then return
the userMessage
---
api/server/routes/ask.js | 18 ++++++-------
api/server/routes/askBing.js | 18 ++++++-------
api/server/routes/askSydney.js | 18 ++++++-------
.../components/Conversations/Conversation.jsx | 9 ++++---
client/src/components/Conversations/index.jsx | 1 +
client/src/components/Main/TextChat.jsx | 26 ++++++++++++++-----
6 files changed, 50 insertions(+), 40 deletions(-)
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index ea43392551..9be3fe7014 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -17,11 +17,13 @@ router.use('/bing', askBing);
router.use('/sydney', askSydney);
router.post('/', async (req, res) => {
- let { model, text, parentMessageId, conversationId, chatGptLabel, promptPrefix } = req.body;
+ let { model, text, parentMessageId, conversationId: oldConversationId , chatGptLabel, promptPrefix } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
+ const conversationId = oldConversationId || crypto.randomUUID();
+
const userMessageId = crypto.randomUUID();
const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'
let userMessage = {
@@ -36,8 +38,6 @@ router.post('/', async (req, res) => {
console.log('ask log', {
model,
...userMessage,
- parentMessageId: userParentMessageId,
- conversationId,
chatGptLabel,
promptPrefix
});
@@ -61,12 +61,6 @@ router.post('/', async (req, res) => {
}
}
- // if (messageId) {
- // // existing conversation
- // await saveMessage(userMessage);
- // await deleteMessagesSince(userMessage);
- // } else {}
-
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
@@ -75,6 +69,10 @@ router.post('/', async (req, res) => {
'X-Accel-Buffering': 'no'
});
+ await saveMessage(userMessage);
+ await saveConvo({ ...userMessage, model, chatGptLabel, promptPrefix });
+ sendMessage(res, { message: userMessage, created: true });
+
try {
let i = 0;
let tokens = '';
@@ -165,7 +163,7 @@ router.post('/', async (req, res) => {
res.end();
} catch (error) {
console.log(error);
- await deleteMessages({ messageId: userMessageId });
+ // await deleteMessages({ messageId: userMessageId });
handleError(res, error.message);
}
});
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index 08c086597d..cffb669ba8 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -7,10 +7,12 @@ const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { model, text, parentMessageId, conversationId, ...convo } = req.body;
+ const { model, text, parentMessageId, conversationId: oldConversationId, ...convo } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
+
+ const conversationId = oldConversationId || crypto.randomUUID();
const userMessageId = messageId;
const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'
@@ -26,17 +28,9 @@ router.post('/', async (req, res) => {
console.log('ask log', {
model,
...userMessage,
- parentMessageId: userParentMessageId,
- conversationId,
...convo
});
- // if (messageId) {
- // // existing conversation
- // await saveMessage(userMessage);
- // await deleteMessagesSince(userMessage);
- // } else {}
-
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
@@ -45,6 +39,10 @@ router.post('/', async (req, res) => {
'X-Accel-Buffering': 'no'
});
+ await saveMessage(userMessage);
+ await saveConvo({ ...userMessage, model, chatGptLabel, promptPrefix });
+ sendMessage(res, { message: userMessage, created: true });
+
try {
let tokens = '';
const progressCallback = async (partial) => {
@@ -107,7 +105,7 @@ router.post('/', async (req, res) => {
res.end();
} catch (error) {
console.log(error);
- await deleteMessages({ messageId: userMessageId });
+ // await deleteMessages({ messageId: userMessageId });
handleError(res, error.message);
}
});
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 27284ec526..a4f1b2b866 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -7,10 +7,12 @@ const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { model, text, parentMessageId, conversationId, ...convo } = req.body;
+ const { model, text, parentMessageId, conversationId: oldConversationId, ...convo } = req.body;
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
+
+ const conversationId = oldConversationId || crypto.randomUUID();
const userMessageId = messageId;
const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'
@@ -27,17 +29,9 @@ router.post('/', async (req, res) => {
console.log('ask log', {
model,
...userMessage,
- parentMessageId: userParentMessageId,
- conversationId,
...convo
});
- // if (messageId) {
- // // existing conversation
- // await saveMessage(userMessage);
- // await deleteMessagesSince(userMessage);
- // } else {}
-
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
@@ -46,6 +40,10 @@ router.post('/', async (req, res) => {
'X-Accel-Buffering': 'no'
});
+ await saveMessage(userMessage);
+ await saveConvo({ ...userMessage, model, chatGptLabel, promptPrefix });
+ sendMessage(res, { message: userMessage, created: true });
+
try {
let tokens = '';
const progressCallback = async (partial) => {
@@ -117,7 +115,7 @@ router.post('/', async (req, res) => {
res.end();
} catch (error) {
console.log(error);
- await deleteMessages({ messageId: userMessageId });
+ // await deleteMessages({ messageId: userMessageId });
handleError(res, error.message);
}
});
diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx
index 34e384f624..30df587da9 100644
--- a/client/src/components/Conversations/Conversation.jsx
+++ b/client/src/components/Conversations/Conversation.jsx
@@ -11,6 +11,7 @@ import ConvoIcon from '../svg/ConvoIcon';
export default function Conversation({
id,
+ model,
parentMessageId,
conversationId,
title = 'New conversation',
@@ -76,12 +77,12 @@ export default function Conversation({
if (chatGptLabel) {
dispatch(setModel('chatgptCustom'));
} else {
- dispatch(setModel(data[1].sender));
+ dispatch(setModel(model));
}
- if (modelMap[data[1].sender.toLowerCase()]) {
- console.log('sender', data[1].sender);
- dispatch(setCustomModel(data[1].sender.toLowerCase()));
+ if (modelMap[model.toLowerCase()]) {
+ console.log('sender', model);
+ dispatch(setCustomModel(model.toLowerCase()));
} else {
dispatch(setCustomModel(null));
}
diff --git a/client/src/components/Conversations/index.jsx b/client/src/components/Conversations/index.jsx
index 6fcac5e27f..d3ab1ffd0b 100644
--- a/client/src/components/Conversations/index.jsx
+++ b/client/src/components/Conversations/index.jsx
@@ -26,6 +26,7 @@ export default function Conversations({ conversations, conversationId, showMore
{
- const { messages, currentMsg, message, sender } = currentState;
+ const messageHandler = (data, currentState, currentMsg) => {
+ const { messages, _currentMsg, message, sender } = currentState;
+
dispatch(setMessages([...messages, currentMsg, { sender, text: data }]));
};
- const convoHandler = (data, currentState) => {
+ const createdHandler = (data, currentState, currentMsg) => {
+ const { conversationId } = currentMsg;
+ dispatch(
+ setConversation({
+ conversationId,
+ })
+ );
+ };
+
+ const convoHandler = (data, currentState, currentMsg) => {
const { requestMessage, responseMessage } = data;
- const { messages, currentMsg, message, isCustomModel, sender } =
+ const { messages, _currentMsg, message, isCustomModel, sender } =
currentState;
const { model, chatGptLabel, promptPrefix } = message;
dispatch(
@@ -210,6 +220,7 @@ export default function TextChat({ messages }) {
}
const currentState = submission;
+ let currentMsg = currentState.currentMsg;
const { server, payload } = createPayload(submission);
const onMessage = (e) => {
if (stopStream) {
@@ -223,12 +234,15 @@ export default function TextChat({ messages }) {
// }
if (data.final) {
- convoHandler(data, currentState);
+ convoHandler(data, currentState, currentMsg);
console.log('final', data);
+ } if (data.created) {
+ currentMsg = data.message;
+ createdHandler(data, currentState, currentMsg);
} else {
let text = data.text || data.response;
if (data.message) {
- messageHandler(text, currentState);
+ messageHandler(text, currentState, currentMsg);
}
// console.log('dataStream', data);
}
From 9f8e9cb091022a1e11bceb86ec0fd93e1fe58f26 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 14:04:47 +0800
Subject: [PATCH 09/47] feat: gen title by sperate api call feat:
fix: rename of convo should based on real request.
---
api/server/routes/ask.js | 3 ++-
api/server/routes/askBing.js | 5 ++--
api/server/routes/askSydney.js | 5 ++--
api/server/routes/convos.js | 26 ++++++++++++++++++
.../components/Conversations/Conversation.jsx | 11 +++++---
client/src/components/Main/TextChat.jsx | 27 ++++++++++---------
client/src/components/Nav/index.jsx | 5 ++--
client/src/store/convoSlice.js | 11 ++++----
8 files changed, 65 insertions(+), 28 deletions(-)
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 9be3fe7014..d21f8be263 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -10,7 +10,7 @@ const {
customClient,
detectCode
} = require('../../app/');
-const { getConvo, saveMessage, deleteMessagesSince, deleteMessages, saveConvo } = require('../../models');
+const { getConvo, saveMessage, getConvoTitle, saveConvo } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
router.use('/bing', askBing);
@@ -156,6 +156,7 @@ router.post('/', async (req, res) => {
await saveMessage(gptResponse);
await saveConvo(gptResponse);
sendMessage(res, {
+ title: await getConvoTitle(conversationId),
final: true,
requestMessage: userMessage,
responseMessage: gptResponse
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index cffb669ba8..f2722bb248 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -2,7 +2,7 @@ const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
-const { saveMessage, deleteMessages, deleteMessagesSince, saveConvo } = require('../../models');
+const { saveMessage, getConvoTitle, saveConvo } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
@@ -11,7 +11,7 @@ router.post('/', async (req, res) => {
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
-
+
const conversationId = oldConversationId || crypto.randomUUID();
const userMessageId = messageId;
@@ -98,6 +98,7 @@ router.post('/', async (req, res) => {
await saveMessage(response);
await saveConvo(response);
sendMessage(res, {
+ title: await getConvoTitle(conversationId),
final: true,
requestMessage: userMessage,
responseMessage: gptResponse
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index a4f1b2b866..a90dd39555 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -2,7 +2,7 @@ const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askSydney } = require('../../app/');
-const { saveMessage, deleteMessages, saveConvo, deleteMessagesSince, getConvoTitle } = require('../../models');
+const { saveMessage, saveConvo, getConvoTitle } = require('../../models');
const { handleError, sendMessage } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
@@ -11,7 +11,7 @@ router.post('/', async (req, res) => {
if (text.length === 0) {
return handleError(res, 'Prompt empty or too short');
}
-
+
const conversationId = oldConversationId || crypto.randomUUID();
const userMessageId = messageId;
@@ -108,6 +108,7 @@ router.post('/', async (req, res) => {
await saveMessage(response);
await saveConvo(response);
sendMessage(res, {
+ title: await getConvoTitle(conversationId),
final: true,
requestMessage: userMessage,
responseMessage: gptResponse
diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js
index 4b9320873f..a82242db4a 100644
--- a/api/server/routes/convos.js
+++ b/api/server/routes/convos.js
@@ -1,12 +1,38 @@
const express = require('express');
const router = express.Router();
+const { titleConvo } = require('../../app/');
+const { getConvo, saveConvo, getConvoTitle } = require('../../models');
const { getConvos, deleteConvos, updateConvo } = require('../../models/Conversation');
+const { getMessages } = require('../../models/Message');
router.get('/', async (req, res) => {
const pageNumber = req.query.pageNumber || 1;
res.status(200).send(await getConvos(pageNumber));
});
+router.post('/gen_title', async (req, res) => {
+ const { conversationId } = req.body.arg;
+
+ const convo = await getConvo(conversationId)
+ const firstMessage = (await getMessages({ conversationId }))[0]
+ const secondMessage = (await getMessages({ conversationId }))[1]
+
+ const title = convo.jailbreakConversationId
+ ? await getConvoTitle(conversationId)
+ : await titleConvo({
+ model: convo?.model,
+ message: firstMessage?.text,
+ response: JSON.stringify(secondMessage?.text || '')
+ });
+
+ await saveConvo({
+ conversationId,
+ title
+ })
+
+ res.status(200).send(title);
+});
+
router.post('/clear', async (req, res) => {
let filter = {};
const { conversationId } = req.body.arg;
diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx
index 30df587da9..4d90ecb2cc 100644
--- a/client/src/components/Conversations/Conversation.jsx
+++ b/client/src/components/Conversations/Conversation.jsx
@@ -8,13 +8,14 @@ import { setMessages, setEmptyMessage } from '~/store/messageSlice';
import { setText } from '~/store/textSlice';
import manualSWR from '~/utils/fetchers';
import ConvoIcon from '../svg/ConvoIcon';
+import { refreshConversation } from '../../store/convoSlice';
export default function Conversation({
id,
model,
parentMessageId,
conversationId,
- title = 'New conversation',
+ title,
chatGptLabel = null,
promptPrefix = null,
bingData,
@@ -95,6 +96,7 @@ export default function Conversation({
const renameHandler = (e) => {
e.preventDefault();
+ setTitleInput(title);
setRenaming(true);
setTimeout(() => {
inputRef.current.focus();
@@ -112,7 +114,10 @@ export default function Conversation({
if (titleInput === title) {
return;
}
- rename.trigger({ conversationId, title: titleInput });
+ rename.trigger({ conversationId, title: titleInput })
+ .then(() => {
+ dispatch(refreshConversation())
+ });
};
const handleKeyDown = (e) => {
@@ -149,7 +154,7 @@ export default function Conversation({
onKeyDown={handleKeyDown}
/>
) : (
- titleInput
+ title
)}
{conversationId === id ? (
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index 69f4b65898..b541c9a51f 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -1,5 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
import { SSE } from '~/utils/sse';
+import axios from 'axios';
import SubmitButton from './SubmitButton';
import Regenerate from './Regenerate';
import ModelMenu from '../Models/ModelMenu';
@@ -7,10 +8,11 @@ import Footer from './Footer';
import TextareaAutosize from 'react-textarea-autosize';
import handleSubmit from '~/utils/handleSubmit';
import { useSelector, useDispatch } from 'react-redux';
-import { setConversation, setError } from '~/store/convoSlice';
+import { setConversation, setError, refreshConversation } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
import { setSubmitState, setSubmission } from '~/store/submitSlice';
import { setText } from '~/store/textSlice';
+import manualSWR from '~/utils/fetchers';
export default function TextChat({ messages }) {
const [errorMessage, setErrorMessage] = useState('');
@@ -22,6 +24,7 @@ export default function TextChat({ messages }) {
useSelector((state) => state.submit);
const { text } = useSelector((state) => state.text);
const { error } = convo;
+ const genTitle = manualSWR(`/api/convos/gen_title`, 'post');
// auto focus to input, when enter a conversation.
useEffect(() => {
@@ -45,26 +48,24 @@ export default function TextChat({ messages }) {
const convoHandler = (data, currentState, currentMsg) => {
const { requestMessage, responseMessage } = data;
+ const { conversationId } = currentMsg;
const { messages, _currentMsg, message, isCustomModel, sender } =
currentState;
const { model, chatGptLabel, promptPrefix } = message;
dispatch(
- setMessages([...messages,
- {
- ...requestMessage,
- // messageId: data?.parentMessageId,
- },
- {
- ...responseMessage,
- // sender,
- // text: data.text || data.response,
- }
- ])
+ setMessages([...messages, requestMessage, responseMessage,])
);
const isBing = model === 'bingai' || model === 'sydney';
- // if (!message.messageId)
+ if (requestMessage.parentMessageId == '00000000-0000-0000-0000-000000000000') {
+ genTitle.trigger({ conversationId }).then((ret) => {
+ const title = ret?.data
+
+ if (title)
+ dispatch(refreshConversation());
+ })
+ }
if (!isBing && convo.conversationId === null && convo.parentMessageId === null) {
const { title } = data;
diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx
index 7d548429d9..3029686cf8 100644
--- a/client/src/components/Nav/index.jsx
+++ b/client/src/components/Nav/index.jsx
@@ -11,7 +11,7 @@ import { incrementPage, setConvos } from '~/store/convoSlice';
export default function Nav({ navVisible, setNavVisible }) {
const dispatch = useDispatch();
const [isHovering, setIsHovering] = useState(false);
- const { conversationId, convos, pageNumber } = useSelector((state) => state.convo);
+ const { conversationId, convos, pageNumber, refreshConvoHint } = useSelector((state) => state.convo);
const onSuccess = (data) => {
dispatch(setConvos(data));
};
@@ -20,6 +20,7 @@ export default function Nav({ navVisible, setNavVisible }) {
`/api/convos?pageNumber=${pageNumber}`,
onSuccess
);
+
const containerRef = useRef(null);
const scrollPositionRef = useRef(null);
@@ -35,7 +36,7 @@ export default function Nav({ navVisible, setNavVisible }) {
}
};
- useDidMountEffect(() => mutate(), [conversationId]);
+ useDidMountEffect(() => mutate(), [conversationId, refreshConvoHint]);
useEffect(() => {
const container = containerRef.current;
diff --git a/client/src/store/convoSlice.js b/client/src/store/convoSlice.js
index 4e55fa2b94..258de84e70 100644
--- a/client/src/store/convoSlice.js
+++ b/client/src/store/convoSlice.js
@@ -13,6 +13,7 @@ const initialState = {
promptPrefix: null,
convosLoading: false,
pageNumber: 1,
+ refreshConvoHint: 0,
convos: []
};
@@ -20,6 +21,9 @@ const currentSlice = createSlice({
name: 'convo',
initialState,
reducers: {
+ refreshConversation: (state, action) => {
+ state.refreshConvoHint = state.refreshConvoHint + 1;
+ },
setConversation: (state, action) => {
return { ...state, ...action.payload };
},
@@ -44,10 +48,7 @@ const currentSlice = createSlice({
state.pageNumber = 1;
},
setConvos: (state, action) => {
- const newConvos = action.payload.filter((convo) => {
- return !state.convos.some((c) => c.conversationId === convo.conversationId);
- });
- state.convos = [...state.convos, ...newConvos].sort(
+ state.convos = action.payload.sort(
(a, b) => new Date(b.createdAt) - new Date(a.createdAt)
);
},
@@ -60,7 +61,7 @@ const currentSlice = createSlice({
}
});
-export const { setConversation, setConvos, setNewConvo, setError, incrementPage, removeConvo, removeAll } =
+export const { refreshConversation, setConversation, setConvos, setNewConvo, setError, incrementPage, removeConvo, removeAll } =
currentSlice.actions;
export default currentSlice.reducer;
From 0e98cb4206d99807e0f2185069a18286b380226e Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 14:05:08 +0800
Subject: [PATCH 10/47] fix: in mobile view, resubmit edit button should always
visible
---
client/src/components/Messages/HoverButtons.jsx | 2 +-
client/src/mobile.css | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/client/src/components/Messages/HoverButtons.jsx b/client/src/components/Messages/HoverButtons.jsx
index 35c5f14821..f24027118e 100644
--- a/client/src/components/Messages/HoverButtons.jsx
+++ b/client/src/components/Messages/HoverButtons.jsx
@@ -6,7 +6,7 @@ export default function HoverButtons({ user, onClick }) {
return (
{user && (
-
+
{/* */}
diff --git a/client/src/mobile.css b/client/src/mobile.css
index 78fff342c5..e0e3988410 100644
--- a/client/src/mobile.css
+++ b/client/src/mobile.css
@@ -30,6 +30,10 @@
margin-left: 20px;
}
+ .resubmit-edit-button {
+ display: block;
+ }
+
.nav {
position: fixed;
z-index: 999;
From 90dc171b34ed27ac43283c902cd07f5f5b0b2b40 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 14:08:42 +0800
Subject: [PATCH 11/47] test: generate of title shouldn't be mislead by answer
---
api/server/routes/convos.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js
index a82242db4a..5862919f17 100644
--- a/api/server/routes/convos.js
+++ b/api/server/routes/convos.js
@@ -22,7 +22,7 @@ router.post('/gen_title', async (req, res) => {
: await titleConvo({
model: convo?.model,
message: firstMessage?.text,
- response: JSON.stringify(secondMessage?.text || '')
+ // response: JSON.stringify(secondMessage?.text || '')
});
await saveConvo({
From 4a39965b22d1ab0feab0baf74e274ee60dfcd460 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 15:55:18 +0800
Subject: [PATCH 12/47] fix: add proxy to titleConvo
---
api/app/titleConvo.js | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/api/app/titleConvo.js b/api/app/titleConvo.js
index e0461eabaf..52e35b841a 100644
--- a/api/app/titleConvo.js
+++ b/api/app/titleConvo.js
@@ -1,5 +1,21 @@
const { Configuration, OpenAIApi } = require('openai');
+const proxyEnvToAxiosProxy = (proxyString) => {
+ if (!proxyString) return null;
+
+ const regex = /^([^:]+):\/\/(?:([^:@]*):?([^:@]*)@)?([^:]+)(?::(\d+))?/;
+ const [, protocol, username, password, host, port] = proxyString.match(regex);
+ const proxyConfig = {
+ protocol,
+ host,
+ port: port ? parseInt(port) : undefined,
+ auth: username && password ? { username, password } : undefined
+ };
+
+ return proxyConfig
+}
+console.log(proxyEnvToAxiosProxy(process.env.PROXY || null))
+
const titleConvo = async ({ message, response, model }) => {
const configuration = new Configuration({
apiKey: process.env.OPENAI_KEY
@@ -15,7 +31,7 @@ const titleConvo = async ({ message, response, model }) => {
},
{ role: 'user', content: `In 5 words or less, summarize the conversation below with a title in title case using the language the user writes in. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${message}"\n\n${model}: "${response}"\n\nTitle: ` },
]
- });
+ }, { proxy: proxyEnvToAxiosProxy(process.env.PROXY || null) });
//eslint-disable-next-line
return completion.data.choices[0].message.content.replace(/["\.]/g, '');
From a4d5f6a3f2dcc78478129f1f9368b1e4ce982e1c Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 21:44:30 +0800
Subject: [PATCH 13/47] feat: fully multipath and resubmit
---
api/server/routes/convos.js | 2 +-
client/src/components/Main/TextChat.jsx | 14 +-
client/src/components/Messages/Message.jsx | 191 ++++++++++++------
.../src/components/Messages/SiblingSwitch.jsx | 26 +++
client/src/components/Messages/index.jsx | 53 +++--
5 files changed, 197 insertions(+), 89 deletions(-)
create mode 100644 client/src/components/Messages/SiblingSwitch.jsx
diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js
index 5862919f17..a82242db4a 100644
--- a/api/server/routes/convos.js
+++ b/api/server/routes/convos.js
@@ -22,7 +22,7 @@ router.post('/gen_title', async (req, res) => {
: await titleConvo({
model: convo?.model,
message: firstMessage?.text,
- // response: JSON.stringify(secondMessage?.text || '')
+ response: JSON.stringify(secondMessage?.text || '')
});
await saveConvo({
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index b541c9a51f..d1a4ed511b 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -34,7 +34,7 @@ export default function TextChat({ messages }) {
const messageHandler = (data, currentState, currentMsg) => {
const { messages, _currentMsg, message, sender } = currentState;
- dispatch(setMessages([...messages, currentMsg, { sender, text: data }]));
+ dispatch(setMessages([...messages, currentMsg, { sender, text: data, parentMessageId: currentMsg?.messageId, messageId: currentMsg?.messageId + '_' }]));
};
const createdHandler = (data, currentState, currentMsg) => {
@@ -152,11 +152,13 @@ export default function TextChat({ messages }) {
return;
}
+ // this is not a real messageId, it is used as placeholder before real messageId returned
+ const fakeMessageId = crypto.randomUUID();
const isCustomModel = model === 'chatgptCustom' || !initial[model];
const message = text.trim();
- const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true };
+ const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId: convo.parentMessageId || '00000000-0000-0000-0000-000000000000', messageId: fakeMessageId };
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
- const initialResponse = { sender, text: '' };
+ const initialResponse = { sender, text: '', parentMessageId: fakeMessageId };
dispatch(setSubmitState(true));
dispatch(setMessages([...messages, currentMsg, initialResponse]));
@@ -166,9 +168,7 @@ export default function TextChat({ messages }) {
convo,
isCustomModel,
message: {
- sender: 'User',
- text: message,
- isCreatedByUser: true,
+ ...currentMsg,
model,
chatGptLabel,
promptPrefix,
@@ -193,7 +193,7 @@ export default function TextChat({ messages }) {
payload = {
...payload,
conversationId: convo.conversationId,
- parentMessageId: convo.parentMessageId
+ parentMessageId: convo.parentMessageId || '00000000-0000-0000-0000-000000000000'
};
}
diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx
index 2d6df0360e..1eff8c7d98 100644
--- a/client/src/components/Messages/Message.jsx
+++ b/client/src/components/Messages/Message.jsx
@@ -4,20 +4,59 @@ import { useSelector, useDispatch } from 'react-redux';
import GPTIcon from '../svg/GPTIcon';
import BingIcon from '../svg/BingIcon';
import HoverButtons from './HoverButtons';
+import SiblingSwitch from './SiblingSwitch';
import Spinner from '../svg/Spinner';
import { setError } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
import { setSubmitState, setSubmission } from '~/store/submitSlice';
import { setText } from '~/store/textSlice';
+import { setConversation } from '../../store/convoSlice';
+
+const MultiMessage = ({
+ messageList,
+ messages,
+ scrollToBottom,
+ currentEditId,
+ setCurrentEditId
+}) => {
+ const [siblingIdx, setSiblingIdx] = useState(0)
+
+ const setSiblingIdxRev = (value) => {
+ setSiblingIdx(messageList?.length - value - 1)
+ }
+
+ if (!messageList?.length) return null;
+
+ if (siblingIdx >= messageList?.length) {
+ setSiblingIdx(0)
+ return null
+ }
+
+ return
+}
+
+export { MultiMessage };
export default function Message({
message,
messages,
- last = false,
scrollToBottom,
- edit,
- currentEditIdx,
- enterEdit
+ currentEditId,
+ setCurrentEditId,
+ siblingIdx,
+ siblingCount,
+ setSiblingIdx
}) {
const { isSubmitting, model, chatGptLabel, promptPrefix } = useSelector((state) => state.submit);
const [abortScroll, setAbort] = useState(false);
@@ -26,6 +65,9 @@ export default function Message({
const convo = useSelector((state) => state.convo);
const { initial } = useSelector((state) => state.models);
const { error: convoError } = convo;
+ const last = !message?.children?.length
+
+ const edit = message.messageId == currentEditId;
const dispatch = useDispatch();
@@ -37,11 +79,18 @@ export default function Message({
scrollToBottom();
}
}, [isSubmitting, text, blinker, scrollToBottom, abortScroll]);
-
+
+ useEffect(() => {
+ if (last)
+ dispatch(setConversation({parentMessageId: message?.messageId}))
+ }, [last, ])
+
if (sender === '') {
return ;
}
+ const enterEdit = (cancel) => setCurrentEditId(cancel?-1:message.messageId)
+
const handleWheel = () => {
if (blinker) {
setAbort(true);
@@ -105,97 +154,109 @@ export default function Message({
return;
}
+ // this is not a real messageId, it is used as placeholder before real messageId returned
+ const fakeMessageId = crypto.randomUUID();
const isCustomModel = model === 'chatgptCustom' || !initial[model];
- const currentMsg = { ...message, sender: 'User', text: text.trim(), current: true, isCreatedByUser: true };
- console.log(model)
+ const currentMsg = { ...message, sender: 'User', text: text.trim(), current: true, isCreatedByUser: true, messageId: fakeMessageId };
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
- const initialResponse = { sender, text: '' };
+ const initialResponse = { sender, text: '', parentMessageId: fakeMessageId };
dispatch(setSubmitState(true));
- dispatch(setMessages([...messages.slice(0, currentEditIdx), currentMsg, initialResponse]));
+ dispatch(setMessages([...messages, currentMsg, initialResponse]));
dispatch(setText(''));
const submission = {
isCustomModel,
message: {
- ...message,
- text: text.trim(),
+ ...currentMsg,
model,
chatGptLabel,
promptPrefix,
},
- messages: messages.slice(0, currentEditIdx),
+ messages: messages,
currentMsg,
initialResponse,
sender,
};
- console.log('User Input:', message);
+ console.log('User Input:', currentMsg?.text);
// handleSubmit(submission);
dispatch(setSubmission(submission));
+ setSiblingIdx(siblingCount - 1)
enterEdit(true);
};
return (
-
-
-
- {typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? (
- {icon}
- ) : (
- icon
- )}
-
-
-
- {error ? (
-
- ) :
- edit ? (
-
- {/*
*/}
-
-
+ <>
+
+
+
+
+ {typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? (
+ {icon}
+ ) : (
+ icon
+ )}
+
+
+
+
+ {error ? (
+
+
{text}
-
-
- Save & Submit
-
- enterEdit(true)}
- >
- Cancel
-
-
- ) : (
-
- {/*
*/}
-
- {!isCreatedByUser ? wrapText(text) : text}
- {blinker &&
█}
+ ) :
+ edit ? (
+
+ {/*
*/}
+
+
+ {text}
+
+
+
+ Save & Submit
+
+ enterEdit(true)}
+ >
+ Cancel
+
+
-
- )}
+ ) : (
+
+ {/*
*/}
+
+ {!isCreatedByUser ? wrapText(text) : text}
+ {blinker && █}
+
+
+ )}
+
+
enterEdit()}/>
-
enterEdit()}/>
-
+
+ >
);
}
diff --git a/client/src/components/Messages/SiblingSwitch.jsx b/client/src/components/Messages/SiblingSwitch.jsx
new file mode 100644
index 0000000000..b5586d6a8c
--- /dev/null
+++ b/client/src/components/Messages/SiblingSwitch.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+
+export default function SiblingSwitch({
+ siblingIdx,
+ siblingCount,
+ setSiblingIdx
+}) {
+ const previous = () => {
+ setSiblingIdx(siblingIdx - 1);
+ }
+
+ const next = () => {
+ setSiblingIdx(siblingIdx + 1);
+ }
+ return siblingCount > 1 ? (
+
+
+
+
+
{siblingIdx + 1}/{siblingCount}
+
+
+
+
+ ):null;
+}
diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx
index 7d9bd69d44..5d191e61cf 100644
--- a/client/src/components/Messages/index.jsx
+++ b/client/src/components/Messages/index.jsx
@@ -1,15 +1,17 @@
-import React, { useEffect, useState, useRef } from 'react';
+import React, { useEffect, useState, useRef, useMemo } from 'react';
import { CSSTransition } from 'react-transition-group';
import ScrollToBottom from './ScrollToBottom';
-import Message from './Message';
+import { MultiMessage } from './Message';
+import Conversation from '../Conversations/Conversation';
+import { useSelector } from 'react-redux';
const Messages = ({ messages }) => {
- const [currentEditIdx, setCurrentEditIdx] = useState(-1)
+ const [currentEditId, setCurrentEditId] = useState(-1)
+ const { conversationId } = useSelector((state) => state.convo);
const [showScrollButton, setShowScrollButton] = useState(false);
const scrollableRef = useRef(null);
const messagesEndRef = useRef(null);
-
useEffect(() => {
const timeoutId = setTimeout(() => {
const scrollable = scrollableRef.current;
@@ -21,6 +23,29 @@ const Messages = ({ messages }) => {
clearTimeout(timeoutId);
};
}, [messages]);
+
+ const messageTree = useMemo(() => buildTree(messages), [messages, ]);
+
+ function buildTree(messages) {
+ let messageMap = {};
+ let rootMessages = [];
+
+ // Traverse the messages array and store each element in messageMap.
+ messages.forEach(message => {
+ messageMap[message.messageId] = {...message, children: []};
+
+ if (message.parentMessageId === "00000000-0000-0000-0000-000000000000") {
+ rootMessages.push(messageMap[message.messageId]);
+ } else {
+ const parentMessage = messageMap[message.parentMessageId];
+ if (parentMessage) {
+ parentMessage.children.push(messageMap[message.messageId]);
+ }
+ }
+ });
+
+ return rootMessages;
+ }
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
@@ -59,18 +84,14 @@ const Messages = ({ messages }) => {
{/*
*/}
- {messages.map((message, i) => (
-
setCurrentEditIdx(cancel?-1:i)}
- />
- ))}
+
Date: Mon, 13 Mar 2023 22:20:50 +0800
Subject: [PATCH 14/47] fix: loading and send button, mobile style feat:
sibling switch, mobile style fix: only the real submitting message will blink
feat: drop the text version username, use a similar square. (or it will mass
up the sibling switch)
---
client/src/components/Main/SubmitButton.jsx | 4 ++--
client/src/components/Main/TextChat.jsx | 4 ++--
client/src/components/Messages/Message.jsx | 17 +++++++++++++----
.../src/components/Messages/SiblingSwitch.jsx | 2 +-
client/src/mobile.css | 8 ++++++++
5 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/client/src/components/Main/SubmitButton.jsx b/client/src/components/Main/SubmitButton.jsx
index 802d1a88c3..ed570f5c76 100644
--- a/client/src/components/Main/SubmitButton.jsx
+++ b/client/src/components/Main/SubmitButton.jsx
@@ -10,7 +10,7 @@ export default function SubmitButton({ submitMessage }) {
if (isSubmitting) {
return (
-
+
·
·
@@ -23,7 +23,7 @@ export default function SubmitButton({ submitMessage }) {
From 71fc86b9a6f5e2250f5ca557a9f793c6c1879716 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Wed, 15 Mar 2023 02:43:21 +0800
Subject: [PATCH 27/47] fix: buildTree should store parent-not-exist message as
root. rather than dropping them.
---
client/src/components/Messages/index.jsx | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx
index 5d191e61cf..85c671d691 100644
--- a/client/src/components/Messages/index.jsx
+++ b/client/src/components/Messages/index.jsx
@@ -34,14 +34,11 @@ const Messages = ({ messages }) => {
messages.forEach(message => {
messageMap[message.messageId] = {...message, children: []};
- if (message.parentMessageId === "00000000-0000-0000-0000-000000000000") {
+ const parentMessage = messageMap[message.parentMessageId];
+ if (parentMessage)
+ parentMessage.children.push(messageMap[message.messageId]);
+ else
rootMessages.push(messageMap[message.messageId]);
- } else {
- const parentMessage = messageMap[message.parentMessageId];
- if (parentMessage) {
- parentMessage.children.push(messageMap[message.messageId]);
- }
- }
});
return rootMessages;
From 9a17e94f8fd70c617b33e2ecd5af13b47636ca8c Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Tue, 14 Mar 2023 14:51:26 -0400
Subject: [PATCH 28/47] fix: refactor migration and sort old convos correctly
---
api/models/Conversation.js | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/api/models/Conversation.js b/api/models/Conversation.js
index a32bc3e7a4..c3b23d85fb 100644
--- a/api/models/Conversation.js
+++ b/api/models/Conversation.js
@@ -97,7 +97,7 @@ module.exports = {
// const limit = pageNumber * pageSize;
const conversations = await Conversation.find({})
- .sort({ createdAt: -1 })
+ .sort({ createdAt: -1, created: -1 })
.skip(skip)
// .limit(limit)
.limit(pageSize)
@@ -129,7 +129,7 @@ module.exports = {
const conversations = await Conversation.find({ model: null }).exec();
if (!conversations || conversations.length === 0)
- return { message: 'No conversations to migrate' };
+ return { message: '[Migrate] No conversations to migrate' };
for (let convo of conversations) {
const messages = await getMessages({
@@ -137,22 +137,20 @@ module.exports = {
messageId: { $exists: false }
});
- const promises = [];
let model;
let oldId;
+ const promises = [];
messages.forEach((message, i) => {
const msgObj = message.toObject();
const newId = msgObj.id;
if (i === 0) {
message.parentMessageId = '00000000-0000-0000-0000-000000000000';
- oldId = newId;
} else {
message.parentMessageId = oldId;
- oldId = newId;
}
+ oldId = newId;
message.messageId = newId;
- message.createdAt = message.created;
if (message.sender.toLowerCase() !== 'user' && !model) {
model = message.sender.toLowerCase();
}
@@ -166,7 +164,7 @@ module.exports = {
await Conversation.findOneAndUpdate(
{ conversationId: convo.conversationId },
- { model, createdAt: convo.created },
+ { model },
{ new: true }
).exec();
}
@@ -174,11 +172,11 @@ module.exports = {
try {
await mongoose.connection.db.collection('messages').dropIndex('id_1');
} catch (error) {
- console.log("Index doesn't exist or already dropped");
+ console.log("[Migrate] Index doesn't exist or already dropped");
}
} catch (error) {
console.log(error);
- return { message: 'Error migrating conversations' };
+ return { message: '[Migrate] Error migrating conversations' };
}
}
};
From 2e20b28c4dd5ec6a1bcf40cf992944e2e091ae75 Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Tue, 14 Mar 2023 15:42:59 -0400
Subject: [PATCH 29/47] chore: refactor progressCB to one place, fix sydney,
and sanitize html
---
api/app/bingai.js | 7 +-
api/app/chatgpt-browser.js | 7 +-
api/app/chatgpt-client.js | 7 +-
api/app/sydney.js | 6 +-
api/package-lock.json | 1 +
api/package.json | 1 +
api/server/routes/ask.js | 33 +---
api/server/routes/askBing.js | 13 +-
api/server/routes/askSydney.js | 13 +-
api/server/routes/handlers.js | 34 +++-
package-lock.json | 293 +++++++++++++++++++++++++++++++++
package.json | 5 +
12 files changed, 351 insertions(+), 69 deletions(-)
create mode 100644 package-lock.json
create mode 100644 package.json
diff --git a/api/app/bingai.js b/api/app/bingai.js
index 2ccbc2b356..dcf150d1cf 100644
--- a/api/app/bingai.js
+++ b/api/app/bingai.js
@@ -1,7 +1,7 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
-const askBing = async ({ text, progressCallback, convo }) => {
+const askBing = async ({ text, onProgress, convo }) => {
const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api'));
const bingAIClient = new BingAIClient({
@@ -14,10 +14,7 @@ const askBing = async ({ text, progressCallback, convo }) => {
proxy: process.env.PROXY || null,
});
- let options = {
- onProgress: async (partialRes) => await progressCallback(partialRes),
- };
-
+ let options = { onProgress };
if (convo) {
options = { ...options, ...convo };
}
diff --git a/api/app/chatgpt-browser.js b/api/app/chatgpt-browser.js
index 8a3c903641..a5bea22729 100644
--- a/api/app/chatgpt-browser.js
+++ b/api/app/chatgpt-browser.js
@@ -10,7 +10,7 @@ const clientOptions = {
proxy: process.env.PROXY || null,
};
-const browserClient = async ({ text, progressCallback, convo }) => {
+const browserClient = async ({ text, onProgress, convo }) => {
const { ChatGPTBrowserClient } = await import('@waylaidwanderer/chatgpt-api');
const store = {
@@ -18,10 +18,7 @@ const browserClient = async ({ text, progressCallback, convo }) => {
};
const client = new ChatGPTBrowserClient(clientOptions, store);
-
- let options = {
- onProgress: async (partialRes) => await progressCallback(partialRes)
- };
+ let options = { onProgress };
if (!!convo.parentMessageId && !!convo.conversationId) {
options = { ...options, ...convo };
diff --git a/api/app/chatgpt-client.js b/api/app/chatgpt-client.js
index afd31e0a81..350d1210ce 100644
--- a/api/app/chatgpt-client.js
+++ b/api/app/chatgpt-client.js
@@ -9,17 +9,14 @@ const clientOptions = {
debug: false
};
-const askClient = async ({ text, progressCallback, convo }) => {
+const askClient = async ({ text, onProgress, convo }) => {
const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
};
const client = new ChatGPTClient(process.env.OPENAI_KEY, clientOptions, store);
-
- let options = {
- onProgress: async (partialRes) => await progressCallback(partialRes)
- };
+ let options = { onProgress };
if (!!convo.parentMessageId && !!convo.conversationId) {
options = { ...options, ...convo };
diff --git a/api/app/sydney.js b/api/app/sydney.js
index fe47c74f57..3466f71c17 100644
--- a/api/app/sydney.js
+++ b/api/app/sydney.js
@@ -1,7 +1,7 @@
require('dotenv').config();
const { KeyvFile } = require('keyv-file');
-const askSydney = async ({ text, progressCallback, convo }) => {
+const askSydney = async ({ text, onProgress, convo }) => {
const { BingAIClient } = (await import('@waylaidwanderer/chatgpt-api'));
const sydneyClient = new BingAIClient({
@@ -15,10 +15,10 @@ const askSydney = async ({ text, progressCallback, convo }) => {
let options = {
jailbreakConversationId: true,
- onProgress: async (partialRes) => await progressCallback(partialRes),
+ onProgress,
};
- if (convo.parentMessageId) {
+ if (convo.jailbreakConversationId) {
options = { ...options, jailbreakConversationId: convo.jailbreakConversationId, parentMessageId: convo.parentMessageId };
}
diff --git a/api/package-lock.json b/api/package-lock.json
index 911fda953e..daa36e69e4 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -17,6 +17,7 @@
"express": "^4.18.2",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
+ "lodash": "^4.17.21",
"mongoose": "^6.9.0",
"openai": "^3.1.0"
},
diff --git a/api/package.json b/api/package.json
index 28bd6769d3..6dcf344ea0 100644
--- a/api/package.json
+++ b/api/package.json
@@ -27,6 +27,7 @@
"express": "^4.18.2",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
+ "lodash": "^4.17.21",
"mongoose": "^6.9.0",
"openai": "^3.1.0"
},
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index d12ee17b4e..5f94d7d4c3 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -11,7 +11,7 @@ const {
detectCode
} = require('../../app/');
const { getConvo, saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const { handleError, sendMessage } = require('./handlers');
+const { handleError, sendMessage, createOnProgress } = require('./handlers');
const { getMessages } = require('../../models/Message');
router.use('/bing', askBing);
@@ -138,37 +138,10 @@ const ask = async ({
sendMessage(res, { message: userMessage, created: true });
try {
- let i = 0;
- let tokens = '';
- const progressCallback = async (partial) => {
- if (i === 0 && typeof partial === 'object') {
- userMessage.conversationId = conversationId ? conversationId : partial.conversationId;
- await saveMessage(userMessage);
- sendMessage(res, { ...partial, parentMessageId: overrideParentMessageId || userMessageId, initial: true });
- i++;
- }
-
- if (typeof partial === 'object') {
- sendMessage(res, { ...partial, parentMessageId: overrideParentMessageId || userMessageId, message: true });
- } else {
- tokens += partial === text ? '' : partial;
- if (tokens.match(/^\n/)) {
- tokens = tokens.replace(/^\n/, '');
- }
-
- if (tokens.includes('[DONE]')) {
- tokens = tokens.replace('[DONE]', '');
- }
-
- // tokens = await detectCode(tokens);
- sendMessage(res, { text: tokens, message: true, initial: i === 0 ? true : false });
- i++;
- }
- };
-
+ const progressCallback = createOnProgress();
let gptResponse = await client({
text,
- progressCallback,
+ onProgress: progressCallback.call(null, model, {res, text }),
convo: {
parentMessageId: userParentMessageId,
conversationId,
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index 1871e96f40..add804a43e 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -3,7 +3,7 @@ const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
const { saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const { handleError, sendMessage } = require('./handlers');
+const { handleError, sendMessage, createOnProgress } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -68,17 +68,10 @@ const ask = async ({
sendMessage(res, { message: userMessage, created: true });
try {
- let tokens = '';
- const progressCallback = async (partial) => {
- tokens += partial === text ? '' : partial;
- // tokens = appendCode(tokens);
- tokens = citeText(tokens, true);
- sendMessage(res, { text: tokens, message: true, parentMessageId: overrideParentMessageId || userMessageId });
- };
-
+ const progressCallback = createOnProgress();
let response = await askBing({
text,
- progressCallback,
+ onProgress: progressCallback.call(null, model, {res, text, parentMessageId: overrideParentMessageId || userMessageId }),
convo: {
...convo,
parentMessageId: userParentMessageId,
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 3a663184b6..1f04ad4166 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -3,7 +3,7 @@ const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askSydney } = require('../../app/');
const { saveMessage, saveConvo, getConvoTitle } = require('../../models');
-const { handleError, sendMessage } = require('./handlers');
+const { handleError, sendMessage, createOnProgress } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -68,17 +68,10 @@ const ask = async ({
sendMessage(res, { message: userMessage, created: true });
try {
- let tokens = '';
- const progressCallback = async (partial) => {
- tokens += partial === text ? '' : partial;
- // tokens = appendCode(tokens);
- tokens = citeText(tokens, true);
- sendMessage(res, { text: tokens, message: true, parentMessageId: overrideParentMessageId || userMessageId });
- };
-
+ const progressCallback = createOnProgress();
let response = await askSydney({
text,
- progressCallback,
+ onProgress: progressCallback.call(null, model, {res, text, parentMessageId: overrideParentMessageId || userMessageId }),
convo: {
parentMessageId: userParentMessageId,
conversationId,
diff --git a/api/server/routes/handlers.js b/api/server/routes/handlers.js
index e727cacfa3..7672ef41a5 100644
--- a/api/server/routes/handlers.js
+++ b/api/server/routes/handlers.js
@@ -1,3 +1,7 @@
+const { citeText } = require('../../app/');
+const _ = require('lodash');
+const sanitizeHtml = require('sanitize-html');
+
const handleError = (res, message) => {
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
res.end();
@@ -10,4 +14,32 @@ const sendMessage = (res, message) => {
res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
};
-module.exports = { handleError, sendMessage };
+const createOnProgress = () => {
+ let i = 0;
+ let tokens = '';
+
+ const progressCallback = async (partial, { res, text, bing = false, ...rest }) => {
+ tokens += partial === text ? '' : partial;
+ tokens = tokens.trim();
+ tokens = tokens.replaceAll('[DONE]', '');
+ if (tokens.includes('```')) {
+ tokens = sanitizeHtml(tokens);
+ }
+
+ if (bing) {
+ tokens = citeText(tokens, true);
+ }
+
+ sendMessage(res, { text: tokens, message: true, initial: i === 0, ...rest });
+ i++;
+ };
+
+ const onProgress = (model, opts) => {
+ const bingModels = new Set(['bingai', 'sydney']);
+ return _.partialRight(progressCallback, { ...opts, bing: bingModels.has(model) });
+ };
+
+ return onProgress;
+};
+
+module.exports = { handleError, sendMessage, createOnProgress };
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000000..bd60daac40
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,293 @@
+{
+ "name": "chatgpt-clone",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "sanitize-html": "^2.10.0"
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
+ "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+ "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
+ "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "entities": "^4.3.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/parse-srcset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
+ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/postcss": {
+ "version": "8.4.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
+ "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/sanitize-html": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.10.0.tgz",
+ "integrity": "sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==",
+ "dependencies": {
+ "deepmerge": "^4.2.2",
+ "escape-string-regexp": "^4.0.0",
+ "htmlparser2": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "parse-srcset": "^1.0.2",
+ "postcss": "^8.3.11"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ }
+ },
+ "dependencies": {
+ "deepmerge": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
+ "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og=="
+ },
+ "dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+ },
+ "domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "requires": {
+ "domelementtype": "^2.3.0"
+ }
+ },
+ "domutils": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+ "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+ "requires": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.1"
+ }
+ },
+ "entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+ },
+ "htmlparser2": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
+ "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "entities": "^4.3.0"
+ }
+ },
+ "is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+ },
+ "nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+ },
+ "parse-srcset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
+ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "postcss": {
+ "version": "8.4.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
+ "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+ "requires": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "sanitize-html": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.10.0.tgz",
+ "integrity": "sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==",
+ "requires": {
+ "deepmerge": "^4.2.2",
+ "escape-string-regexp": "^4.0.0",
+ "htmlparser2": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "parse-srcset": "^1.0.2",
+ "postcss": "^8.3.11"
+ }
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000..4da6b6e8c4
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "sanitize-html": "^2.10.0"
+ }
+}
From 8289558d94b613ff58645dc74ca5f85b333979c9 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Wed, 15 Mar 2023 04:05:14 +0800
Subject: [PATCH 30/47] feat: pagination in nav
---
api/models/Conversation.js | 14 +++----
api/server/routes/convos.js | 4 +-
client/src/components/Conversations/index.jsx | 35 ++++++++++------
client/src/components/Nav/MobileNav.jsx | 6 +--
client/src/components/Nav/index.jsx | 41 ++++++++++++++-----
client/src/store/convoSlice.js | 19 ++++++---
client/src/style.css | 3 ++
client/src/utils/fetchers.js | 8 ++--
8 files changed, 84 insertions(+), 46 deletions(-)
diff --git a/api/models/Conversation.js b/api/models/Conversation.js
index a32bc3e7a4..6e3c646c64 100644
--- a/api/models/Conversation.js
+++ b/api/models/Conversation.js
@@ -91,19 +91,17 @@ module.exports = {
}
},
// getConvos: async () => await Conversation.find({}).sort({ createdAt: -1 }).exec(),
- getConvos: async (pageNumber = 1, pageSize = 12) => {
+ getConvosByPage: async (pageNumber = 1, pageSize = 12) => {
try {
- const skip = (pageNumber - 1) * pageSize;
- // const limit = pageNumber * pageSize;
-
- const conversations = await Conversation.find({})
+ const totalConvos = await Conversation.countDocuments();
+ const totalPages = Math.ceil(totalConvos / pageSize);
+ const convos = await Conversation.find()
.sort({ createdAt: -1 })
- .skip(skip)
- // .limit(limit)
+ .skip((pageNumber - 1) * pageSize)
.limit(pageSize)
.exec();
- return conversations;
+ return { conversations: convos, pages: totalPages, pageNumber, pageSize };
} catch (error) {
console.log(error);
return { message: 'Error getting conversations' };
diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js
index bfe3bc2d56..9a5f71cee3 100644
--- a/api/server/routes/convos.js
+++ b/api/server/routes/convos.js
@@ -2,12 +2,12 @@ const express = require('express');
const router = express.Router();
const { titleConvo } = require('../../app/');
const { getConvo, saveConvo, getConvoTitle } = require('../../models');
-const { getConvos, deleteConvos, updateConvo } = require('../../models/Conversation');
+const { getConvosByPage, deleteConvos, updateConvo } = require('../../models/Conversation');
const { getMessages } = require('../../models/Message');
router.get('/', async (req, res) => {
const pageNumber = req.query.pageNumber || 1;
- res.status(200).send(await getConvos(pageNumber));
+ res.status(200).send(await getConvosByPage(pageNumber));
});
router.post('/gen_title', async (req, res) => {
diff --git a/client/src/components/Conversations/index.jsx b/client/src/components/Conversations/index.jsx
index d3ab1ffd0b..4142cb8ed1 100644
--- a/client/src/components/Conversations/index.jsx
+++ b/client/src/components/Conversations/index.jsx
@@ -1,10 +1,10 @@
import React from 'react';
import Conversation from './Conversation';
-export default function Conversations({ conversations, conversationId, showMore }) {
- const clickHandler = async (e) => {
+export default function Conversations({ conversations, conversationId, pageNumber, pages, nextPage, previousPage, moveToTop }) {
+ const clickHandler = (func) => async (e) => {
e.preventDefault();
- await showMore();
+ await func();
};
return (
@@ -33,18 +33,29 @@ export default function Conversations({ conversations, conversationId, showMore
chatGptLabel={convo.chatGptLabel}
promptPrefix={convo.promptPrefix}
bingData={bingData}
- retainView={showMore.bind(null, false)}
+ retainView={moveToTop}
/>
);
})}
- {conversations?.length >= 12 && (
-
- Show more
-
- )}
+
+
+ <<
+
+
+ {pageNumber} / {pages}
+
+ =pages?" hidden-visibility":"")}
+ disabled={pageNumber>=pages}
+ >
+ >>
+
+
>
);
}
diff --git a/client/src/components/Nav/MobileNav.jsx b/client/src/components/Nav/MobileNav.jsx
index e4107e9ac6..0c846524fc 100644
--- a/client/src/components/Nav/MobileNav.jsx
+++ b/client/src/components/Nav/MobileNav.jsx
@@ -7,7 +7,7 @@ import { setText } from '~/store/textSlice';
export default function MobileNav({ setNavVisible }) {
const dispatch = useDispatch();
- const { conversationId, convos } = useSelector((state) => state.convo);
+ const { conversationId, convos, title } = useSelector((state) => state.convo);
const toggleNavVisible = () => {
setNavVisible((prev) => {
@@ -22,8 +22,6 @@ export default function MobileNav({ setNavVisible }) {
dispatch(setSubmission({}));
}
- const title = convos?.find(element => element?.conversationId == conversationId)?.title || 'New Chat';
-
return (
-
{title}
+ {title || 'New Chat'}
state.convo);
+ const { conversationId, convos, pages, pageNumber, refreshConvoHint } = useSelector((state) => state.convo);
const onSuccess = (data) => {
- dispatch(setConvos(data));
+ const { conversations, pages } = data;
+
+ if (pageNumber > pages)
+ dispatch(setPage(pages));
+ else
+ dispatch(setConvos(conversations));
+ dispatch(setPages(pages));
};
const { data, isLoading, mutate } = swr(
`/api/convos?pageNumber=${pageNumber}`,
- onSuccess
+ onSuccess,
+ {revalidateOnMount: false}
);
const containerRef = useRef(null);
const scrollPositionRef = useRef(null);
- const showMore = async (increment = true) => {
+ const moveToTop = () => {
const container = containerRef.current;
if (container) {
scrollPositionRef.current = container.scrollTop;
}
+ }
- if (increment) {
- dispatch(incrementPage());
- await mutate();
- }
+ const nextPage = async () => {
+ moveToTop()
+
+ dispatch(increasePage());
+ await mutate();
};
- useDidMountEffect(() => mutate(), [conversationId, refreshConvoHint]);
+ const previousPage = async () => {
+ moveToTop()
+
+ dispatch(decreasePage());
+ await mutate();
+ };
+
+ useEffect(() => {mutate()}, [pageNumber, conversationId, refreshConvoHint]);
useEffect(() => {
const container = containerRef.current;
@@ -86,8 +102,11 @@ export default function Nav({ navVisible, setNavVisible }) {
)}
diff --git a/client/src/store/convoSlice.js b/client/src/store/convoSlice.js
index 258de84e70..c862a8e1e7 100644
--- a/client/src/store/convoSlice.js
+++ b/client/src/store/convoSlice.js
@@ -13,8 +13,9 @@ const initialState = {
promptPrefix: null,
convosLoading: false,
pageNumber: 1,
+ pages: 1,
refreshConvoHint: 0,
- convos: []
+ convos: [],
};
const currentSlice = createSlice({
@@ -30,12 +31,18 @@ const currentSlice = createSlice({
setError: (state, action) => {
state.error = action.payload;
},
- incrementPage: (state) => {
+ increasePage: (state) => {
state.pageNumber = state.pageNumber + 1;
},
+ decreasePage: (state) => {
+ state.pageNumber = state.pageNumber - 1;
+ },
+ setPage: (state, action) => {
+ state.pageNumber = action.payload;
+ },
setNewConvo: (state) => {
state.error = false;
- state.title = 'New Chat';
+ state.title = 'ChatGPT Clone';
state.conversationId = null;
state.parentMessageId = null;
state.jailbreakConversationId = null;
@@ -45,13 +52,15 @@ const currentSlice = createSlice({
state.chatGptLabel = null;
state.promptPrefix = null;
state.convosLoading = false;
- state.pageNumber = 1;
},
setConvos: (state, action) => {
state.convos = action.payload.sort(
(a, b) => new Date(b.createdAt) - new Date(a.createdAt)
);
},
+ setPages: (state, action) => {
+ state.pages = action.payload;
+ },
removeConvo: (state, action) => {
state.convos = state.convos.filter((convo) => convo.conversationId !== action.payload);
},
@@ -61,7 +70,7 @@ const currentSlice = createSlice({
}
});
-export const { refreshConversation, setConversation, setConvos, setNewConvo, setError, incrementPage, removeConvo, removeAll } =
+export const { refreshConversation, setConversation, setPages, setConvos, setNewConvo, setError, increasePage, decreasePage, setPage, removeConvo, removeAll } =
currentSlice.actions;
export default currentSlice.reducer;
diff --git a/client/src/style.css b/client/src/style.css
index 6b17c388e2..c4a03408b7 100644
--- a/client/src/style.css
+++ b/client/src/style.css
@@ -1876,3 +1876,6 @@ button.scroll-convo {
background-color:hsla(0,0%,100%,.4)
}
}
+.hidden-visibility {
+ visibility: hidden;
+}
\ No newline at end of file
diff --git a/client/src/utils/fetchers.js b/client/src/utils/fetchers.js
index 9faa211d4e..d9cfa54116 100644
--- a/client/src/utils/fetchers.js
+++ b/client/src/utils/fetchers.js
@@ -9,14 +9,14 @@ const postRequest = async (url, { arg }) => {
return await axios.post(url, { arg });
};
-export const swr = (path, successCallback) => {
- const options = {};
+export const swr = (path, successCallback, options) => {
+ const _options = {...options};
if (successCallback) {
- options.onSuccess = successCallback;
+ _options.onSuccess = successCallback;
}
- return useSWR(path, fetcher, options);
+ return useSWR(path, fetcher, _options);
}
export default function manualSWR(path, type, successCallback) {
From d0ef0f84c85ff16201c45863eaed5edd490c6246 Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Tue, 14 Mar 2023 16:05:46 -0400
Subject: [PATCH 31/47] chore: delegate text handling to one place, html
sanitization in progress
---
api/server/routes/ask.js | 7 ++++---
api/server/routes/askBing.js | 3 ++-
api/server/routes/askSydney.js | 3 ++-
api/server/routes/handlers.js | 24 +++++++++++++++++++-----
4 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 5f94d7d4c3..8d20b808dc 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -8,10 +8,10 @@ const {
askClient,
browserClient,
customClient,
- detectCode
+ // detectCode
} = require('../../app/');
const { getConvo, saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const { handleError, sendMessage, createOnProgress } = require('./handlers');
+const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const { getMessages } = require('../../models/Message');
router.use('/bing', askBing);
@@ -177,7 +177,8 @@ const ask = async ({
gptResponse.sender = model === 'chatgptCustom' ? convo.chatGptLabel : model;
// gptResponse.final = true;
- gptResponse.text = await detectCode(gptResponse.text);
+ // gptResponse.text = await detectCode(gptResponse.text);
+ gptResponse.text = await handleText(gptResponse.text);
if (convo.chatGptLabel?.length > 0 && model === 'chatgptCustom') {
gptResponse.chatGptLabel = convo.chatGptLabel;
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index add804a43e..5bb8abd4f0 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -3,7 +3,7 @@ const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
const { saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const { handleError, sendMessage, createOnProgress } = require('./handlers');
+const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -113,6 +113,7 @@ const ask = async ({
response.text =
citeText(response) +
(links?.length > 0 && hasCitations ? `\n${links}` : '');
+ response.text = await handleText(response.text);
await saveMessage(response);
await saveConvo({...response, model, ...convo});
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 1f04ad4166..a0a3bd32df 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -3,7 +3,7 @@ const crypto = require('crypto');
const router = express.Router();
const { titleConvo, getCitations, citeText, askSydney } = require('../../app/');
const { saveMessage, saveConvo, getConvoTitle } = require('../../models');
-const { handleError, sendMessage, createOnProgress } = require('./handlers');
+const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -114,6 +114,7 @@ const ask = async ({
response.text =
citeText(response) +
(links?.length > 0 && hasCitations ? `\n${links}` : '');
+ response.text = await handleText(response.text);
// Save user message
userMessage.conversationId = response.conversationId || conversationId;
diff --git a/api/server/routes/handlers.js b/api/server/routes/handlers.js
index 7672ef41a5..d3e613bccc 100644
--- a/api/server/routes/handlers.js
+++ b/api/server/routes/handlers.js
@@ -1,4 +1,4 @@
-const { citeText } = require('../../app/');
+const { citeText, detectCode } = require('../../app/');
const _ = require('lodash');
const sanitizeHtml = require('sanitize-html');
@@ -20,11 +20,14 @@ const createOnProgress = () => {
const progressCallback = async (partial, { res, text, bing = false, ...rest }) => {
tokens += partial === text ? '' : partial;
- tokens = tokens.trim();
tokens = tokens.replaceAll('[DONE]', '');
- if (tokens.includes('```')) {
- tokens = sanitizeHtml(tokens);
+
+ if (tokens.match(/^\n/)) {
+ tokens = tokens.replace(/^\n/, '');
}
+ // if (tokens.includes('```')) {
+ // tokens = sanitizeHtml(tokens);
+ // }
if (bing) {
tokens = citeText(tokens, true);
@@ -42,4 +45,15 @@ const createOnProgress = () => {
return onProgress;
};
-module.exports = { handleError, sendMessage, createOnProgress };
\ No newline at end of file
+const handleText = async (input) => {
+ let text = input;
+ text = await detectCode(text);
+ // if (text.includes('```')) {
+ // text = sanitizeHtml(text);
+ // text = text.replaceAll(') =>', ') =>');
+ // }
+
+ return text;
+};
+
+module.exports = { handleError, sendMessage, createOnProgress, handleText };
\ No newline at end of file
From 626a8fbd8e048b16d0bca80bc9ac5f44cf304f63 Mon Sep 17 00:00:00 2001
From: Daniel Avila
Date: Tue, 14 Mar 2023 18:53:46 -0400
Subject: [PATCH 32/47] fix: if db is empty, will never show new convos until
refresh
---
api/models/Conversation.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/api/models/Conversation.js b/api/models/Conversation.js
index 622744d27a..c4151198cd 100644
--- a/api/models/Conversation.js
+++ b/api/models/Conversation.js
@@ -93,7 +93,7 @@ module.exports = {
// getConvos: async () => await Conversation.find({}).sort({ createdAt: -1 }).exec(),
getConvosByPage: async (pageNumber = 1, pageSize = 12) => {
try {
- const totalConvos = await Conversation.countDocuments();
+ const totalConvos = (await Conversation.countDocuments()) || 1;
const totalPages = Math.ceil(totalConvos / pageSize);
const convos = await Conversation.find()
.sort({ createdAt: -1, created: -1 })
From 6192c2964ed48adea4e95c8d580692e42b3fd8fd Mon Sep 17 00:00:00 2001
From: Daniel Avila
Date: Tue, 14 Mar 2023 20:14:38 -0400
Subject: [PATCH 33/47] fix: validation to avoid saving customGpt params to
non-custom models
---
api/models/Conversation.js | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/api/models/Conversation.js b/api/models/Conversation.js
index c4151198cd..45834379e8 100644
--- a/api/models/Conversation.js
+++ b/api/models/Conversation.js
@@ -18,10 +18,12 @@ const convoSchema = mongoose.Schema(
default: 'New Chat'
},
jailbreakConversationId: {
- type: String
+ type: String,
+ default: null
},
conversationSignature: {
- type: String
+ type: String,
+ default: null
},
clientId: {
type: String
@@ -30,13 +32,16 @@ const convoSchema = mongoose.Schema(
type: String
},
chatGptLabel: {
- type: String
+ type: String,
+ default: null
},
promptPrefix: {
- type: String
+ type: String,
+ default: null
},
model: {
- type: String
+ type: String,
+ required: true
},
suggestions: [{ type: String }],
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }]
@@ -67,8 +72,14 @@ module.exports = {
if (newConversationId) {
update.conversationId = newConversationId;
}
- if (!update.jailbreakConversationId)
- update.jailbreakConversationId = null
+ if (!update.jailbreakConversationId) {
+ update.jailbreakConversationId = null;
+ }
+ if (update.model !== 'chatgptCustom' && update.chatGptLabel && update.promptPrefix) {
+ console.log('Validation error: resetting chatgptCustom fields', update);
+ update.chatGptLabel = null;
+ update.promptPrefix = null;
+ }
return await Conversation.findOneAndUpdate(
{ conversationId },
@@ -146,7 +157,7 @@ module.exports = {
} else {
message.parentMessageId = oldId;
}
-
+
oldId = newId;
message.messageId = newId;
if (message.sender.toLowerCase() !== 'user' && !model) {
From 6e32f71565ba9449e64593bd684c8d3d60fb108a Mon Sep 17 00:00:00 2001
From: Daniel Avila
Date: Tue, 14 Mar 2023 20:15:06 -0400
Subject: [PATCH 34/47] fix: adjust custom client for new progress CB
---
api/app/chatgpt-custom.js | 7 ++-----
api/app/chatgpt.js | 38 --------------------------------------
2 files changed, 2 insertions(+), 43 deletions(-)
delete mode 100644 api/app/chatgpt.js
diff --git a/api/app/chatgpt-custom.js b/api/app/chatgpt-custom.js
index 9eabb80ccd..e7a0ee0503 100644
--- a/api/app/chatgpt-custom.js
+++ b/api/app/chatgpt-custom.js
@@ -9,7 +9,7 @@ const clientOptions = {
debug: false
};
-const customClient = async ({ text, progressCallback, convo, promptPrefix, chatGptLabel }) => {
+const customClient = async ({ text, onProgress, convo, promptPrefix, chatGptLabel }) => {
const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
const store = {
store: new KeyvFile({ filename: './data/cache.json' })
@@ -23,10 +23,7 @@ const customClient = async ({ text, progressCallback, convo, promptPrefix, chatG
const client = new ChatGPTClient(process.env.OPENAI_KEY, clientOptions, store);
- let options = {
- onProgress: async (partialRes) => await progressCallback(partialRes)
- };
-
+ let options = { onProgress };
if (!!convo.parentMessageId && !!convo.conversationId) {
options = { ...options, ...convo };
}
diff --git a/api/app/chatgpt.js b/api/app/chatgpt.js
deleted file mode 100644
index 18edcfca83..0000000000
--- a/api/app/chatgpt.js
+++ /dev/null
@@ -1,38 +0,0 @@
-require('dotenv').config();
-const Keyv = require('keyv');
-const { Configuration, OpenAIApi } = require('openai');
-const messageStore = new Keyv(process.env.MONGODB_URI, { namespace: 'chatgpt' });
-
-const ask = async (question, progressCallback, convo) => {
- const { ChatGPTAPI } = await import('chatgpt');
- const api = new ChatGPTAPI({ apiKey: process.env.OPENAI_KEY, messageStore });
- let options = {
- onProgress: async (partialRes) => {
- if (partialRes.text.length > 0) {
- await progressCallback(partialRes);
- }
- }
- };
-
- if (!!convo.parentMessageId && !!convo.conversationId) {
- options = { ...options, ...convo };
- }
-
- const res = await api.sendMessage(question, options);
- return res;
-};
-
-const titleConvo = async (message, response, model) => {
- const configuration = new Configuration({
- apiKey: process.env.OPENAI_KEY
- });
- const openai = new OpenAIApi(configuration);
- const completion = await openai.createCompletion({
- model: 'text-davinci-002',
- prompt: `Write a short title in title case, ideally in 5 words or less, and do not refer to the user or ${model}, that summarizes this conversation:\nUser:"${message}"\n${model}:"${response}"\nTitle: `
- });
-
- return completion.data.choices[0].text.replace(/\n/g, '');
-};
-
-module.exports = { ask, titleConvo };
From 796d8031e8d2c82fa18de6ec8de1cd94c2ed0128 Mon Sep 17 00:00:00 2001
From: Daniel Avila
Date: Tue, 14 Mar 2023 20:21:41 -0400
Subject: [PATCH 35/47] fix: ensure custom params are not passed to non custom
models
---
api/package-lock.json | 274 +++++++++++++++++-
api/package.json | 3 +-
api/server/routes/ask.js | 92 +++---
api/server/routes/askBing.js | 94 +++---
api/server/routes/askSydney.js | 96 +++---
client/package-lock.json | 12 +-
.../components/Conversations/Conversation.jsx | 16 +-
client/src/components/Models/ModelMenu.jsx | 6 +-
client/src/components/Nav/index.jsx | 56 ++--
client/src/store/submitSlice.js | 5 +-
10 files changed, 503 insertions(+), 151 deletions(-)
diff --git a/api/package-lock.json b/api/package-lock.json
index daa36e69e4..f934f00b8d 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -19,7 +19,8 @@
"keyv-file": "^0.2.0",
"lodash": "^4.17.21",
"mongoose": "^6.9.0",
- "openai": "^3.1.0"
+ "openai": "^3.1.0",
+ "sanitize-html": "^2.10.0"
},
"devDependencies": {
"nodemon": "^2.0.20",
@@ -2211,6 +2212,14 @@
}
}
},
+ "node_modules/deepmerge": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
+ "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/defaults": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
@@ -2247,6 +2256,57 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+ "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
"node_modules/dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
@@ -2278,6 +2338,17 @@
"node": ">= 0.8"
}
},
+ "node_modules/entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -2751,6 +2822,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/htmlparser2": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
+ "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "entities": "^4.3.0"
+ }
+ },
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -2958,6 +3047,14 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -3273,6 +3370,17 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
+ "node_modules/nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -3469,6 +3577,11 @@
"p-defer": "^3.0.0"
}
},
+ "node_modules/parse-srcset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
+ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
+ },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -3577,6 +3690,29 @@
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz",
"integrity": "sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g=="
},
+ "node_modules/postcss": {
+ "version": "8.4.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
+ "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@@ -3796,6 +3932,30 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
+ "node_modules/sanitize-html": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.10.0.tgz",
+ "integrity": "sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==",
+ "dependencies": {
+ "deepmerge": "^4.2.2",
+ "escape-string-regexp": "^4.0.0",
+ "htmlparser2": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "parse-srcset": "^1.0.2",
+ "postcss": "^8.3.11"
+ }
+ },
+ "node_modules/sanitize-html/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/saslprep": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
@@ -3985,6 +4145,14 @@
"atomic-sleep": "^1.0.0"
}
},
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/sparse-bitfield": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
@@ -6275,6 +6443,11 @@
"ms": "2.1.2"
}
},
+ "deepmerge": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
+ "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og=="
+ },
"defaults": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
@@ -6298,6 +6471,39 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
},
+ "dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+ },
+ "domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "requires": {
+ "domelementtype": "^2.3.0"
+ }
+ },
+ "domutils": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+ "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+ "requires": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.1"
+ }
+ },
"dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
@@ -6323,6 +6529,11 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
},
+ "entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
+ },
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -6687,6 +6898,17 @@
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
},
+ "htmlparser2": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
+ "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "entities": "^4.3.0"
+ }
+ },
"http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -6826,6 +7048,11 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
+ "is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+ },
"is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -7074,6 +7301,11 @@
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
+ "nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+ },
"negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -7217,6 +7449,11 @@
"p-defer": "^3.0.0"
}
},
+ "parse-srcset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
+ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
+ },
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -7303,6 +7540,16 @@
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz",
"integrity": "sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g=="
},
+ "postcss": {
+ "version": "8.4.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
+ "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+ "requires": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@@ -7458,6 +7705,26 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
+ "sanitize-html": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.10.0.tgz",
+ "integrity": "sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==",
+ "requires": {
+ "deepmerge": "^4.2.2",
+ "escape-string-regexp": "^4.0.0",
+ "htmlparser2": "^8.0.0",
+ "is-plain-object": "^5.0.0",
+ "parse-srcset": "^1.0.2",
+ "postcss": "^8.3.11"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+ }
+ }
+ },
"saslprep": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
@@ -7615,6 +7882,11 @@
"atomic-sleep": "^1.0.0"
}
},
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+ },
"sparse-bitfield": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
diff --git a/api/package.json b/api/package.json
index 6dcf344ea0..561908fad3 100644
--- a/api/package.json
+++ b/api/package.json
@@ -29,7 +29,8 @@
"keyv-file": "^0.2.0",
"lodash": "^4.17.21",
"mongoose": "^6.9.0",
- "openai": "^3.1.0"
+ "openai": "^3.1.0",
+ "sanitize-html": "^2.10.0"
},
"devDependencies": {
"nodemon": "^2.0.20",
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 8d20b808dc..265672db8b 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -7,7 +7,7 @@ const {
titleConvo,
askClient,
browserClient,
- customClient,
+ customClient
// detectCode
} = require('../../app/');
const { getConvo, saveMessage, getConvoTitle, saveConvo } = require('../../models');
@@ -18,7 +18,7 @@ router.use('/bing', askBing);
router.use('/sydney', askSydney);
router.post('/', async (req, res) => {
- let { model, text, parentMessageId, conversationId: oldConversationId , ...convo } = req.body;
+ let { model, text, parentMessageId, conversationId: oldConversationId, ...convo } = req.body;
if (text.length === 0) {
return handleError(res, { text: 'Prompt empty or too short' });
}
@@ -26,14 +26,14 @@ router.post('/', async (req, res) => {
const conversationId = oldConversationId || crypto.randomUUID();
const userMessageId = crypto.randomUUID();
- const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'
+ const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000';
let userMessage = {
- messageId: userMessageId,
- sender: 'User',
- text,
+ messageId: userMessageId,
+ sender: 'User',
+ text,
parentMessageId: userParentMessageId,
- conversationId,
- isCreatedByUser: true
+ conversationId,
+ isCreatedByUser: true
};
console.log('ask log', {
@@ -55,27 +55,28 @@ router.post('/', async (req, res) => {
await saveConvo({ ...userMessage, model, ...convo });
return await ask({
- userMessage,
+ userMessage,
model,
convo,
preSendRequest: true,
- req, res
+ req,
+ res
});
-})
+});
router.post('/regenerate', async (req, res) => {
- const { parentMessageId, model, chatGptLabel, promptPrefix } = req.body;
+ const { model } = req.body;
- const oldUserMessage = await getMessages({ messageId: req.body })
+ const oldUserMessage = await getMessages({ messageId: req.body });
if (oldUserMessage) {
- const convo = await getConvo(userMessage?.conversationId)
+ const convo = await getConvo(userMessage?.conversationId);
const userMessageId = crypto.randomUUID();
let userMessage = {
...userMessage,
- messageId: userMessageId,
+ messageId: userMessageId
};
console.log('ask log for regeneration', {
@@ -85,14 +86,14 @@ router.post('/regenerate', async (req, res) => {
});
return await ask({
- userMessage,
+ userMessage,
model,
convo,
preSendRequest: false,
- req, res
+ req,
+ res
});
- } else
- return handleError(res, { text: 'Parent message not found' });
+ } else return handleError(res, { text: 'Parent message not found' });
// if (model === 'chatgptCustom' && !chatGptLabel && conversationId) {
// const convo = await getConvo({ conversationId });
@@ -106,15 +107,21 @@ router.post('/regenerate', async (req, res) => {
// await saveConvo({ ...userMessage, model, chatGptLabel, promptPrefix });
});
-const ask = async ({
- userMessage,
+const ask = async ({
+ userMessage,
overrideParentMessageId = null,
model,
convo,
preSendRequest = true,
- req, res
+ req,
+ res
}) => {
- let { sender, text, parentMessageId: userParentMessageId, conversationId, messageId: userMessageId } = userMessage;
+ let {
+ text,
+ parentMessageId: userParentMessageId,
+ conversationId,
+ messageId: userMessageId
+ } = userMessage;
let client;
@@ -134,14 +141,13 @@ const ask = async ({
'X-Accel-Buffering': 'no'
});
- if (preSendRequest)
- sendMessage(res, { message: userMessage, created: true });
+ if (preSendRequest) sendMessage(res, { message: userMessage, created: true });
try {
const progressCallback = createOnProgress();
let gptResponse = await client({
text,
- onProgress: progressCallback.call(null, model, {res, text }),
+ onProgress: progressCallback.call(null, model, { res, text }),
convo: {
parentMessageId: userParentMessageId,
conversationId,
@@ -168,16 +174,20 @@ const ask = async ({
gptResponse.text.toLowerCase().includes('no response') ||
gptResponse.text.toLowerCase().includes('no answer')
) {
- await saveMessage({
- messageId: crypto.randomUUID(), sender: model,
- conversationId, parentMessageId: overrideParentMessageId || userMessageId,
- error: true, text: 'Prompt empty or too short'});
+ await saveMessage({
+ messageId: crypto.randomUUID(),
+ sender: model,
+ conversationId,
+ parentMessageId: overrideParentMessageId || userMessageId,
+ error: true,
+ text: 'Prompt empty or too short'
+ });
return handleError(res, { text: 'Prompt empty or too short' });
}
gptResponse.sender = model === 'chatgptCustom' ? convo.chatGptLabel : model;
+ gptResponse.model = model;
// gptResponse.final = true;
- // gptResponse.text = await detectCode(gptResponse.text);
gptResponse.text = await handleText(gptResponse.text);
if (convo.chatGptLabel?.length > 0 && model === 'chatgptCustom') {
@@ -189,14 +199,14 @@ const ask = async ({
}
// override the parentMessageId, for the regeneration.
- gptResponse.parentMessageId = overrideParentMessageId || userMessageId
+ gptResponse.parentMessageId = overrideParentMessageId || userMessageId;
await saveMessage(gptResponse);
await saveConvo(gptResponse);
sendMessage(res, {
title: await getConvoTitle(conversationId),
- final: true,
- requestMessage: userMessage,
+ final: true,
+ requestMessage: userMessage,
responseMessage: gptResponse
});
res.end();
@@ -213,15 +223,19 @@ const ask = async ({
await saveConvo({
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}
+ const errorMessage = {
+ messageId: crypto.randomUUID(),
+ sender: model,
+ conversationId,
+ parentMessageId: overrideParentMessageId || userMessageId,
+ error: true,
+ text: error.message
+ };
await saveMessage(errorMessage);
handleError(res, errorMessage);
}
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index 5bb8abd4f0..066b64faec 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -7,26 +7,32 @@ const { handleError, sendMessage, createOnProgress, handleText } = require('./ha
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { model, text, parentMessageId, conversationId: oldConversationId, ...convo } = req.body;
+ const {
+ model,
+ text,
+ 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 isNewConversation = !oldConversationId;
const userMessageId = crypto.randomUUID();
- const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'
+ const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000';
let userMessage = {
- messageId: userMessageId,
- sender: 'User',
- text,
+ messageId: userMessageId,
+ sender: 'User',
+ text,
parentMessageId: userParentMessageId,
- conversationId,
- isCreatedByUser: true
- };
+ conversationId,
+ isCreatedByUser: true
+ };
- console.log('ask log', {
+ console.log('ask log', {
model,
...userMessage,
...convo
@@ -37,24 +43,31 @@ router.post('/', async (req, res) => {
return await ask({
isNewConversation,
- userMessage,
+ userMessage,
model,
convo,
preSendRequest: true,
- req, res
+ req,
+ res
});
-})
+});
-const ask = async ({
+const ask = async ({
isNewConversation,
overrideParentMessageId = null,
- userMessage,
+ userMessage,
model,
convo,
preSendRequest = true,
- req, res
+ req,
+ res
}) => {
- let { sender, text, parentMessageId: userParentMessageId, conversationId, messageId: userMessageId } = userMessage;
+ let {
+ text,
+ parentMessageId: userParentMessageId,
+ conversationId,
+ messageId: userMessageId
+ } = userMessage;
res.writeHead(200, {
Connection: 'keep-alive',
@@ -64,19 +77,22 @@ const ask = async ({
'X-Accel-Buffering': 'no'
});
- if (preSendRequest)
- sendMessage(res, { message: userMessage, created: true });
+ if (preSendRequest) sendMessage(res, { message: userMessage, created: true });
try {
const progressCallback = createOnProgress();
let response = await askBing({
text,
- onProgress: progressCallback.call(null, model, {res, text, parentMessageId: overrideParentMessageId || userMessageId }),
+ onProgress: progressCallback.call(null, model, {
+ res,
+ text,
+ parentMessageId: overrideParentMessageId || userMessageId
+ }),
convo: {
...convo,
parentMessageId: userParentMessageId,
- conversationId,
- },
+ conversationId
+ }
});
console.log('BING RESPONSE', response);
@@ -88,13 +104,16 @@ const ask = async ({
userMessage.conversationId = response.conversationId || conversationId;
userMessage.invocationId = response.invocationId;
await saveMessage(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 saveConvo({ conversationId: conversationId, newConversationId: userMessage.conversationId });
+ if (conversationId != userMessage.conversationId && isNewConversation)
+ await saveConvo({
+ conversationId: conversationId,
+ newConversationId: userMessage.conversationId
+ });
conversationId = userMessage.conversationId;
response.text = response.response;
@@ -107,7 +126,8 @@ const ask = async ({
// response.final = true;
// override the parentMessageId, for the regeneration.
- response.parentMessageId = overrideParentMessageId || response.parentMessageId || userMessageId;
+ response.parentMessageId =
+ overrideParentMessageId || response.parentMessageId || userMessageId;
const links = getCitations(response);
response.text =
@@ -116,11 +136,11 @@ const ask = async ({
response.text = await handleText(response.text);
await saveMessage(response);
- await saveConvo({...response, model, ...convo});
+ await saveConvo({ ...response, model, chatGptLabel: null, promptPrefix: null, ...convo });
sendMessage(res, {
title: await getConvoTitle(conversationId),
- final: true,
- requestMessage: userMessage,
+ final: true,
+ requestMessage: userMessage,
responseMessage: response
});
res.end();
@@ -133,19 +153,23 @@ const ask = async ({
});
console.log('CONVERSATION TITLE', title);
-
+
await saveConvo({
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}
+ const errorMessage = {
+ messageId: crypto.randomUUID(),
+ sender: model,
+ conversationId,
+ parentMessageId: overrideParentMessageId || userMessageId,
+ error: true,
+ text: error.message
+ };
await saveMessage(errorMessage);
handleError(res, errorMessage);
}
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index a0a3bd32df..03e3479be4 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -7,26 +7,32 @@ const { handleError, sendMessage, createOnProgress, handleText } = require('./ha
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
- const { model, text, parentMessageId, conversationId: oldConversationId, ...convo } = req.body;
+ const {
+ model,
+ text,
+ 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 isNewConversation = !oldConversationId;
const userMessageId = crypto.randomUUID();
- const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000'
+ const userParentMessageId = parentMessageId || '00000000-0000-0000-0000-000000000000';
let userMessage = {
- messageId: userMessageId,
- sender: 'User',
- text,
+ messageId: userMessageId,
+ sender: 'User',
+ text,
parentMessageId: userParentMessageId,
- conversationId,
- isCreatedByUser: true
- };
+ conversationId,
+ isCreatedByUser: true
+ };
- console.log('ask log', {
+ console.log('ask log', {
model,
...userMessage,
...convo
@@ -37,24 +43,31 @@ router.post('/', async (req, res) => {
return await ask({
isNewConversation,
- userMessage,
+ userMessage,
model,
convo,
preSendRequest: true,
- req, res
+ req,
+ res
});
-})
+});
-const ask = async ({
+const ask = async ({
isNewConversation,
overrideParentMessageId = null,
- userMessage,
+ userMessage,
model,
convo,
preSendRequest = true,
- req, res
+ req,
+ res
}) => {
- let { sender, text, parentMessageId: userParentMessageId, conversationId, messageId: userMessageId } = userMessage;
+ let {
+ text,
+ parentMessageId: userParentMessageId,
+ conversationId,
+ messageId: userMessageId
+ } = userMessage;
res.writeHead(200, {
Connection: 'keep-alive',
@@ -64,19 +77,22 @@ const ask = async ({
'X-Accel-Buffering': 'no'
});
- if (preSendRequest)
- sendMessage(res, { message: userMessage, created: true });
+ if (preSendRequest) sendMessage(res, { message: userMessage, created: true });
try {
const progressCallback = createOnProgress();
let response = await askSydney({
text,
- onProgress: progressCallback.call(null, model, {res, text, parentMessageId: overrideParentMessageId || userMessageId }),
+ onProgress: progressCallback.call(null, model, {
+ res,
+ text,
+ parentMessageId: overrideParentMessageId || userMessageId
+ }),
convo: {
parentMessageId: userParentMessageId,
conversationId,
...convo
- },
+ }
});
console.log('SYDNEY RESPONSE', response);
@@ -89,13 +105,11 @@ const ask = async ({
userMessage.invocationId = response.invocationId;
// Unlike gpt and bing, Sydney will never accept our given userMessage.messageId, it will generate its own one.
await saveMessage(userMessage);
-
+
// Save sydney response
// response.id = response.messageId;
response.invocationId = convo.invocationId ? convo.invocationId + 1 : 1;
- response.conversationId = conversationId
- ? conversationId
- : crypto.randomUUID();
+ response.conversationId = conversationId ? conversationId : crypto.randomUUID();
response.conversationSignature = convo.conversationSignature
? convo.conversationSignature
: crypto.randomUUID();
@@ -108,7 +122,8 @@ const ask = async ({
// response.final = true;
// override the parentMessageId, for the regeneration.
- response.parentMessageId = overrideParentMessageId || response.parentMessageId || userMessageId;
+ response.parentMessageId =
+ overrideParentMessageId || response.parentMessageId || userMessageId;
const links = getCitations(response);
response.text =
@@ -124,17 +139,20 @@ const ask = async ({
// 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 saveConvo({ conversationId: conversationId, newConversationId: userMessage.conversationId });
+ if (conversationId != userMessage.conversationId && isNewConversation)
+ await saveConvo({
+ conversationId: conversationId,
+ newConversationId: userMessage.conversationId
+ });
conversationId = userMessage.conversationId;
// Save sydney response & convo, then send
await saveMessage(response);
- await saveConvo({...response, model, ...convo});
+ await saveConvo({ ...response, model, chatGptLabel: null, promptPrefix: null, ...convo });
sendMessage(res, {
title: await getConvoTitle(conversationId),
- final: true,
- requestMessage: userMessage,
+ final: true,
+ requestMessage: userMessage,
responseMessage: response
});
res.end();
@@ -147,19 +165,23 @@ const ask = async ({
});
console.log('CONVERSATION TITLE', title);
-
+
await saveConvo({
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}
+ const errorMessage = {
+ messageId: crypto.randomUUID(),
+ sender: model,
+ conversationId,
+ parentMessageId: overrideParentMessageId || userMessageId,
+ error: true,
+ text: error.message
+ };
await saveMessage(errorMessage);
handleError(res, errorMessage);
}
diff --git a/client/package-lock.json b/client/package-lock.json
index fbfcb2b345..d5e3024751 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -11125,9 +11125,9 @@
}
},
"node_modules/webpack": {
- "version": "5.75.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
- "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
+ "version": "5.76.1",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
+ "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
"dev": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
@@ -19431,9 +19431,9 @@
}
},
"webpack": {
- "version": "5.75.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
- "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
+ "version": "5.76.1",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
+ "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
"dev": true,
"requires": {
"@types/eslint-scope": "^3.7.3",
diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx
index 4d90ecb2cc..b89f9c8623 100644
--- a/client/src/components/Conversations/Conversation.jsx
+++ b/client/src/components/Conversations/Conversation.jsx
@@ -42,6 +42,8 @@ export default function Conversation({
dispatch(setEmptyMessage());
const convo = { title, error: false, conversationId: id, chatGptLabel, promptPrefix };
+ // debugging
+ console.log(model, chatGptLabel, promptPrefix);
if (bingData) {
const {
@@ -77,17 +79,19 @@ export default function Conversation({
if (chatGptLabel) {
dispatch(setModel('chatgptCustom'));
+ dispatch(setCustomModel(chatGptLabel.toLowerCase()));
} else {
dispatch(setModel(model));
- }
-
- if (modelMap[model.toLowerCase()]) {
- console.log('sender', model);
- dispatch(setCustomModel(model.toLowerCase()));
- } else {
dispatch(setCustomModel(null));
}
+ // if (modelMap[chatGptLabel.toLowerCase()]) {
+ // console.log('custom model', chatGptLabel);
+ // dispatch(setCustomModel(chatGptLabel.toLowerCase()));
+ // } else {
+ // dispatch(setCustomModel(null));
+ // }
+
dispatch(setMessages(data));
dispatch(setCustomGpt(convo));
dispatch(setText(''));
diff --git a/client/src/components/Models/ModelMenu.jsx b/client/src/components/Models/ModelMenu.jsx
index 64d8a427b1..05bafc676b 100644
--- a/client/src/components/Models/ModelMenu.jsx
+++ b/client/src/components/Models/ModelMenu.jsx
@@ -46,7 +46,10 @@ export default function ModelMenu() {
mutate();
try {
const lastSelected = JSON.parse(localStorage.getItem('model'));
- if (lastSelected && lastSelected !== 'chatgptCustom' && initial[lastSelected]) {
+
+ if (lastSelected === 'chatgptCustom') {
+ return;
+ } else if (initial[lastSelected]) {
dispatch(setModel(lastSelected));
}
} catch (err) {
@@ -72,6 +75,7 @@ export default function ModelMenu() {
dispatch(setModel(value));
dispatch(setDisabled(false));
dispatch(setCustomModel(null));
+ dispatch(setCustomGpt({ chatGptLabel: null, promptPrefix: null }));
} else if (!initial[value]) {
const chatGptLabel = modelMap[value]?.chatGptLabel;
const promptPrefix = modelMap[value]?.promptPrefix;
diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx
index ea95559cd8..9c02a5e1ea 100644
--- a/client/src/components/Nav/index.jsx
+++ b/client/src/components/Nav/index.jsx
@@ -11,22 +11,23 @@ import { increasePage, decreasePage, setPage, setConvos, setPages } from '~/stor
export default function Nav({ navVisible, setNavVisible }) {
const dispatch = useDispatch();
const [isHovering, setIsHovering] = useState(false);
- const { conversationId, convos, pages, pageNumber, refreshConvoHint } = useSelector((state) => state.convo);
+ const { conversationId, convos, pages, pageNumber, refreshConvoHint } = useSelector(
+ (state) => state.convo
+ );
const onSuccess = (data) => {
const { conversations, pages } = data;
- if (pageNumber > pages)
+ if (pageNumber > pages) {
dispatch(setPage(pages));
- else
+ } else {
dispatch(setConvos(conversations));
- dispatch(setPages(pages));
+ dispatch(setPages(pages));
+ }
};
- const { data, isLoading, mutate } = swr(
- `/api/convos?pageNumber=${pageNumber}`,
- onSuccess,
- {revalidateOnMount: false}
- );
+ const { data, isLoading, mutate } = swr(`/api/convos?pageNumber=${pageNumber}`, onSuccess, {
+ revalidateOnMount: false
+ });
const containerRef = useRef(null);
const scrollPositionRef = useRef(null);
@@ -36,23 +37,25 @@ export default function Nav({ navVisible, setNavVisible }) {
if (container) {
scrollPositionRef.current = container.scrollTop;
}
- }
+ };
const nextPage = async () => {
- moveToTop()
+ moveToTop();
dispatch(increasePage());
await mutate();
};
const previousPage = async () => {
- moveToTop()
-
+ moveToTop();
+
dispatch(decreasePage());
await mutate();
};
- useEffect(() => {mutate()}, [pageNumber, conversationId, refreshConvoHint]);
+ useEffect(() => {
+ mutate();
+ }, [pageNumber, conversationId, refreshConvoHint]);
useEffect(() => {
const container = containerRef.current;
@@ -66,14 +69,14 @@ export default function Nav({ navVisible, setNavVisible }) {
}, [data]);
useEffect(() => {
- setNavVisible(false)
- }, [conversationId, ])
+ setNavVisible(false);
+ }, [conversationId]);
const toggleNavVisible = () => {
setNavVisible((prev) => {
- return !prev
- })
- }
+ return !prev;
+ });
+ };
const containerClasses =
isLoading && pageNumber === 1
@@ -82,7 +85,12 @@ export default function Nav({ navVisible, setNavVisible }) {
return (
<>
-
+
Open sidebar
@@ -148,8 +156,10 @@ export default function Nav({ navVisible, setNavVisible }) {
-
-
+
>
);
}
diff --git a/client/src/store/submitSlice.js b/client/src/store/submitSlice.js
index a1b4afee34..a965c29335 100644
--- a/client/src/store/submitSlice.js
+++ b/client/src/store/submitSlice.js
@@ -6,8 +6,8 @@ const initialState = {
stopStream: false,
disabled: false,
model: 'chatgpt',
- promptPrefix: '',
- chatGptLabel: '',
+ promptPrefix: null,
+ chatGptLabel: null,
customModel: null,
};
@@ -34,6 +34,7 @@ const currentSlice = createSlice({
state.model = action.payload;
},
setCustomGpt: (state, action) => {
+ console.log('setCustomGpt', action.payload);
state.promptPrefix = action.payload.promptPrefix;
state.chatGptLabel = action.payload.chatGptLabel;
},
From 918f2fecb6d469bd54fda73b60adc782ad6612db Mon Sep 17 00:00:00 2001
From: Daniel Avila
Date: Tue, 14 Mar 2023 21:25:02 -0400
Subject: [PATCH 36/47] fix: convo resets on model change
---
client/src/components/Main/TextChat.jsx | 44 ++++------------------
client/src/components/Models/ModelMenu.jsx | 7 ++--
client/src/utils/createPayload.js | 31 +++++++++++++++
client/src/utils/resetConvo.js | 22 +++++++++++
4 files changed, 64 insertions(+), 40 deletions(-)
create mode 100644 client/src/utils/createPayload.js
create mode 100644 client/src/utils/resetConvo.js
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index 8f1276fce6..c4fd6a8f23 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -1,18 +1,17 @@
import React, { useEffect, useRef, useState } from 'react';
import { SSE } from '~/utils/sse';
-import axios from 'axios';
import SubmitButton from './SubmitButton';
import Regenerate from './Regenerate';
import ModelMenu from '../Models/ModelMenu';
import Footer from './Footer';
import TextareaAutosize from 'react-textarea-autosize';
-import handleSubmit from '~/utils/handleSubmit';
+import createPayload from '~/utils/createPayload';
+import resetConvo from '~/utils/resetConvo';
import { useSelector, useDispatch } from 'react-redux';
import { setConversation, setError, refreshConversation } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
import { setSubmitState, setSubmission } from '~/store/submitSlice';
import { setText } from '~/store/textSlice';
-import manualSWR from '~/utils/fetchers';
export default function TextChat({ messages }) {
const [errorMessage, setErrorMessage] = useState('');
@@ -25,7 +24,6 @@ export default function TextChat({ messages }) {
useSelector((state) => state.submit);
const { text } = useSelector((state) => state.text);
const { error } = convo;
- const genTitle = manualSWR(`/api/convos/gen_title`, 'post');
// auto focus to input, when enter a conversation.
useEffect(() => {
@@ -157,8 +155,12 @@ export default function TextChat({ messages }) {
const fakeMessageId = crypto.randomUUID();
const isCustomModel = model === 'chatgptCustom' || !initial[model];
const message = text.trim();
- const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId: convo.parentMessageId || '00000000-0000-0000-0000-000000000000', messageId: fakeMessageId };
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
+ let parentMessageId = convo.parentMessageId || '00000000-0000-0000-0000-000000000000';
+ if (resetConvo(messages, sender)) {
+ parentMessageId = '00000000-0000-0000-0000-000000000000';
+ }
+ const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId , messageId: fakeMessageId };
const initialResponse = { sender, text: '', parentMessageId: fakeMessageId, submitting: true };
dispatch(setSubmitState(true));
@@ -184,38 +186,6 @@ export default function TextChat({ messages }) {
dispatch(setSubmission(submission));
};
- const createPayload = ({ convo, message }) => {
- const endpoint = `/api/ask`;
- let payload = { ...message };
- const { model } = message
-
- if (!payload.conversationId)
- if (convo?.conversationId && convo?.parentMessageId) {
- payload = {
- ...payload,
- conversationId: convo.conversationId,
- parentMessageId: convo.parentMessageId || '00000000-0000-0000-0000-000000000000'
- };
- }
-
- const isBing = model === 'bingai' || model === 'sydney';
- if (isBing && convo?.conversationId) {
- payload = {
- ...payload,
- jailbreakConversationId: convo.jailbreakConversationId,
- conversationId: convo.conversationId,
- conversationSignature: convo.conversationSignature,
- clientId: convo.clientId,
- invocationId: convo.invocationId
- };
- }
-
- let server = endpoint;
- server = model === 'bingai' ? server + '/bing' : server;
- server = model === 'sydney' ? server + '/sydney' : server;
- return { server, payload };
- };
-
useEffect(() => {
if (Object.keys(submission).length === 0) {
return;
diff --git a/client/src/components/Models/ModelMenu.jsx b/client/src/components/Models/ModelMenu.jsx
index 05bafc676b..7fc819209d 100644
--- a/client/src/components/Models/ModelMenu.jsx
+++ b/client/src/components/Models/ModelMenu.jsx
@@ -64,6 +64,9 @@ export default function ModelMenu() {
}, [model]);
const onChange = (value, custom = false) => {
+ // Set new conversation
+ dispatch(setNewConvo());
+ dispatch(setSubmission({}));
// if (custom) {
// mutate();
// }
@@ -89,9 +92,7 @@ export default function ModelMenu() {
dispatch(setCustomModel(null));
}
- // Set new conversation
- dispatch(setNewConvo());
- dispatch(setSubmission({}));
+
};
const onOpenChange = (open) => {
diff --git a/client/src/utils/createPayload.js b/client/src/utils/createPayload.js
new file mode 100644
index 0000000000..fd195e43e1
--- /dev/null
+++ b/client/src/utils/createPayload.js
@@ -0,0 +1,31 @@
+export default function createPayload({ convo, message }) {
+ const endpoint = `/api/ask`;
+ let payload = { ...message };
+ const { model } = message;
+
+ if (!payload.conversationId)
+ if (convo?.conversationId && convo?.parentMessageId) {
+ payload = {
+ ...payload,
+ conversationId: convo.conversationId,
+ parentMessageId: convo.parentMessageId || '00000000-0000-0000-0000-000000000000'
+ };
+ }
+
+ const isBing = model === 'bingai' || model === 'sydney';
+ if (isBing && convo?.conversationId) {
+ payload = {
+ ...payload,
+ jailbreakConversationId: convo.jailbreakConversationId,
+ conversationId: convo.conversationId,
+ conversationSignature: convo.conversationSignature,
+ clientId: convo.clientId,
+ invocationId: convo.invocationId
+ };
+ }
+
+ let server = endpoint;
+ server = model === 'bingai' ? server + '/bing' : server;
+ server = model === 'sydney' ? server + '/sydney' : server;
+ return { server, payload };
+};
diff --git a/client/src/utils/resetConvo.js b/client/src/utils/resetConvo.js
new file mode 100644
index 0000000000..8ce01fda57
--- /dev/null
+++ b/client/src/utils/resetConvo.js
@@ -0,0 +1,22 @@
+export default function resetConvo(messages, sender) {
+ if (messages.length === 0) {
+ return false;
+ }
+ let modelMessages = messages.filter((message) => !message.isCreatedByUser);
+ let lastModel = modelMessages[modelMessages.length - 1].sender;
+ if (lastModel !== sender) {
+ console.log(
+ 'Model change! Reseting convo. Original messages: ',
+ messages,
+ 'filtered messages: ',
+ modelMessages,
+ 'last model: ',
+ lastModel,
+ 'sender: ',
+ sender
+ );
+ return true;
+ }
+
+ return false;
+}
From 4e91437049797fe6cde60732741391f1a050f6c9 Mon Sep 17 00:00:00 2001
From: Daniel Avila
Date: Tue, 14 Mar 2023 21:32:25 -0400
Subject: [PATCH 37/47] fix: convo resets, sets new Convo
---
client/src/components/Main/TextChat.jsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index c4fd6a8f23..3671833ef0 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -8,7 +8,7 @@ import TextareaAutosize from 'react-textarea-autosize';
import createPayload from '~/utils/createPayload';
import resetConvo from '~/utils/resetConvo';
import { useSelector, useDispatch } from 'react-redux';
-import { setConversation, setError, refreshConversation } from '~/store/convoSlice';
+import { setConversation, setNewConvo, setError, refreshConversation } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
import { setSubmitState, setSubmission } from '~/store/submitSlice';
import { setText } from '~/store/textSlice';
@@ -159,6 +159,7 @@ export default function TextChat({ messages }) {
let parentMessageId = convo.parentMessageId || '00000000-0000-0000-0000-000000000000';
if (resetConvo(messages, sender)) {
parentMessageId = '00000000-0000-0000-0000-000000000000';
+ dispatch(setNewConvo());
}
const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId , messageId: fakeMessageId };
const initialResponse = { sender, text: '', parentMessageId: fakeMessageId, submitting: true };
From 54aa9debb4bad5484b4d53600e4a4a31b5084758 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Wed, 15 Mar 2023 13:38:01 +0800
Subject: [PATCH 38/47] fix: don't reset new convo if model not change fix:
change model will clear all messages.
---
client/src/components/Models/ModelMenu.jsx | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/client/src/components/Models/ModelMenu.jsx b/client/src/components/Models/ModelMenu.jsx
index 7fc819209d..4e47ccbb1b 100644
--- a/client/src/components/Models/ModelMenu.jsx
+++ b/client/src/components/Models/ModelMenu.jsx
@@ -12,6 +12,8 @@ import ModelDialog from './ModelDialog';
import MenuItems from './MenuItems';
import { swr } from '~/utils/fetchers';
import { setModels } from '~/store/modelSlice';
+import { setMessages } from '~/store/messageSlice';
+import { setText } from '~/store/textSlice';
import GPTIcon from '../svg/GPTIcon';
import BingIcon from '../svg/BingIcon';
import { Button } from '../ui/Button.tsx';
@@ -64,16 +66,13 @@ export default function ModelMenu() {
}, [model]);
const onChange = (value, custom = false) => {
- // Set new conversation
- dispatch(setNewConvo());
- dispatch(setSubmission({}));
- // if (custom) {
- // mutate();
- // }
if (!value) {
return;
+ } else if (value === model) {
+ return;
} else if (value === 'chatgptCustom') {
// dispatch(setMessages([]));
+ return;
} else if (initial[value]) {
dispatch(setModel(value));
dispatch(setDisabled(false));
@@ -92,7 +91,11 @@ export default function ModelMenu() {
dispatch(setCustomModel(null));
}
-
+ // Set new conversation
+ dispatch(setText(''));
+ dispatch(setMessages([]));
+ dispatch(setNewConvo());
+ dispatch(setSubmission({}));
};
const onOpenChange = (open) => {
From 5d0b849930921a8cad297c3ac435672d71a0490a Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Wed, 15 Mar 2023 14:21:08 +0800
Subject: [PATCH 39/47] feat: show icon within model select menu fix: use icon
for gptCustom
---
.../components/Conversations/Conversation.jsx | 2 -
client/src/components/Messages/Message.jsx | 51 ++---------------
client/src/components/Models/MenuItems.jsx | 3 +
client/src/components/Models/ModelItem.jsx | 10 +++-
client/src/components/svg/BingIcon.jsx | 6 +-
client/src/components/svg/GPTIcon.jsx | 7 +--
client/src/utils/index.js | 57 +++++++++++++++++++
7 files changed, 80 insertions(+), 56 deletions(-)
diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx
index b89f9c8623..9e5c3dce6d 100644
--- a/client/src/components/Conversations/Conversation.jsx
+++ b/client/src/components/Conversations/Conversation.jsx
@@ -42,8 +42,6 @@ export default function Conversation({
dispatch(setEmptyMessage());
const convo = { title, error: false, conversationId: id, chatGptLabel, promptPrefix };
- // debugging
- console.log(model, chatGptLabel, promptPrefix);
if (bingData) {
const {
diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx
index ac74285be0..cfda8e524f 100644
--- a/client/src/components/Messages/Message.jsx
+++ b/client/src/components/Messages/Message.jsx
@@ -1,8 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';
import TextWrapper from './TextWrapper';
import { useSelector, useDispatch } from 'react-redux';
-import GPTIcon from '../svg/GPTIcon';
-import BingIcon from '../svg/BingIcon';
import HoverButtons from './HoverButtons';
import SiblingSwitch from './SiblingSwitch';
import Spinner from '../svg/Spinner';
@@ -11,6 +9,7 @@ import { setMessages } from '~/store/messageSlice';
import { setSubmitState, setSubmission } from '~/store/submitSlice';
import { setText } from '~/store/textSlice';
import { setConversation } from '../../store/convoSlice';
+import { getIconOfModel } from '../../utils';
const MultiMessage = ({
messageList,
@@ -103,52 +102,12 @@ export default function Message({
className:
'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 bg-white dark:text-gray-100 group dark:bg-gray-800'
};
-
- const bgColors = {
- chatgpt: 'rgb(16, 163, 127)',
- chatgptBrowser: 'rgb(25, 207, 207)',
- bingai: '',
- sydney: ''
- };
-
- const isBing = sender === 'bingai' || sender === 'sydney';
-
- let icon = (
-
- User
-
- );
- //`${sender}:`;
-
- let backgroundColor = bgColors[sender];
-
- if (!isCreatedByUser) {
+
+ const icon = getIconOfModel({ sender, isCreatedByUser, model, chatGptLabel, promptPrefix, error });
+
+ if (!isCreatedByUser)
props.className =
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654]';
- }
-
- if ((!isCreatedByUser && backgroundColor) || isBing) {
- icon = (
-
- {isBing ? : }
- {error && (
-
- !
-
- )}
-
- );
- }
const wrapText = (text) => ;
diff --git a/client/src/components/Models/MenuItems.jsx b/client/src/components/Models/MenuItems.jsx
index a119bee381..23e54633cd 100644
--- a/client/src/components/Models/MenuItems.jsx
+++ b/client/src/components/Models/MenuItems.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import ModelItem from './ModelItem';
export default function MenuItems({ models, onSelect }) {
+ console.log(models)
return (
<>
{models.map((modelItem) => (
@@ -11,6 +12,8 @@ export default function MenuItems({ models, onSelect }) {
modelName={modelItem.name}
value={modelItem.value}
onSelect={onSelect}
+ chatGptLabel={modelItem.chatGptLabel}
+ promptPrefix={modelItem.promptPrefix}
/>
))}
>
diff --git a/client/src/components/Models/ModelItem.jsx b/client/src/components/Models/ModelItem.jsx
index c3df204351..e6153e8002 100644
--- a/client/src/components/Models/ModelItem.jsx
+++ b/client/src/components/Models/ModelItem.jsx
@@ -7,8 +7,9 @@ import { DialogTrigger } from '../ui/Dialog.tsx';
import RenameButton from '../Conversations/RenameButton';
import TrashIcon from '../svg/TrashIcon';
import manualSWR from '~/utils/fetchers';
+import { getIconOfModel } from '../../utils';
-export default function ModelItem({ modelName, value, onSelect, id }) {
+export default function ModelItem({ modelName, value, onSelect, id, chatGptLabel, promptPrefix }) {
const dispatch = useDispatch();
const { customModel } = useSelector((state) => state.submit);
const { initial } = useSelector((state) => state.models);
@@ -27,6 +28,8 @@ export default function ModelItem({ modelName, value, onSelect, id }) {
dispatch(setModels(fetchedModels));
});
+ const icon = getIconOfModel({ size: 16, sender: modelName, isCreatedByUser: false, model: value, chatGptLabel, promptPrefix, error: false, className: "mr-2" });
+
if (value === 'chatgptCustom') {
return (
@@ -34,6 +37,7 @@ export default function ModelItem({ modelName, value, onSelect, id }) {
value={value}
className="dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
>
+ {icon}
{modelName}
$
@@ -47,6 +51,7 @@ export default function ModelItem({ modelName, value, onSelect, id }) {
value={value}
className="dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
>
+ {icon}
{modelName}
{value === 'chatgpt' && $}
@@ -122,6 +127,9 @@ export default function ModelItem({ modelName, value, onSelect, id }) {
)}
+
+ {icon}
+
{renaming === true ? (
{
+ const bgColors = {
+ chatgpt: 'rgb(16, 163, 127)',
+ chatgptBrowser: 'rgb(25, 207, 207)',
+ bingai: 'transparent',
+ sydney: 'radial-gradient(circle at 90% 110%, #F0F0FA, #D0E0F9)',
+ chatgptCustom: 'rgb(0, 163, 255)',
+ };
+
+ if (isCreatedByUser)
+ return (
+
+ User
+
+ )
+ else if (!isCreatedByUser) {
+ // TODO: use model from convo, rather than submit
+ // const { model, chatGptLabel, promptPrefix } = convo;
+ console.log(model, chatGptLabel)
+ let background = bgColors[model];
+ const isBing = model === 'bingai' || model === 'sydney';
+
+ return (
+
+ {isBing ? : }
+ {error && (
+
+ !
+
+ )}
+
+ );
+ } else
+ return (
+
+ {chatGptLabel}
+
+ )
+}
From 45ca0a8713904694cd5378d9e07347c3bcab6a48 Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Wed, 15 Mar 2023 14:42:39 +0800
Subject: [PATCH 40/47] fix: gptCustom icon should show as same in model and
message
---
client/src/components/Models/MenuItems.jsx | 2 +-
client/src/components/Models/ModelItem.jsx | 4 ++--
client/src/store/modelSlice.js | 18 ++++++++++++------
client/src/utils/index.js | 1 -
4 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/client/src/components/Models/MenuItems.jsx b/client/src/components/Models/MenuItems.jsx
index 23e54633cd..d0a277ea9e 100644
--- a/client/src/components/Models/MenuItems.jsx
+++ b/client/src/components/Models/MenuItems.jsx
@@ -2,7 +2,6 @@ import React from 'react';
import ModelItem from './ModelItem';
export default function MenuItems({ models, onSelect }) {
- console.log(models)
return (
<>
{models.map((modelItem) => (
@@ -11,6 +10,7 @@ export default function MenuItems({ models, onSelect }) {
id={modelItem._id}
modelName={modelItem.name}
value={modelItem.value}
+ model={modelItem.model || 'chatgptCustom'}
onSelect={onSelect}
chatGptLabel={modelItem.chatGptLabel}
promptPrefix={modelItem.promptPrefix}
diff --git a/client/src/components/Models/ModelItem.jsx b/client/src/components/Models/ModelItem.jsx
index e6153e8002..163c4206fd 100644
--- a/client/src/components/Models/ModelItem.jsx
+++ b/client/src/components/Models/ModelItem.jsx
@@ -9,7 +9,7 @@ import TrashIcon from '../svg/TrashIcon';
import manualSWR from '~/utils/fetchers';
import { getIconOfModel } from '../../utils';
-export default function ModelItem({ modelName, value, onSelect, id, chatGptLabel, promptPrefix }) {
+export default function ModelItem({ modelName, value, model, onSelect, id, chatGptLabel, promptPrefix }) {
const dispatch = useDispatch();
const { customModel } = useSelector((state) => state.submit);
const { initial } = useSelector((state) => state.models);
@@ -28,7 +28,7 @@ export default function ModelItem({ modelName, value, onSelect, id, chatGptLabel
dispatch(setModels(fetchedModels));
});
- const icon = getIconOfModel({ size: 16, sender: modelName, isCreatedByUser: false, model: value, chatGptLabel, promptPrefix, error: false, className: "mr-2" });
+ const icon = getIconOfModel({ size: 16, sender: modelName, isCreatedByUser: false, model, chatGptLabel, promptPrefix, error: false, className: "mr-2" });
if (value === 'chatgptCustom') {
return (
diff --git a/client/src/store/modelSlice.js b/client/src/store/modelSlice.js
index bc34673bfc..7caefdc548 100644
--- a/client/src/store/modelSlice.js
+++ b/client/src/store/modelSlice.js
@@ -5,27 +5,32 @@ const initialState = {
{
_id: '0',
name: 'ChatGPT',
- value: 'chatgpt'
+ value: 'chatgpt',
+ model: 'chatgpt'
},
{
_id: '1',
name: 'CustomGPT',
- value: 'chatgptCustom'
+ value: 'chatgptCustom',
+ model: 'chatgptCustom'
},
{
_id: '2',
name: 'BingAI',
- value: 'bingai'
+ value: 'bingai',
+ model: 'bingai'
},
{
_id: '3',
name: 'Sydney',
- value: 'sydney'
+ value: 'sydney',
+ model: 'sydney'
},
{
_id: '4',
name: 'ChatGPT',
- value: 'chatgptBrowser'
+ value: 'chatgptBrowser',
+ model: 'chatgptBrowser'
},
],
modelMap: {},
@@ -45,7 +50,8 @@ const currentSlice = createSlice({
models.slice(initialState.models.length).forEach((modelItem) => {
modelMap[modelItem.value] = {
chatGptLabel: modelItem.chatGptLabel,
- promptPrefix: modelItem.promptPrefix
+ promptPrefix: modelItem.promptPrefix,
+ model: 'chatgptCustom'
};
});
diff --git a/client/src/utils/index.js b/client/src/utils/index.js
index 4231b0c441..a48f2ff3c8 100644
--- a/client/src/utils/index.js
+++ b/client/src/utils/index.js
@@ -68,7 +68,6 @@ export const getIconOfModel = ({ size=30, sender, isCreatedByUser, model, chatGp
else if (!isCreatedByUser) {
// TODO: use model from convo, rather than submit
// const { model, chatGptLabel, promptPrefix } = convo;
- console.log(model, chatGptLabel)
let background = bgColors[model];
const isBing = model === 'bingai' || model === 'sydney';
From 96ca7835172c6c0d38ef81bc62b10f5f3bb0a3cf Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Wed, 15 Mar 2023 10:42:45 -0400
Subject: [PATCH 41/47] chore: re-organize message modules, fix icon size,
convo reset properly rebuilds Tree
---
.gitignore | 1 -
client/src/App.jsx | 3 +-
client/src/components/Main/TextChat.jsx | 8 +-
client/src/components/Messages/Message.jsx | 45 ++---------
.../src/components/Messages/MultiMessage.jsx | 40 ++++++++++
client/src/components/Messages/index.jsx | 76 ++++++++-----------
client/src/store/messageSlice.js | 3 +
client/src/utils/buildTree.js | 17 +++++
8 files changed, 103 insertions(+), 90 deletions(-)
create mode 100644 client/src/components/Messages/MultiMessage.jsx
create mode 100644 client/src/utils/buildTree.js
diff --git a/.gitignore b/.gitignore
index fa70c3c4c4..e201974879 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,7 +47,6 @@ bower_components/
.env
cache.json
api/data/
-.eslintrc.js
owner.yml
archive
.vscode/settings.json
diff --git a/client/src/App.jsx b/client/src/App.jsx
index 9bab1f5939..5f1446fd0b 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -8,7 +8,7 @@ import useDocumentTitle from '~/hooks/useDocumentTitle';
import { useSelector } from 'react-redux';
const App = () => {
- const { messages } = useSelector((state) => state.messages);
+ const { messages, messageTree } = useSelector((state) => state.messages);
const { title } = useSelector((state) => state.convo);
const { conversationId } = useSelector((state) => state.convo);
const [ navVisible, setNavVisible ]= useState(false)
@@ -25,6 +25,7 @@ const App = () => {
) : (
)}
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index 3671833ef0..e0363cd8b1 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -157,15 +157,17 @@ export default function TextChat({ messages }) {
const message = text.trim();
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
let parentMessageId = convo.parentMessageId || '00000000-0000-0000-0000-000000000000';
- if (resetConvo(messages, sender)) {
+ let currentMessages = messages;
+ if (resetConvo(currentMessages, sender)) {
parentMessageId = '00000000-0000-0000-0000-000000000000';
dispatch(setNewConvo());
+ currentMessages = [];
}
const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId , messageId: fakeMessageId };
const initialResponse = { sender, text: '', parentMessageId: fakeMessageId, submitting: true };
dispatch(setSubmitState(true));
- dispatch(setMessages([...messages, currentMsg, initialResponse]));
+ dispatch(setMessages([...currentMessages, currentMsg, initialResponse]));
dispatch(setText(''));
const submission = {
@@ -177,7 +179,7 @@ export default function TextChat({ messages }) {
chatGptLabel,
promptPrefix,
},
- messages,
+ messages: currentMessages,
currentMsg,
initialResponse,
sender,
diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx
index cfda8e524f..af60fc1a9a 100644
--- a/client/src/components/Messages/Message.jsx
+++ b/client/src/components/Messages/Message.jsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';
import TextWrapper from './TextWrapper';
+import MultiMessage from './MultiMessage';
import { useSelector, useDispatch } from 'react-redux';
import HoverButtons from './HoverButtons';
import SiblingSwitch from './SiblingSwitch';
@@ -11,42 +12,6 @@ import { setText } from '~/store/textSlice';
import { setConversation } from '../../store/convoSlice';
import { getIconOfModel } from '../../utils';
-const MultiMessage = ({
- messageList,
- messages,
- scrollToBottom,
- currentEditId,
- setCurrentEditId
-}) => {
- const [siblingIdx, setSiblingIdx] = useState(0)
-
- const setSiblingIdxRev = (value) => {
- setSiblingIdx(messageList?.length - value - 1)
- }
-
- if (!messageList?.length) return null;
-
- if (siblingIdx >= messageList?.length) {
- setSiblingIdx(0)
- return null
- }
-
- return
-}
-
-export { MultiMessage };
-
export default function Message({
message,
messages,
@@ -84,9 +49,9 @@ export default function Message({
dispatch(setConversation({parentMessageId: message?.messageId}))
}, [last, ])
- if (sender === '') {
- return ;
- }
+ // if (sender === '') {
+ // return ;
+ // }
const enterEdit = (cancel) => setCurrentEditId(cancel?-1:message.messageId)
@@ -167,7 +132,7 @@ export default function Message({
>
-
+
{typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? (
{icon}
) : (
diff --git a/client/src/components/Messages/MultiMessage.jsx b/client/src/components/Messages/MultiMessage.jsx
new file mode 100644
index 0000000000..24ab761eb6
--- /dev/null
+++ b/client/src/components/Messages/MultiMessage.jsx
@@ -0,0 +1,40 @@
+import React, { useState } from 'react';
+import Message from './Message';
+
+export default function MultiMessage({
+ messageList,
+ messages,
+ scrollToBottom,
+ currentEditId,
+ setCurrentEditId
+}) {
+ const [siblingIdx, setSiblingIdx] = useState(0);
+
+ const setSiblingIdxRev = (value) => {
+ setSiblingIdx(messageList?.length - value - 1);
+ };
+
+ // if (!messageList?.length) return null;
+ if (!(messageList && messageList.length)) {
+ return null;
+ }
+
+ if (siblingIdx >= messageList?.length) {
+ setSiblingIdx(0);
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx
index 85c671d691..eaf0a0cb6d 100644
--- a/client/src/components/Messages/index.jsx
+++ b/client/src/components/Messages/index.jsx
@@ -1,12 +1,13 @@
import React, { useEffect, useState, useRef, useMemo } from 'react';
+import Spinner from '../svg/Spinner';
import { CSSTransition } from 'react-transition-group';
import ScrollToBottom from './ScrollToBottom';
-import { MultiMessage } from './Message';
-import Conversation from '../Conversations/Conversation';
+import MultiMessage from './MultiMessage';
+import buildTree from '~/utils/buildTree';
import { useSelector } from 'react-redux';
-const Messages = ({ messages }) => {
- const [currentEditId, setCurrentEditId] = useState(-1)
+const Messages = ({ messages, messageTree }) => {
+ const [currentEditId, setCurrentEditId] = useState(-1);
const { conversationId } = useSelector((state) => state.convo);
const [showScrollButton, setShowScrollButton] = useState(false);
const scrollableRef = useRef(null);
@@ -23,26 +24,6 @@ const Messages = ({ messages }) => {
clearTimeout(timeoutId);
};
}, [messages]);
-
- const messageTree = useMemo(() => buildTree(messages), [messages, ]);
-
- function buildTree(messages) {
- let messageMap = {};
- let rootMessages = [];
-
- // Traverse the messages array and store each element in messageMap.
- messages.forEach(message => {
- messageMap[message.messageId] = {...message, children: []};
-
- const parentMessage = messageMap[message.parentMessageId];
- if (parentMessage)
- parentMessage.children.push(messageMap[message.messageId]);
- else
- rootMessages.push(messageMap[message.messageId]);
- });
-
- return rootMessages;
- }
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
@@ -79,28 +60,33 @@ const Messages = ({ messages }) => {
onScroll={debouncedHandleScroll}
>
{/*
*/}
-
-
-
-
- {() => showScrollButton && }
-
-
+
+
+ {messageTree.length === 0 ? (
+
+ ) : (
+ <>
+
+
+ {() => showScrollButton && }
+
+ >
+ )}
diff --git a/client/src/store/messageSlice.js b/client/src/store/messageSlice.js
index 137c329822..955d0c98f8 100644
--- a/client/src/store/messageSlice.js
+++ b/client/src/store/messageSlice.js
@@ -1,7 +1,9 @@
import { createSlice } from '@reduxjs/toolkit';
+import buildTree from '~/utils/buildTree';
const initialState = {
messages: [],
+ messageTree: []
};
const currentSlice = createSlice({
@@ -10,6 +12,7 @@ const currentSlice = createSlice({
reducers: {
setMessages: (state, action) => {
state.messages = action.payload;
+ state.messageTree = buildTree(action.payload);
},
setEmptyMessage: (state) => {
state.messages = [
diff --git a/client/src/utils/buildTree.js b/client/src/utils/buildTree.js
new file mode 100644
index 0000000000..a030509bca
--- /dev/null
+++ b/client/src/utils/buildTree.js
@@ -0,0 +1,17 @@
+export default function buildTree(messages) {
+ let messageMap = {};
+ let rootMessages = [];
+
+ // Traverse the messages array and store each element in messageMap.
+ messages.forEach(message => {
+ messageMap[message.messageId] = {...message, children: []};
+
+ const parentMessage = messageMap[message.parentMessageId];
+ if (parentMessage)
+ parentMessage.children.push(messageMap[message.messageId]);
+ else
+ rootMessages.push(messageMap[message.messageId]);
+ });
+
+ return rootMessages;
+}
\ No newline at end of file
From 2fd50c99b83978ce6420aadc01cdbfe84bd0ba2c Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Wed, 15 Mar 2023 12:47:30 -0400
Subject: [PATCH 42/47] fix: debounce title request and handle error with
default title
---
api/app/titleConvo.js | 39 +++++++++++++--------
api/server/routes/ask.js | 44 +++++-------------------
api/server/routes/askBing.js | 18 +++++-----
api/server/routes/askSydney.js | 18 +++++-----
api/server/routes/convos.js | 29 ----------------
api/server/routes/handlers.js | 21 +++++++++--
client/src/components/Messages/index.jsx | 1 -
7 files changed, 70 insertions(+), 100 deletions(-)
diff --git a/api/app/titleConvo.js b/api/app/titleConvo.js
index 88e8c75074..344af6f893 100644
--- a/api/app/titleConvo.js
+++ b/api/app/titleConvo.js
@@ -1,4 +1,5 @@
const { Configuration, OpenAIApi } = require('openai');
+const _ = require('lodash');
const proxyEnvToAxiosProxy = (proxyString) => {
if (!proxyString) return null;
@@ -11,29 +12,37 @@ const proxyEnvToAxiosProxy = (proxyString) => {
port: port ? parseInt(port) : undefined,
auth: username && password ? { username, password } : undefined
};
-
- return proxyConfig
-}
+
+ return proxyConfig;
+};
const titleConvo = async ({ message, response, model }) => {
const configuration = new Configuration({
apiKey: process.env.OPENAI_KEY
});
const openai = new OpenAIApi(configuration);
- const completion = await openai.createChatCompletion({
- model: 'gpt-3.5-turbo',
- messages: [
- {
- role: 'system',
- content:
- 'You are a title-generator with one job: giving a conversation, detect the language and titling the conversation provided by a user in title case, using the same language.'
- },
- { role: 'user', content: `In 5 words or less, summarize the conversation below with a title in title case using the language the user writes in. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${message}"\n\n${model}: "${response}"\n\nTitle: ` },
- ]
- }, { proxy: proxyEnvToAxiosProxy(process.env.PROXY || null) });
+ const completion = await openai.createChatCompletion(
+ {
+ model: 'gpt-3.5-turbo',
+ messages: [
+ {
+ role: 'system',
+ content:
+ 'You are a title-generator with one job: giving a conversation, detect the language and titling the conversation provided by a user in title case, using the same language.'
+ },
+ {
+ role: 'user',
+ content: `In 5 words or less, summarize the conversation below with a title in title case using the language the user writes in. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${message}"\n\n${model}: "${response}"\n\nTitle: `
+ }
+ ]
+ },
+ { proxy: proxyEnvToAxiosProxy(process.env.PROXY || null) }
+ );
//eslint-disable-next-line
return completion.data.choices[0].message.content.replace(/["\.]/g, '');
};
-module.exports = titleConvo;
+const debouncedTitleConvo = _.debounce(titleConvo, 500);
+
+module.exports = debouncedTitleConvo;
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 265672db8b..1d497c86b8 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -3,15 +3,15 @@ const crypto = require('crypto');
const router = express.Router();
const askBing = require('./askBing');
const askSydney = require('./askSydney');
-const {
- titleConvo,
- askClient,
- browserClient,
- customClient
- // detectCode
-} = require('../../app/');
+const { askClient, browserClient, customClient } = require('../../app/');
const { getConvo, saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
+const {
+ handleError,
+ sendMessage,
+ createOnProgress,
+ genTitle,
+ handleText
+} = require('./handlers');
const { getMessages } = require('../../models/Message');
router.use('/bing', askBing);
@@ -42,15 +42,6 @@ router.post('/', async (req, res) => {
...convo
});
- // if (model === 'chatgptCustom' && !chatGptLabel && conversationId) {
- // const convo = await getConvo({ conversationId });
- // if (convo) {
- // console.log('found convo for custom gpt', { convo })
- // chatGptLabel = convo.chatGptLabel;
- // promptPrefix = convo.promptPrefix;
- // }
- // }
-
await saveMessage(userMessage);
await saveConvo({ ...userMessage, model, ...convo });
@@ -94,17 +85,6 @@ router.post('/regenerate', async (req, res) => {
res
});
} else return handleError(res, { text: 'Parent message not found' });
-
- // if (model === 'chatgptCustom' && !chatGptLabel && conversationId) {
- // const convo = await getConvo({ conversationId });
- // if (convo) {
- // console.log('found convo for custom gpt', { convo })
- // chatGptLabel = convo.chatGptLabel;
- // promptPrefix = convo.promptPrefix;
- // }
- // }
-
- // await saveConvo({ ...userMessage, model, chatGptLabel, promptPrefix });
});
const ask = async ({
@@ -212,13 +192,7 @@ const ask = async ({
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
- const title = await titleConvo({
- model,
- message: text,
- response: JSON.stringify(gptResponse?.text)
- });
-
- console.log('CONVERSATION TITLE', title);
+ const title = await genTitle({ model, text, response: gptResponse });
await saveConvo({
conversationId,
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index 066b64faec..681332ec37 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -1,9 +1,15 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
-const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
+const { getCitations, citeText, askBing } = require('../../app/');
const { saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
+const {
+ handleError,
+ sendMessage,
+ createOnProgress,
+ genTitle,
+ handleText
+} = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -146,13 +152,7 @@ const ask = async ({
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
- const title = await titleConvo({
- model,
- message: text,
- response: JSON.stringify(response?.text)
- });
-
- console.log('CONVERSATION TITLE', title);
+ const title = await genTitle({ model, text, response });
await saveConvo({
conversationId,
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 03e3479be4..40aa967bcc 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -1,9 +1,15 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
-const { titleConvo, getCitations, citeText, askSydney } = require('../../app/');
+const { getCitations, citeText, askSydney } = require('../../app/');
const { saveMessage, saveConvo, getConvoTitle } = require('../../models');
-const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
+const {
+ handleError,
+ sendMessage,
+ createOnProgress,
+ genTitle,
+ handleText
+} = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -158,13 +164,7 @@ const ask = async ({
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
- const title = await titleConvo({
- model,
- message: text,
- response: JSON.stringify(response?.text)
- });
-
- console.log('CONVERSATION TITLE', title);
+ const title = await genTitle({ model, text, response });
await saveConvo({
conversationId,
diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js
index 9a5f71cee3..99fb824708 100644
--- a/api/server/routes/convos.js
+++ b/api/server/routes/convos.js
@@ -1,41 +1,12 @@
const express = require('express');
const router = express.Router();
-const { titleConvo } = require('../../app/');
-const { getConvo, saveConvo, getConvoTitle } = require('../../models');
const { getConvosByPage, deleteConvos, updateConvo } = require('../../models/Conversation');
-const { getMessages } = require('../../models/Message');
router.get('/', async (req, res) => {
const pageNumber = req.query.pageNumber || 1;
res.status(200).send(await getConvosByPage(pageNumber));
});
-router.post('/gen_title', async (req, res) => {
- const { conversationId } = req.body.arg;
-
- const convo = await getConvo(conversationId)
- const firstMessage = (await getMessages({ conversationId }))[0]
- const secondMessage = (await getMessages({ conversationId }))[1]
-
- // if (convo.title == 'New Chat') {
- // const title = await titleConvo({
- // model: convo?.model,
- // message: firstMessage?.text,
- // response: JSON.stringify(secondMessage?.text || '')
- // });
-
- // console.log('CONVERSATION TITLE', title);
-
- // await saveConvo({
- // conversationId,
- // title
- // })
-
- // res.status(200).send(title);
- // } else
- return res.status(200).send(convo.title);
-});
-
router.post('/clear', async (req, res) => {
let filter = {};
const { conversationId } = req.body.arg;
diff --git a/api/server/routes/handlers.js b/api/server/routes/handlers.js
index d3e613bccc..7c6d6ce315 100644
--- a/api/server/routes/handlers.js
+++ b/api/server/routes/handlers.js
@@ -1,4 +1,4 @@
-const { citeText, detectCode } = require('../../app/');
+const { titleConvo, citeText, detectCode } = require('../../app/');
const _ = require('lodash');
const sanitizeHtml = require('sanitize-html');
@@ -14,6 +14,23 @@ const sendMessage = (res, message) => {
res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
};
+const genTitle = async ({ model, text, response }) => {
+ let title = 'New Chat';
+ try {
+ title = await titleConvo({
+ model,
+ message: text,
+ response: JSON.stringify(response?.text)
+ });
+ } catch (e) {
+ console.error(e);
+ console.log('There was an issue generating title, see error above');
+ }
+
+ console.log('CONVERSATION TITLE', title);
+ return title;
+};
+
const createOnProgress = () => {
let i = 0;
let tokens = '';
@@ -56,4 +73,4 @@ const handleText = async (input) => {
return text;
};
-module.exports = { handleError, sendMessage, createOnProgress, handleText };
\ No newline at end of file
+module.exports = { handleError, sendMessage, createOnProgress, genTitle, handleText };
diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx
index eaf0a0cb6d..d9b5b2a80b 100644
--- a/client/src/components/Messages/index.jsx
+++ b/client/src/components/Messages/index.jsx
@@ -3,7 +3,6 @@ import Spinner from '../svg/Spinner';
import { CSSTransition } from 'react-transition-group';
import ScrollToBottom from './ScrollToBottom';
import MultiMessage from './MultiMessage';
-import buildTree from '~/utils/buildTree';
import { useSelector } from 'react-redux';
const Messages = ({ messages, messageTree }) => {
From a8aad30fc8f4d6bc75347cf7898f91946d1a9aab Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Wed, 15 Mar 2023 14:36:17 -0400
Subject: [PATCH 43/47] chore: memoized Messages component, will require custom
equality check
---
api/server/routes/handlers.js | 21 +++++++++++++++------
client/src/components/Messages/index.jsx | 2 +-
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/api/server/routes/handlers.js b/api/server/routes/handlers.js
index 7c6d6ce315..e3bd72583f 100644
--- a/api/server/routes/handlers.js
+++ b/api/server/routes/handlers.js
@@ -1,6 +1,7 @@
-const { titleConvo, citeText, detectCode } = require('../../app/');
const _ = require('lodash');
const sanitizeHtml = require('sanitize-html');
+const { titleConvo, citeText, detectCode } = require('../../app/');
+const htmlTagRegex = /(<\/?\s*[a-zA-Z]*\s*(?:\s+[a-zA-Z]+\s*=\s*(?:"[^"]*"|'[^']*'))*\s*(?:\/?)>|<\s*[a-zA-Z]+\s*(?:\s+[a-zA-Z]+\s*=\s*(?:"[^"]*"|'[^']*'))*\s*(?:\/?>|<\/?>))/g;
const handleError = (res, message) => {
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
@@ -42,8 +43,13 @@ const createOnProgress = () => {
if (tokens.match(/^\n/)) {
tokens = tokens.replace(/^\n/, '');
}
- // if (tokens.includes('```')) {
- // tokens = sanitizeHtml(tokens);
+
+ // const htmlTags = tokens.match(htmlTagRegex);
+ // if (tokens.includes('```') && htmlTags && htmlTags.length > 0) {
+ // htmlTags.forEach((tag) => {
+ // const sanitizedTag = sanitizeHtml(tag);
+ // tokens = tokens.replaceAll(tag, sanitizedTag);
+ // });
// }
if (bing) {
@@ -65,9 +71,12 @@ const createOnProgress = () => {
const handleText = async (input) => {
let text = input;
text = await detectCode(text);
- // if (text.includes('```')) {
- // text = sanitizeHtml(text);
- // text = text.replaceAll(') =>', ') =>');
+ // const htmlTags = text.match(htmlTagRegex);
+ // if (text.includes('```') && htmlTags && htmlTags.length > 0) {
+ // htmlTags.forEach((tag) => {
+ // const sanitizedTag = sanitizeHtml(tag);
+ // text = text.replaceAll(tag, sanitizedTag);
+ // });
// }
return text;
diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx
index d9b5b2a80b..611a0231d3 100644
--- a/client/src/components/Messages/index.jsx
+++ b/client/src/components/Messages/index.jsx
@@ -95,4 +95,4 @@ const Messages = ({ messages, messageTree }) => {
);
};
-export default Messages;
+export default React.memo(Messages);
From a0c94715ce99321441d0276e8733ca372d03ea42 Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Wed, 15 Mar 2023 15:21:04 -0400
Subject: [PATCH 44/47] chore: refactor titleConvo
---
api/app/titleConvo.js | 63 ++++++++++++++++++++--------------
api/server/routes/ask.js | 12 ++-----
api/server/routes/askBing.js | 12 ++-----
api/server/routes/askSydney.js | 12 ++-----
api/server/routes/handlers.js | 19 +---------
5 files changed, 47 insertions(+), 71 deletions(-)
diff --git a/api/app/titleConvo.js b/api/app/titleConvo.js
index 344af6f893..e37fcb3b0c 100644
--- a/api/app/titleConvo.js
+++ b/api/app/titleConvo.js
@@ -16,33 +16,44 @@ const proxyEnvToAxiosProxy = (proxyString) => {
return proxyConfig;
};
-const titleConvo = async ({ message, response, model }) => {
- const configuration = new Configuration({
- apiKey: process.env.OPENAI_KEY
- });
- const openai = new OpenAIApi(configuration);
- const completion = await openai.createChatCompletion(
- {
- model: 'gpt-3.5-turbo',
- messages: [
- {
- role: 'system',
- content:
- 'You are a title-generator with one job: giving a conversation, detect the language and titling the conversation provided by a user in title case, using the same language.'
- },
- {
- role: 'user',
- content: `In 5 words or less, summarize the conversation below with a title in title case using the language the user writes in. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${message}"\n\n${model}: "${response}"\n\nTitle: `
- }
- ]
- },
- { proxy: proxyEnvToAxiosProxy(process.env.PROXY || null) }
- );
+const titleConvo = async ({ model, text, response }) => {
+ let title = 'New Chat';
+ try {
+ const configuration = new Configuration({
+ apiKey: process.env.OPENAI_KEY
+ });
+ const openai = new OpenAIApi(configuration);
+ const completion = await openai.createChatCompletion(
+ {
+ model: 'gpt-3.5-turbo',
+ messages: [
+ {
+ role: 'system',
+ content:
+ 'You are a title-generator with one job: giving a conversation, detect the language and titling the conversation provided by a user in title case, using the same language.'
+ },
+ {
+ role: 'user',
+ content: `In 5 words or less, summarize the conversation below with a title in title case using the language the user writes in. Don't refer to the participants of the conversation by name. Do not include punctuation or quotation marks. Your response should be in title case, exclusively containing the title. Conversation:\n\nUser: "${text}"\n\n${model}: "${JSON.stringify(
+ response?.text
+ )}"\n\nTitle: `
+ }
+ ]
+ },
+ { proxy: proxyEnvToAxiosProxy(process.env.PROXY || null) }
+ );
- //eslint-disable-next-line
- return completion.data.choices[0].message.content.replace(/["\.]/g, '');
+ //eslint-disable-next-line
+ title = completion.data.choices[0].message.content.replace(/["\.]/g, '');
+ } catch (e) {
+ console.error(e);
+ console.log('There was an issue generating title, see error above');
+ }
+
+ console.log('CONVERSATION TITLE', title);
+ return title;
};
-const debouncedTitleConvo = _.debounce(titleConvo, 500);
+const throttledTitleConvo = _.throttle(titleConvo, 1000);
-module.exports = debouncedTitleConvo;
+module.exports = throttledTitleConvo;
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 1d497c86b8..69c180c8a7 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -3,15 +3,9 @@ const crypto = require('crypto');
const router = express.Router();
const askBing = require('./askBing');
const askSydney = require('./askSydney');
-const { askClient, browserClient, customClient } = require('../../app/');
+const { titleConvo, askClient, browserClient, customClient } = require('../../app/');
const { getConvo, saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const {
- handleError,
- sendMessage,
- createOnProgress,
- genTitle,
- handleText
-} = require('./handlers');
+const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const { getMessages } = require('../../models/Message');
router.use('/bing', askBing);
@@ -192,7 +186,7 @@ const ask = async ({
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
- const title = await genTitle({ model, text, response: gptResponse });
+ const title = await titleConvo({ model, text, response: gptResponse });
await saveConvo({
conversationId,
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index 681332ec37..aa052c31d7 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -1,15 +1,9 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
-const { getCitations, citeText, askBing } = require('../../app/');
+const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
const { saveMessage, getConvoTitle, saveConvo } = require('../../models');
-const {
- handleError,
- sendMessage,
- createOnProgress,
- genTitle,
- handleText
-} = require('./handlers');
+const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -152,7 +146,7 @@ const ask = async ({
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
- const title = await genTitle({ model, text, response });
+ const title = await titleConvo({ model, text, response });
await saveConvo({
conversationId,
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 40aa967bcc..7fecc2a450 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -1,15 +1,9 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
-const { getCitations, citeText, askSydney } = require('../../app/');
+const { titleConvo, getCitations, citeText, askSydney } = require('../../app/');
const { saveMessage, saveConvo, getConvoTitle } = require('../../models');
-const {
- handleError,
- sendMessage,
- createOnProgress,
- genTitle,
- handleText
-} = require('./handlers');
+const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
@@ -164,7 +158,7 @@ const ask = async ({
res.end();
if (userParentMessageId == '00000000-0000-0000-0000-000000000000') {
- const title = await genTitle({ model, text, response });
+ const title = await titleConvo({ model, text, response });
await saveConvo({
conversationId,
diff --git a/api/server/routes/handlers.js b/api/server/routes/handlers.js
index e3bd72583f..31213f3080 100644
--- a/api/server/routes/handlers.js
+++ b/api/server/routes/handlers.js
@@ -15,23 +15,6 @@ const sendMessage = (res, message) => {
res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
};
-const genTitle = async ({ model, text, response }) => {
- let title = 'New Chat';
- try {
- title = await titleConvo({
- model,
- message: text,
- response: JSON.stringify(response?.text)
- });
- } catch (e) {
- console.error(e);
- console.log('There was an issue generating title, see error above');
- }
-
- console.log('CONVERSATION TITLE', title);
- return title;
-};
-
const createOnProgress = () => {
let i = 0;
let tokens = '';
@@ -82,4 +65,4 @@ const handleText = async (input) => {
return text;
};
-module.exports = { handleError, sendMessage, createOnProgress, genTitle, handleText };
+module.exports = { handleError, sendMessage, createOnProgress, handleText };
From 84b104e65fad1dfa328787ff03eeba554e680022 Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Wed, 15 Mar 2023 15:44:48 -0400
Subject: [PATCH 45/47] chore: delegate response text parsing to one location
---
api/server/routes/ask.js | 2 +-
api/server/routes/askBing.js | 10 ++--------
api/server/routes/askSydney.js | 11 ++--------
api/server/routes/handlers.js | 20 +++++++++++++++----
.../src/components/Messages/TextWrapper.jsx | 2 +-
5 files changed, 22 insertions(+), 23 deletions(-)
diff --git a/api/server/routes/ask.js b/api/server/routes/ask.js
index 69c180c8a7..ad2a7178c2 100644
--- a/api/server/routes/ask.js
+++ b/api/server/routes/ask.js
@@ -162,7 +162,7 @@ const ask = async ({
gptResponse.sender = model === 'chatgptCustom' ? convo.chatGptLabel : model;
gptResponse.model = model;
// gptResponse.final = true;
- gptResponse.text = await handleText(gptResponse.text);
+ gptResponse.text = await handleText(gptResponse);
if (convo.chatGptLabel?.length > 0 && model === 'chatgptCustom') {
gptResponse.chatGptLabel = convo.chatGptLabel;
diff --git a/api/server/routes/askBing.js b/api/server/routes/askBing.js
index aa052c31d7..cfbac77185 100644
--- a/api/server/routes/askBing.js
+++ b/api/server/routes/askBing.js
@@ -1,10 +1,9 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
-const { titleConvo, getCitations, citeText, askBing } = require('../../app/');
+const { titleConvo, askBing } = require('../../app/');
const { saveMessage, getConvoTitle, saveConvo } = require('../../models');
const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
-const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
const {
@@ -129,12 +128,7 @@ const ask = async ({
response.parentMessageId =
overrideParentMessageId || response.parentMessageId || userMessageId;
- const links = getCitations(response);
- response.text =
- citeText(response) +
- (links?.length > 0 && hasCitations ? `\n${links}` : '');
- response.text = await handleText(response.text);
-
+ response.text = await handleText(response, true);
await saveMessage(response);
await saveConvo({ ...response, model, chatGptLabel: null, promptPrefix: null, ...convo });
sendMessage(res, {
diff --git a/api/server/routes/askSydney.js b/api/server/routes/askSydney.js
index 7fecc2a450..00f287fa7b 100644
--- a/api/server/routes/askSydney.js
+++ b/api/server/routes/askSydney.js
@@ -1,10 +1,9 @@
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
-const { titleConvo, getCitations, citeText, askSydney } = require('../../app/');
+const { titleConvo, askSydney } = require('../../app/');
const { saveMessage, saveConvo, getConvoTitle } = require('../../models');
const { handleError, sendMessage, createOnProgress, handleText } = require('./handlers');
-const citationRegex = /\[\^\d+?\^]/g;
router.post('/', async (req, res) => {
const {
@@ -97,7 +96,6 @@ const ask = async ({
console.log('SYDNEY RESPONSE', response);
// console.dir(response, { depth: null });
- const hasCitations = response.response.match(citationRegex)?.length > 0;
userMessage.conversationSignature =
convo.conversationSignature || response.conversationSignature;
@@ -125,12 +123,6 @@ const ask = async ({
response.parentMessageId =
overrideParentMessageId || response.parentMessageId || userMessageId;
- const links = getCitations(response);
- response.text =
- citeText(response) +
- (links?.length > 0 && hasCitations ? `\n${links}` : '');
- response.text = await handleText(response.text);
-
// Save user message
userMessage.conversationId = response.conversationId || conversationId;
await saveMessage(userMessage);
@@ -146,6 +138,7 @@ const ask = async ({
});
conversationId = userMessage.conversationId;
+ response.text = await handleText(response, true);
// Save sydney response & convo, then send
await saveMessage(response);
await saveConvo({ ...response, model, chatGptLabel: null, promptPrefix: null, ...convo });
diff --git a/api/server/routes/handlers.js b/api/server/routes/handlers.js
index 31213f3080..3ba4e81243 100644
--- a/api/server/routes/handlers.js
+++ b/api/server/routes/handlers.js
@@ -1,7 +1,8 @@
const _ = require('lodash');
const sanitizeHtml = require('sanitize-html');
-const { titleConvo, citeText, detectCode } = require('../../app/');
-const htmlTagRegex = /(<\/?\s*[a-zA-Z]*\s*(?:\s+[a-zA-Z]+\s*=\s*(?:"[^"]*"|'[^']*'))*\s*(?:\/?)>|<\s*[a-zA-Z]+\s*(?:\s+[a-zA-Z]+\s*=\s*(?:"[^"]*"|'[^']*'))*\s*(?:\/?>|<\/?>))/g;
+const citationRegex = /\[\^\d+?\^]/g;
+const { getCitations, citeText, detectCode } = require('../../app/');
+// const htmlTagRegex = /(<\/?\s*[a-zA-Z]*\s*(?:\s+[a-zA-Z]+\s*=\s*(?:"[^"]*"|'[^']*'))*\s*(?:\/?)>|<\s*[a-zA-Z]+\s*(?:\s+[a-zA-Z]+\s*=\s*(?:"[^"]*"|'[^']*'))*\s*(?:\/?>|<\/?>))/g;
const handleError = (res, message) => {
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
@@ -51,9 +52,20 @@ const createOnProgress = () => {
return onProgress;
};
-const handleText = async (input) => {
- let text = input;
+const handleText = async (response, bing = false) => {
+ let { text } = response;
text = await detectCode(text);
+ response.text = text;
+
+ if (bing) {
+ // const hasCitations = response.response.match(citationRegex)?.length > 0;
+ const links = getCitations(response);
+ if (response.text.match(citationRegex)?.length > 0) {
+ text = citeText(response);
+ }
+ text += links?.length > 0 ? `\n${links}` : '';
+ }
+
// const htmlTags = text.match(htmlTagRegex);
// if (text.includes('```') && htmlTags && htmlTags.length > 0) {
// htmlTags.forEach((tag) => {
diff --git a/client/src/components/Messages/TextWrapper.jsx b/client/src/components/Messages/TextWrapper.jsx
index af38b56a5c..5719ee876b 100644
--- a/client/src/components/Messages/TextWrapper.jsx
+++ b/client/src/components/Messages/TextWrapper.jsx
@@ -142,7 +142,7 @@ export default function TextWrapper({ text }) {
// map over the parts and wrap any text between tildes with tags
const parts = text.split(markupRegex);
const codeParts = inLineWrap(parts);
- return <>{codeParts}>; // return the wrapped text
+ return {codeParts}; // return the wrapped text
} else {
return {text};
}
From 8c6340aed05b87784e43f9ae13c8b55efcf9dbba Mon Sep 17 00:00:00 2001
From: Danny Avila
Date: Wed, 15 Mar 2023 16:38:01 -0400
Subject: [PATCH 46/47] chore: refactor cursor blink, debugging
---
client/src/components/Messages/Message.jsx | 179 +++++++++++-------
.../src/components/Messages/TextWrapper.jsx | 19 +-
client/src/style.css | 1 -
3 files changed, 121 insertions(+), 78 deletions(-)
diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx
index af60fc1a9a..622b907454 100644
--- a/client/src/components/Messages/Message.jsx
+++ b/client/src/components/Messages/Message.jsx
@@ -1,10 +1,9 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect, useRef, useCallback } from 'react';
import TextWrapper from './TextWrapper';
import MultiMessage from './MultiMessage';
import { useSelector, useDispatch } from 'react-redux';
import HoverButtons from './HoverButtons';
import SiblingSwitch from './SiblingSwitch';
-import Spinner from '../svg/Spinner';
import { setError } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
import { setSubmitState, setSubmission } from '~/store/submitSlice';
@@ -22,21 +21,28 @@ export default function Message({
siblingCount,
setSiblingIdx
}) {
- const { isSubmitting, model, chatGptLabel, promptPrefix } = useSelector((state) => state.submit);
+ const { isSubmitting, model, chatGptLabel, promptPrefix } = useSelector(
+ (state) => state.submit
+ );
const [abortScroll, setAbort] = useState(false);
- const { sender, text, isCreatedByUser, error, submitting } = message
- const textEditor = useRef(null)
+ const { sender, text, isCreatedByUser, error, submitting } = message;
+ const textEditor = useRef(null);
const convo = useSelector((state) => state.convo);
const { initial } = useSelector((state) => state.models);
const { error: convoError } = convo;
- const last = !message?.children?.length
-
+ const last = !message?.children?.length;
const edit = message.messageId == currentEditId;
-
const dispatch = useDispatch();
// const notUser = !isCreatedByUser; // sender.toLowerCase() !== 'user';
const blinker = submitting && isSubmitting && last && !isCreatedByUser;
+ const generateCursor = useCallback(() => {
+ if (!blinker) {
+ return '';
+ }
+
+ return █;
+ }, [blinker]);
useEffect(() => {
if (blinker && !abortScroll) {
@@ -45,15 +51,10 @@ export default function Message({
}, [isSubmitting, text, blinker, scrollToBottom, abortScroll]);
useEffect(() => {
- if (last)
- dispatch(setConversation({parentMessageId: message?.messageId}))
- }, [last, ])
+ if (last) dispatch(setConversation({ parentMessageId: message?.messageId }));
+ }, [last]);
- // if (sender === '') {
- // return ;
- // }
-
- const enterEdit = (cancel) => setCurrentEditId(cancel?-1:message.messageId)
+ const enterEdit = (cancel) => setCurrentEditId(cancel ? -1 : message.messageId);
const handleWheel = () => {
if (blinker) {
@@ -67,17 +68,24 @@ export default function Message({
className:
'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 bg-white dark:text-gray-100 group dark:bg-gray-800'
};
-
- const icon = getIconOfModel({ sender, isCreatedByUser, model, chatGptLabel, promptPrefix, error });
-
+
+ const icon = getIconOfModel({
+ sender,
+ isCreatedByUser,
+ model,
+ chatGptLabel,
+ promptPrefix,
+ error
+ });
+
if (!isCreatedByUser)
props.className =
'w-full border-b border-black/10 bg-gray-50 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-100 dark:bg-[#444654]';
- const wrapText = (text) => ;
+ // const wrapText = (text) => ;
const resubmitMessage = () => {
- const text = textEditor.current.innerText
+ const text = textEditor.current.innerText;
if (convoError) {
dispatch(setError(false));
@@ -90,14 +98,23 @@ export default function Message({
// this is not a real messageId, it is used as placeholder before real messageId returned
const fakeMessageId = crypto.randomUUID();
const isCustomModel = model === 'chatgptCustom' || !initial[model];
- const currentMsg = {
- sender: 'User', text: text.trim(), current: true, isCreatedByUser: true,
- parentMessageId: message?.parentMessageId,
- conversationId: message?.conversationId,
- messageId: fakeMessageId };
+ const currentMsg = {
+ sender: 'User',
+ text: text.trim(),
+ current: true,
+ isCreatedByUser: true,
+ parentMessageId: message?.parentMessageId,
+ conversationId: message?.conversationId,
+ messageId: fakeMessageId
+ };
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
- const initialResponse = { sender, text: '', parentMessageId: fakeMessageId, submitting: true };
+ const initialResponse = {
+ sender,
+ text: '',
+ parentMessageId: fakeMessageId,
+ submitting: true
+ };
dispatch(setSubmitState(true));
dispatch(setMessages([...messages, currentMsg, initialResponse]));
@@ -105,22 +122,22 @@ export default function Message({
const submission = {
isCustomModel,
- message: {
- ...currentMsg,
+ message: {
+ ...currentMsg,
model,
chatGptLabel,
- promptPrefix,
+ promptPrefix
},
messages: messages,
currentMsg,
initialResponse,
- sender,
+ sender
};
console.log('User Input:', currentMsg?.text);
// handleSubmit(submission);
dispatch(setSubmission(submission));
- setSiblingIdx(siblingCount - 1)
+ setSiblingIdx(siblingCount - 1);
enterEdit(true);
};
@@ -131,68 +148,84 @@ export default function Message({
onWheel={handleWheel}
>
-
{typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? (
{icon}
) : (
icon
)}
-
+
{error ? (
-
+
{`An error occurred. Please try again in a few moments.\n\nError message: ${text}`}
- ) :
- edit ? (
-
- {/*
*/}
-
-
- {text}
-
-
-
- Save & Submit
-
- enterEdit(true)}
- >
- Cancel
-
-
+ ) : edit ? (
+
+ {/*
*/}
+
+
+ {text}
- ) : (
-
- {/*
*/}
-
- {!isCreatedByUser ? wrapText(text) : text}
- {blinker && █}
-
+
+
+ Save & Submit
+
+ enterEdit(true)}
+ >
+ Cancel
+
- )}
+
+ ) : (
+
+ {/*
*/}
+
+ {!isCreatedByUser ? (
+
+ ) : (
+ text
+ )}
+
+
+ )}
-
enterEdit()}/>
+ enterEdit()}
+ />
>
);
diff --git a/client/src/components/Messages/TextWrapper.jsx b/client/src/components/Messages/TextWrapper.jsx
index 5719ee876b..75766bca0c 100644
--- a/client/src/components/Messages/TextWrapper.jsx
+++ b/client/src/components/Messages/TextWrapper.jsx
@@ -46,8 +46,9 @@ const inLineWrap = (parts) => {
});
};
-export default function TextWrapper({ text }) {
+export default function TextWrapper({ text, generateCursor }) {
let embedTest = false;
+ let result = null;
// to match unenclosed code blocks
if (text.match(/```/g)?.length === 1) {
@@ -137,13 +138,23 @@ export default function TextWrapper({ text }) {
}
});
- return <>{codeParts}>; // return the wrapped text
+ // return <>{codeParts}>; // return the wrapped text
+ result = <>{codeParts}>;
} else if (text.match(markupRegex)) {
// map over the parts and wrap any text between tildes with
tags
const parts = text.split(markupRegex);
const codeParts = inLineWrap(parts);
- return {codeParts}; // return the wrapped text
+ // return <>{codeParts}>; // return the wrapped text
+ result = <>{codeParts}>;
} else {
- return {text};
+ // return {text};
+ result = {text};
}
+
+ return (
+ <>
+ {result}
+ {(<>{generateCursor()}>)}
+ >
+ );
}
diff --git a/client/src/style.css b/client/src/style.css
index c4a03408b7..30fae19b40 100644
--- a/client/src/style.css
+++ b/client/src/style.css
@@ -1251,7 +1251,6 @@ html {
vertical-align: baseline;
}
-
/* .result-streaming>:not(ol):not(ul):not(pre):last-child:after,
.result-streaming>ol:last-child li:last-child:after,
.result-streaming>pre:last-child code:after,
From d052d221dc3c102979f9df017d7cc78e193c0998 Mon Sep 17 00:00:00 2001
From: Daniel Avila
Date: Wed, 15 Mar 2023 18:05:34 -0400
Subject: [PATCH 47/47] chore: switch focus to textarea when custom model
change (still need to figure out reg model change)
---
client/src/components/Main/TextChat.jsx | 4 ++--
client/src/components/Messages/TextWrapper.jsx | 2 +-
client/src/components/Models/ModelMenu.jsx | 11 ++++-------
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index e0363cd8b1..0de24e7f52 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -28,7 +28,7 @@ export default function TextChat({ messages }) {
// auto focus to input, when enter a conversation.
useEffect(() => {
inputRef.current?.focus();
- }, [convo?.conversationId, ])
+ }, [convo?.conversationId,])
const messageHandler = (data, currentState, currentMsg) => {
const { messages, _currentMsg, message, sender } = currentState;
@@ -185,11 +185,11 @@ export default function TextChat({ messages }) {
sender,
};
console.log('User Input:', message);
- // handleSubmit(submission);
dispatch(setSubmission(submission));
};
useEffect(() => {
+ inputRef.current?.focus();
if (Object.keys(submission).length === 0) {
return;
}
diff --git a/client/src/components/Messages/TextWrapper.jsx b/client/src/components/Messages/TextWrapper.jsx
index 75766bca0c..3cfd0a1b37 100644
--- a/client/src/components/Messages/TextWrapper.jsx
+++ b/client/src/components/Messages/TextWrapper.jsx
@@ -154,7 +154,7 @@ export default function TextWrapper({ text, generateCursor }) {
return (
<>
{result}
- {(<>{generateCursor()}>)}
+ {generateCursor()}
>
);
}
diff --git a/client/src/components/Models/ModelMenu.jsx b/client/src/components/Models/ModelMenu.jsx
index 4e47ccbb1b..3e3333a7cc 100644
--- a/client/src/components/Models/ModelMenu.jsx
+++ b/client/src/components/Models/ModelMenu.jsx
@@ -65,14 +65,13 @@ export default function ModelMenu() {
localStorage.setItem('model', JSON.stringify(model));
}, [model]);
- const onChange = (value, custom = false) => {
+ const onChange = (value) => {
if (!value) {
return;
} else if (value === model) {
return;
} else if (value === 'chatgptCustom') {
- // dispatch(setMessages([]));
- return;
+ // return;
} else if (initial[value]) {
dispatch(setModel(value));
dispatch(setDisabled(false));
@@ -84,9 +83,7 @@ export default function ModelMenu() {
dispatch(setCustomGpt({ chatGptLabel, promptPrefix }));
dispatch(setModel('chatgptCustom'));
dispatch(setCustomModel(value));
- // if (custom) {
- // setMenuOpen((prevOpen) => !prevOpen);
- // }
+ setMenuOpen(false);
} else if (!modelMap[value]) {
dispatch(setCustomModel(null));
}
@@ -160,7 +157,7 @@ export default function ModelMenu() {
{icon}
-
+ event.preventDefault()}>
Select a Model