mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
refactor: nav and search.
feat: use recoil to replace redux feat: use react-native THIS IS NOT FINISHED. DONT USE THIS
This commit is contained in:
parent
d8ccc5b870
commit
af3d74b104
33 changed files with 1142 additions and 473 deletions
|
|
@ -1,19 +1,21 @@
|
|||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './src/store';
|
||||
// import { Provider } from 'react-redux';
|
||||
// import { store } from './src/store';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { ThemeProvider } from './src/hooks/ThemeContext';
|
||||
import App from './src/App';
|
||||
import './src/style.css';
|
||||
import './src/mobile.css'
|
||||
import './src/mobile.css';
|
||||
|
||||
const container = document.getElementById('root');
|
||||
const root = createRoot(container);
|
||||
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<RecoilRoot>
|
||||
<ThemeProvider>
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
);
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -36,9 +36,11 @@
|
|||
"react-lazy-load": "^4.0.1",
|
||||
"react-markdown": "^8.0.5",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.9.0",
|
||||
"react-string-replace": "^1.1.0",
|
||||
"react-textarea-autosize": "^8.4.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"recoil": "^0.7.7",
|
||||
"rehype-highlight": "^6.0.0",
|
||||
"rehype-katex": "^6.0.2",
|
||||
"rehype-raw": "^6.1.1",
|
||||
|
|
|
|||
|
|
@ -1,53 +1,52 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Messages from './components/Messages';
|
||||
import Landing from './components/Main/Landing';
|
||||
import TextChat from './components/Main/TextChat';
|
||||
import Nav from './components/Nav';
|
||||
import MobileNav from './components/Nav/MobileNav';
|
||||
import useDocumentTitle from '~/hooks/useDocumentTitle';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom';
|
||||
import Root from './routes/Root';
|
||||
// import Chat from './routes/Chat';
|
||||
import store from './store';
|
||||
import userAuth from './utils/userAuth';
|
||||
import { setUser } from './store/userReducer';
|
||||
import { setSearchState } from './store/searchSlice';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
const App = () => {
|
||||
const dispatch = useDispatch();
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <Root />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: (
|
||||
<Navigate
|
||||
to="/chat/new"
|
||||
replace={true}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'chat/:conversationId',
|
||||
element: null //<Chat />
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const { messages, messageTree } = useSelector((state) => state.messages);
|
||||
const { user } = useSelector((state) => state.user);
|
||||
const { title } = useSelector((state) => state.convo);
|
||||
const [navVisible, setNavVisible] = useState(false);
|
||||
useDocumentTitle(title);
|
||||
const App = () => {
|
||||
const [user, setUser] = useRecoilState(store.user);
|
||||
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
|
||||
|
||||
useEffect(() => {
|
||||
axios.get('/api/search/enable').then((res) => { console.log(res.data); dispatch(setSearchState(res.data))});
|
||||
axios.get('/api/search/enable').then(res => {
|
||||
setIsSearchEnabled(res.data);
|
||||
});
|
||||
userAuth()
|
||||
.then((user) => dispatch(setUser(user)))
|
||||
.catch((err) => console.log(err));
|
||||
.then(user => setUser(user))
|
||||
.catch(err => console.log(err));
|
||||
}, []);
|
||||
|
||||
if (user)
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<Nav
|
||||
navVisible={navVisible}
|
||||
setNavVisible={setNavVisible}
|
||||
/>
|
||||
<div className="flex h-full w-full flex-1 flex-col bg-gray-50 md:pl-[260px]">
|
||||
<div className="transition-width relative flex h-full w-full flex-1 flex-col items-stretch overflow-hidden bg-white dark:bg-gray-800">
|
||||
<MobileNav setNavVisible={setNavVisible} />
|
||||
{messages.length === 0 && title.toLowerCase() === 'chatgpt clone' ? (
|
||||
<Landing title={title} />
|
||||
) : (
|
||||
<Messages
|
||||
messages={messages}
|
||||
messageTree={messageTree}
|
||||
/>
|
||||
)}
|
||||
<TextChat messages={messages} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<RouterProvider router={router} />
|
||||
</div>
|
||||
);
|
||||
else return <div className="flex h-screen"></div>;
|
||||
|
|
|
|||
|
|
@ -1,99 +1,125 @@
|
|||
import React, { useState, useRef } from 'react';
|
||||
import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import RenameButton from './RenameButton';
|
||||
import DeleteButton from './DeleteButton';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { setConversation } from '~/store/convoSlice';
|
||||
import { setSubmission, setStopStream, setCustomGpt, setModel, setCustomModel } from '~/store/submitSlice';
|
||||
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';
|
||||
import manualSWR from '~/utils/fetchers';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function Conversation({ conversation, retainView }) {
|
||||
const [currentConversation, setCurrentConversation] = useRecoilState(store.conversation);
|
||||
const setMessages = useSetRecoilState(store.messages);
|
||||
const setSubmission = useSetRecoilState(store.submission);
|
||||
const resetLatestMessage = useResetRecoilState(store.latestMessage);
|
||||
|
||||
const { refreshConversations } = store.useConversations();
|
||||
|
||||
export default function Conversation({
|
||||
id,
|
||||
model,
|
||||
parentMessageId,
|
||||
conversationId,
|
||||
title,
|
||||
chatGptLabel = null,
|
||||
promptPrefix = null,
|
||||
bingData,
|
||||
retainView,
|
||||
}) {
|
||||
const [renaming, setRenaming] = useState(false);
|
||||
const [titleInput, setTitleInput] = useState(title);
|
||||
const { stopStream } = useSelector((state) => state.submit);
|
||||
const inputRef = useRef(null);
|
||||
const dispatch = useDispatch();
|
||||
const { trigger } = manualSWR(`/api/messages/${id}`, 'get');
|
||||
|
||||
const {
|
||||
model,
|
||||
parentMessageId,
|
||||
conversationId,
|
||||
title,
|
||||
chatGptLabel = null,
|
||||
promptPrefix = null,
|
||||
jailbreakConversationId,
|
||||
conversationSignature,
|
||||
clientId,
|
||||
invocationId,
|
||||
toneStyle
|
||||
} = conversation;
|
||||
|
||||
const rename = manualSWR(`/api/convos/update`, 'post');
|
||||
|
||||
const bingData = conversationSignature
|
||||
? {
|
||||
jailbreakConversationId: jailbreakConversationId,
|
||||
conversationSignature: conversationSignature,
|
||||
parentMessageId: parentMessageId || null,
|
||||
clientId: clientId,
|
||||
invocationId: invocationId,
|
||||
toneStyle: toneStyle
|
||||
}
|
||||
: null;
|
||||
|
||||
const clickHandler = async () => {
|
||||
if (conversationId === id) {
|
||||
if (currentConversation?.conversationId === conversationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stopStream) {
|
||||
dispatch(setStopStream(true));
|
||||
dispatch(setSubmission({}));
|
||||
}
|
||||
dispatch(setEmptyMessage());
|
||||
// stop existing submission
|
||||
setSubmission(null);
|
||||
|
||||
const convo = { title, error: false, conversationId: id, chatGptLabel, promptPrefix };
|
||||
// set conversation to the new conversation
|
||||
setCurrentConversation(conversation);
|
||||
setMessages(null);
|
||||
resetLatestMessage();
|
||||
|
||||
if (bingData) {
|
||||
const {
|
||||
parentMessageId,
|
||||
conversationSignature,
|
||||
jailbreakConversationId,
|
||||
clientId,
|
||||
invocationId,
|
||||
toneStyle,
|
||||
} = bingData;
|
||||
dispatch(
|
||||
setConversation({
|
||||
...convo,
|
||||
parentMessageId,
|
||||
jailbreakConversationId,
|
||||
conversationSignature,
|
||||
clientId,
|
||||
invocationId,
|
||||
toneStyle,
|
||||
latestMessage: null
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(
|
||||
setConversation({
|
||||
...convo,
|
||||
parentMessageId,
|
||||
jailbreakConversationId: null,
|
||||
conversationSignature: null,
|
||||
clientId: null,
|
||||
invocationId: null,
|
||||
toneStyle: null,
|
||||
latestMessage: null
|
||||
})
|
||||
);
|
||||
}
|
||||
const data = await trigger();
|
||||
// if (!stopStream) {
|
||||
// dispatch(setStopStream(true));
|
||||
// dispatch(setSubmission({}));
|
||||
// }
|
||||
// dispatch(setEmptyMessage());
|
||||
|
||||
if (chatGptLabel) {
|
||||
dispatch(setModel('chatgptCustom'));
|
||||
dispatch(setCustomModel(chatGptLabel.toLowerCase()));
|
||||
} else {
|
||||
dispatch(setModel(model));
|
||||
dispatch(setCustomModel(null));
|
||||
}
|
||||
// const convo = { title, error: false, conversationId: id, chatGptLabel, promptPrefix };
|
||||
|
||||
dispatch(setMessages(data));
|
||||
dispatch(setCustomGpt(convo));
|
||||
dispatch(setText(''));
|
||||
dispatch(setStopStream(false));
|
||||
// if (bingData) {
|
||||
// const {
|
||||
// parentMessageId,
|
||||
// conversationSignature,
|
||||
// jailbreakConversationId,
|
||||
// clientId,
|
||||
// invocationId,
|
||||
// toneStyle
|
||||
// } = bingData;
|
||||
// dispatch(
|
||||
// setConversation({
|
||||
// ...convo,
|
||||
// parentMessageId,
|
||||
// jailbreakConversationId,
|
||||
// conversationSignature,
|
||||
// clientId,
|
||||
// invocationId,
|
||||
// toneStyle,
|
||||
// latestMessage: null
|
||||
// })
|
||||
// );
|
||||
// } else {
|
||||
// dispatch(
|
||||
// setConversation({
|
||||
// ...convo,
|
||||
// parentMessageId,
|
||||
// jailbreakConversationId: null,
|
||||
// conversationSignature: null,
|
||||
// clientId: null,
|
||||
// invocationId: null,
|
||||
// toneStyle: null,
|
||||
// latestMessage: null
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// const data = await trigger();
|
||||
|
||||
// if (chatGptLabel) {
|
||||
// dispatch(setModel('chatgptCustom'));
|
||||
// dispatch(setCustomModel(chatGptLabel.toLowerCase()));
|
||||
// } else {
|
||||
// dispatch(setModel(model));
|
||||
// dispatch(setCustomModel(null));
|
||||
// }
|
||||
|
||||
// dispatch(setMessages(data));
|
||||
// dispatch(setCustomGpt(convo));
|
||||
// dispatch(setText(''));
|
||||
// dispatch(setStopStream(false));
|
||||
};
|
||||
|
||||
const renameHandler = (e) => {
|
||||
const renameHandler = e => {
|
||||
e.preventDefault();
|
||||
setTitleInput(title);
|
||||
setRenaming(true);
|
||||
|
|
@ -102,24 +128,28 @@ export default function Conversation({
|
|||
}, 25);
|
||||
};
|
||||
|
||||
const cancelHandler = (e) => {
|
||||
const cancelHandler = e => {
|
||||
e.preventDefault();
|
||||
setRenaming(false);
|
||||
};
|
||||
|
||||
const onRename = (e) => {
|
||||
const onRename = e => {
|
||||
e.preventDefault();
|
||||
setRenaming(false);
|
||||
if (titleInput === title) {
|
||||
return;
|
||||
}
|
||||
rename.trigger({ conversationId, title: titleInput })
|
||||
.then(() => {
|
||||
dispatch(refreshConversation())
|
||||
});
|
||||
rename.trigger({ conversationId, title: titleInput }).then(() => {
|
||||
refreshConversations();
|
||||
if (conversationId == currentConversation?.conversationId)
|
||||
setCurrentConversation(prevState => ({
|
||||
...prevState,
|
||||
title: titleInput
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
const handleKeyDown = e => {
|
||||
if (e.key === 'Enter') {
|
||||
onRename(e);
|
||||
}
|
||||
|
|
@ -130,7 +160,7 @@ export default function Conversation({
|
|||
'animate-flash group relative flex cursor-pointer items-center gap-3 break-all rounded-md bg-gray-800 py-3 px-3 pr-14 hover:bg-gray-800'
|
||||
};
|
||||
|
||||
if (conversationId !== id) {
|
||||
if (currentConversation?.conversationId !== conversationId) {
|
||||
aProps.className =
|
||||
'group relative flex cursor-pointer items-center gap-3 break-all rounded-md py-3 px-3 hover:bg-[#2A2B32] hover:pr-4';
|
||||
}
|
||||
|
|
@ -148,7 +178,7 @@ export default function Conversation({
|
|||
type="text"
|
||||
className="m-0 mr-0 w-full border border-blue-500 bg-transparent p-0 text-sm leading-tight outline-none"
|
||||
value={titleInput}
|
||||
onChange={(e) => setTitleInput(e.target.value)}
|
||||
onChange={e => setTitleInput(e.target.value)}
|
||||
onBlur={onRename}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
|
|
@ -156,16 +186,16 @@ export default function Conversation({
|
|||
title
|
||||
)}
|
||||
</div>
|
||||
{conversationId === id ? (
|
||||
{currentConversation?.conversationId === conversationId ? (
|
||||
<div className="visible absolute right-1 z-10 flex text-gray-300">
|
||||
<RenameButton
|
||||
conversationId={id}
|
||||
conversationId={conversationId}
|
||||
renaming={renaming}
|
||||
renameHandler={renameHandler}
|
||||
onRename={onRename}
|
||||
/>
|
||||
<DeleteButton
|
||||
conversationId={id}
|
||||
conversationId={conversationId}
|
||||
renaming={renaming}
|
||||
cancelHandler={cancelHandler}
|
||||
retainView={retainView}
|
||||
|
|
|
|||
|
|
@ -2,24 +2,19 @@ import React from 'react';
|
|||
import TrashIcon from '../svg/TrashIcon';
|
||||
import CrossIcon from '../svg/CrossIcon';
|
||||
import manualSWR from '~/utils/fetchers';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setNewConvo, removeConvo } from '~/store/convoSlice';
|
||||
import { setMessages } from '~/store/messageSlice';
|
||||
import { setSubmission } from '~/store/submitSlice';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function DeleteButton({ conversationId, renaming, cancelHandler, retainView }) {
|
||||
const dispatch = useDispatch();
|
||||
const { trigger } = manualSWR(
|
||||
`/api/convos/clear`,
|
||||
'post',
|
||||
() => {
|
||||
dispatch(setMessages([]));
|
||||
dispatch(removeConvo(conversationId));
|
||||
dispatch(setNewConvo());
|
||||
dispatch(setSubmission({}));
|
||||
retainView();
|
||||
}
|
||||
);
|
||||
const currentConversation = useRecoilValue(store.conversation) || {};
|
||||
const { newConversation } = store.useConversation();
|
||||
const { refreshConversations } = store.useConversations();
|
||||
const { trigger } = manualSWR(`/api/convos/clear`, 'post', () => {
|
||||
if (currentConversation?.conversationId == conversationId) newConversation();
|
||||
refreshConversations();
|
||||
retainView();
|
||||
});
|
||||
|
||||
const clickHandler = () => trigger({ conversationId });
|
||||
const handler = renaming ? cancelHandler : clickHandler;
|
||||
|
|
@ -29,7 +24,7 @@ export default function DeleteButton({ conversationId, renaming, cancelHandler,
|
|||
className="p-1 hover:text-white"
|
||||
onClick={handler}
|
||||
>
|
||||
{ renaming ? <CrossIcon/> : <TrashIcon />}
|
||||
{renaming ? <CrossIcon /> : <TrashIcon />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
|
||||
export default function Pages({ pageNumber, pages, nextPage, previousPage }) {
|
||||
const clickHandler = (func) => async (e) => {
|
||||
const clickHandler = func => async e => {
|
||||
e.preventDefault();
|
||||
await func();
|
||||
};
|
||||
|
||||
return (
|
||||
return pageNumber == 1 && pages == 1 ? null : (
|
||||
<div className="m-auto mt-4 mb-2 flex items-center justify-center gap-2">
|
||||
<button
|
||||
onClick={clickHandler(previousPage)}
|
||||
|
|
|
|||
|
|
@ -2,34 +2,15 @@ import React from 'react';
|
|||
import Conversation from './Conversation';
|
||||
|
||||
export default function Conversations({ conversations, conversationId, moveToTop }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{conversations &&
|
||||
conversations.length > 0 &&
|
||||
conversations.map((convo) => {
|
||||
const bingData = convo.conversationSignature
|
||||
? {
|
||||
jailbreakConversationId: convo.jailbreakConversationId,
|
||||
conversationSignature: convo.conversationSignature,
|
||||
parentMessageId: convo.parentMessageId || null,
|
||||
clientId: convo.clientId,
|
||||
invocationId: convo.invocationId,
|
||||
toneStyle: convo.toneStyle,
|
||||
}
|
||||
: null;
|
||||
|
||||
conversations.map(convo => {
|
||||
return (
|
||||
<Conversation
|
||||
key={convo.conversationId}
|
||||
id={convo.conversationId}
|
||||
model={convo.model}
|
||||
parentMessageId={convo.parentMessageId}
|
||||
title={convo.title}
|
||||
conversationId={conversationId}
|
||||
chatGptLabel={convo.chatGptLabel}
|
||||
promptPrefix={convo.promptPrefix}
|
||||
bingData={bingData}
|
||||
conversation={convo}
|
||||
retainView={moveToTop}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
305
client/src/components/MessageHandler/index.jsx
Normal file
305
client/src/components/MessageHandler/index.jsx
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useRecoilState, useResetRecoilState, useSetRecoilState } from "recoil";
|
||||
import { SSE } from "~/utils/sse";
|
||||
import { useMessageHandler } from "../../utils/handleSubmit";
|
||||
import createPayload from "~/utils/createPayload";
|
||||
|
||||
import store from "~/store";
|
||||
|
||||
export default function MessageHandler({ messages }) {
|
||||
const [submission, setSubmission] = useRecoilState(store.submission);
|
||||
const [isSubmitting, setIsSubmitting] = useRecoilState(store.isSubmitting);
|
||||
const setMessages = useSetRecoilState(store.messages);
|
||||
const setConversation = useSetRecoilState(store.conversation);
|
||||
const resetLatestMessage = useResetRecoilState(store.latestMessage);
|
||||
|
||||
const { refreshConversations } = store.useConversations();
|
||||
|
||||
const messageHandler = (data, submission) => {
|
||||
const {
|
||||
messages,
|
||||
message,
|
||||
initialResponse,
|
||||
isRegenerate = false,
|
||||
} = submission;
|
||||
|
||||
if (isRegenerate)
|
||||
setMessages([
|
||||
...messages,
|
||||
{
|
||||
...initialResponse,
|
||||
text: data,
|
||||
parentMessageId: message?.overrideParentMessageId,
|
||||
messageId: message?.overrideParentMessageId + "_",
|
||||
submitting: true,
|
||||
},
|
||||
]);
|
||||
else
|
||||
setMessages([
|
||||
...messages,
|
||||
message,
|
||||
{
|
||||
...initialResponse,
|
||||
text: data,
|
||||
parentMessageId: message?.messageId,
|
||||
messageId: message?.messageId + "_",
|
||||
submitting: true,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const cancelHandler = (data, submission) => {
|
||||
const {
|
||||
messages,
|
||||
message,
|
||||
initialResponse,
|
||||
isRegenerate = false,
|
||||
} = submission;
|
||||
|
||||
if (isRegenerate)
|
||||
setMessages([
|
||||
...messages,
|
||||
{
|
||||
...initialResponse,
|
||||
text: data,
|
||||
parentMessageId: message?.overrideParentMessageId,
|
||||
messageId: message?.overrideParentMessageId + "_",
|
||||
cancelled: true,
|
||||
},
|
||||
]);
|
||||
else
|
||||
setMessages([
|
||||
...messages,
|
||||
message,
|
||||
{
|
||||
...initialResponse,
|
||||
text: data,
|
||||
parentMessageId: message?.messageId,
|
||||
messageId: message?.messageId + "_",
|
||||
cancelled: true,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const createdHandler = (data, submission) => {
|
||||
const {
|
||||
messages,
|
||||
message,
|
||||
initialResponse,
|
||||
isRegenerate = false,
|
||||
} = submission;
|
||||
|
||||
if (isRegenerate)
|
||||
setMessages([
|
||||
...messages,
|
||||
{
|
||||
...initialResponse,
|
||||
parentMessageId: message?.overrideParentMessageId,
|
||||
messageId: message?.overrideParentMessageId + "_",
|
||||
submitting: true,
|
||||
},
|
||||
]);
|
||||
else
|
||||
setMessages([
|
||||
...messages,
|
||||
message,
|
||||
{
|
||||
...initialResponse,
|
||||
parentMessageId: message?.messageId,
|
||||
messageId: message?.messageId + "_",
|
||||
submitting: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const { conversationId } = message;
|
||||
setConversation((prevState) => ({
|
||||
...prevState,
|
||||
conversationId,
|
||||
}));
|
||||
resetLatestMessage();
|
||||
};
|
||||
|
||||
const finalHandler = (data, submission) => {
|
||||
const {
|
||||
conversation,
|
||||
messages,
|
||||
message,
|
||||
initialResponse,
|
||||
isRegenerate = false,
|
||||
} = submission;
|
||||
|
||||
const { requestMessage, responseMessage } = data;
|
||||
const { conversationId } = requestMessage;
|
||||
|
||||
// update the messages
|
||||
if (isRegenerate) setMessages([...messages, responseMessage]);
|
||||
else setMessages([...messages, requestMessage, responseMessage]);
|
||||
setIsSubmitting(false);
|
||||
|
||||
// refresh title
|
||||
if (
|
||||
requestMessage.parentMessageId == "00000000-0000-0000-0000-000000000000"
|
||||
) {
|
||||
setTimeout(() => {
|
||||
refreshConversations();
|
||||
}, 2000);
|
||||
|
||||
// in case it takes too long.
|
||||
setTimeout(() => {
|
||||
refreshConversations();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
const { model, chatGptLabel, promptPrefix } = conversation;
|
||||
const isBing = model === "bingai" || model === "sydney";
|
||||
|
||||
if (!isBing) {
|
||||
const { title } = data;
|
||||
const { conversationId } = responseMessage;
|
||||
setConversation((prevState) => ({
|
||||
...prevState,
|
||||
title,
|
||||
conversationId,
|
||||
jailbreakConversationId: null,
|
||||
conversationSignature: null,
|
||||
clientId: null,
|
||||
invocationId: null,
|
||||
chatGptLabel,
|
||||
promptPrefix,
|
||||
latestMessage: null,
|
||||
}));
|
||||
} else if (model === "bingai") {
|
||||
const { title } = data;
|
||||
const { conversationSignature, clientId, conversationId, invocationId } =
|
||||
responseMessage;
|
||||
setConversation((prevState) => ({
|
||||
...prevState,
|
||||
title,
|
||||
conversationId,
|
||||
jailbreakConversationId: null,
|
||||
conversationSignature,
|
||||
clientId,
|
||||
invocationId,
|
||||
chatGptLabel,
|
||||
promptPrefix,
|
||||
latestMessage: null,
|
||||
}));
|
||||
} else if (model === "sydney") {
|
||||
const { title } = data;
|
||||
const {
|
||||
jailbreakConversationId,
|
||||
parentMessageId,
|
||||
conversationSignature,
|
||||
clientId,
|
||||
conversationId,
|
||||
invocationId,
|
||||
} = responseMessage;
|
||||
setConversation((prevState) => ({
|
||||
...prevState,
|
||||
title,
|
||||
conversationId,
|
||||
jailbreakConversationId,
|
||||
conversationSignature,
|
||||
clientId,
|
||||
invocationId,
|
||||
chatGptLabel,
|
||||
promptPrefix,
|
||||
latestMessage: null,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const errorHandler = (data, submission) => {
|
||||
const {
|
||||
conversation,
|
||||
messages,
|
||||
message,
|
||||
initialResponse,
|
||||
isRegenerate = false,
|
||||
} = submission;
|
||||
|
||||
console.log("Error:", data);
|
||||
const errorResponse = {
|
||||
...data,
|
||||
error: true,
|
||||
parentMessageId: message?.messageId,
|
||||
};
|
||||
setIsSubmitting(false);
|
||||
setMessages([...messages, message, errorResponse]);
|
||||
return;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (submission === null) return;
|
||||
if (Object.keys(submission).length === 0) return;
|
||||
|
||||
const { messages, initialResponse, isRegenerate = false } = submission;
|
||||
let { message } = submission;
|
||||
|
||||
const { server, payload } = createPayload(submission);
|
||||
|
||||
const events = new SSE(server, {
|
||||
payload: JSON.stringify(payload),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
let latestResponseText = "";
|
||||
events.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
|
||||
if (data.final) {
|
||||
finalHandler(data, { ...submission, message });
|
||||
console.log("final", data);
|
||||
}
|
||||
if (data.created) {
|
||||
message = {
|
||||
...data.message,
|
||||
model: message?.model,
|
||||
chatGptLabel: message?.chatGptLabel,
|
||||
promptPrefix: message?.promptPrefix,
|
||||
overrideParentMessageId: message?.overrideParentMessageId,
|
||||
};
|
||||
createdHandler(data, { ...submission, message });
|
||||
console.log("created", message);
|
||||
} else {
|
||||
let text = data.text || data.response;
|
||||
if (data.initial) console.log(data);
|
||||
|
||||
if (data.message) {
|
||||
latestResponseText = text;
|
||||
messageHandler(text, { ...submission, message });
|
||||
}
|
||||
// console.log('dataStream', data);
|
||||
}
|
||||
};
|
||||
|
||||
events.onopen = () => console.log("connection is opened");
|
||||
|
||||
events.oncancel = (e) =>
|
||||
cancelHandler(latestResponseText, { ...submission, message });
|
||||
|
||||
events.onerror = function (e) {
|
||||
console.log("error in opening conn.");
|
||||
events.close();
|
||||
|
||||
const data = JSON.parse(e.data);
|
||||
|
||||
errorHandler(data, { ...submission, message });
|
||||
};
|
||||
|
||||
setIsSubmitting(true);
|
||||
events.stream();
|
||||
|
||||
return () => {
|
||||
const isCancelled = events.readyState <= 1;
|
||||
events.close();
|
||||
if (isCancelled) {
|
||||
const e = new Event("cancel");
|
||||
events.dispatchEvent(e);
|
||||
}
|
||||
setIsSubmitting(false);
|
||||
};
|
||||
}, [submission]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -2,50 +2,31 @@ import React from 'react';
|
|||
import TrashIcon from '../svg/TrashIcon';
|
||||
import { useSWRConfig } from 'swr';
|
||||
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';
|
||||
import { Dialog, DialogTrigger } from '../ui/Dialog.tsx';
|
||||
import DialogTemplate from '../ui/DialogTemplate';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function ClearConvos() {
|
||||
const dispatch = useDispatch();
|
||||
const { newConversation } = store.useConversation();
|
||||
const { refreshConversations } = store.useConversations();
|
||||
const { mutate } = useSWRConfig();
|
||||
|
||||
const { trigger } = manualSWR(`/api/convos/clear`, 'post', () => {
|
||||
dispatch(setMessages([]));
|
||||
dispatch(setNewConvo());
|
||||
dispatch(setSubmission({}));
|
||||
mutate(`/api/convos`);
|
||||
newConversation();
|
||||
refreshConversations();
|
||||
});
|
||||
|
||||
const clickHandler = () => {
|
||||
console.log('Clearing conversations...');
|
||||
dispatch(removeAll());
|
||||
trigger({});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<a
|
||||
className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
|
||||
// onClick={clickHandler}
|
||||
onClick={clickHandler}
|
||||
>
|
||||
<TrashIcon />
|
||||
Clear conversations
|
||||
</a>
|
||||
</DialogTrigger>
|
||||
<DialogTemplate
|
||||
title="Clear conversations"
|
||||
description="Are you sure you want to clear all conversations? This is irreversible."
|
||||
selection={{
|
||||
selectHandler: clickHandler,
|
||||
selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white',
|
||||
selectText: 'Clear',
|
||||
}}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import React, { useState, useContext } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import React from 'react';
|
||||
import LogOutIcon from '../svg/LogOutIcon';
|
||||
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import store from '~/store';
|
||||
|
||||
export default function Logout() {
|
||||
const { user } = useSelector((state) => state.user);
|
||||
|
||||
const user = useRecoilValue(store.user);
|
||||
|
||||
const clickHandler = () => {
|
||||
window.location.href = "/auth/logout";
|
||||
window.location.href = '/auth/logout';
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,33 +1,19 @@
|
|||
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';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function MobileNav({ setNavVisible }) {
|
||||
const dispatch = useDispatch();
|
||||
const { conversationId, convos, title } = useSelector((state) => state.convo);
|
||||
|
||||
const toggleNavVisible = () => {
|
||||
setNavVisible((prev) => {
|
||||
return !prev
|
||||
})
|
||||
}
|
||||
|
||||
const newConvo = () => {
|
||||
dispatch(setText(''));
|
||||
dispatch(setMessages([]));
|
||||
dispatch(setNewConvo());
|
||||
dispatch(setSubmission({}));
|
||||
}
|
||||
const conversation = useRecoilValue(store.conversation);
|
||||
const { newConversation } = store.useConversation();
|
||||
const { title = 'New Chat' } = conversation || {};
|
||||
|
||||
return (
|
||||
<div className="fixed top-0 left-0 right-0 z-10 flex items-center border-b border-white/20 bg-gray-800 pl-1 pt-1 text-gray-200 sm:pl-3 md:hidden">
|
||||
<button
|
||||
type="button"
|
||||
className="-ml-0.5 -mt-0.5 inline-flex h-10 w-10 items-center justify-center rounded-md hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white dark:hover:text-white"
|
||||
onClick={toggleNavVisible}
|
||||
onClick={() => setNavVisible(prev => !prev)}
|
||||
>
|
||||
<span className="sr-only">Open sidebar</span>
|
||||
<svg
|
||||
|
|
@ -66,7 +52,7 @@ export default function MobileNav({ setNavVisible }) {
|
|||
<button
|
||||
type="button"
|
||||
className="px-3"
|
||||
onClick={newConvo}
|
||||
onClick={() => newConversation()}
|
||||
>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
|
|
|
|||
|
|
@ -3,13 +3,17 @@ import SearchBar from './SearchBar';
|
|||
import ClearConvos from './ClearConvos';
|
||||
import DarkMode from './DarkMode';
|
||||
import Logout from './Logout';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
export default function NavLinks({ fetch, onSearchSuccess, clearSearch }) {
|
||||
const { searchEnabled } = useSelector((state) => state.search);
|
||||
export default function NavLinks({ fetch, onSearchSuccess, clearSearch, isSearchEnabled }) {
|
||||
return (
|
||||
<>
|
||||
{ !!searchEnabled && <SearchBar fetch={fetch} onSuccess={onSearchSuccess} clearSearch={clearSearch}/>}
|
||||
{!!isSearchEnabled && (
|
||||
<SearchBar
|
||||
fetch={fetch}
|
||||
onSuccess={onSearchSuccess}
|
||||
clearSearch={clearSearch}
|
||||
/>
|
||||
)}
|
||||
<DarkMode />
|
||||
<ClearConvos />
|
||||
<Logout />
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setNewConvo, refreshConversation } from '~/store/convoSlice';
|
||||
import { setMessages } from '~/store/messageSlice';
|
||||
import { setSubmission, setDisabled } from '~/store/submitSlice';
|
||||
import { setText } from '~/store/textSlice';
|
||||
import { setInputValue, setQuery } from '~/store/searchSlice';
|
||||
import store from '~/store';
|
||||
|
||||
export default function NewChat() {
|
||||
const dispatch = useDispatch();
|
||||
const { newConversation } = store.useConversation();
|
||||
|
||||
const clickHandler = () => {
|
||||
dispatch(setText(''));
|
||||
dispatch(setMessages([]));
|
||||
dispatch(setNewConvo());
|
||||
dispatch(refreshConversation());
|
||||
dispatch(setSubmission({}));
|
||||
dispatch(setDisabled(false));
|
||||
dispatch(setInputValue(''));
|
||||
dispatch(setQuery(''));
|
||||
// dispatch(setInputValue(''));
|
||||
// dispatch(setQuery(''));
|
||||
newConversation();
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,41 +1,44 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { Search } from 'lucide-react';
|
||||
import { setInputValue, setQuery } from '~/store/searchSlice';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function SearchBar({ fetch, clearSearch }) {
|
||||
const dispatch = useDispatch();
|
||||
const { inputValue } = useSelector((state) => state.search);
|
||||
// const dispatch = useDispatch();
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const setSearchQuery = useSetRecoilState(store.searchQuery);
|
||||
|
||||
// const [inputValue, setInputValue] = useState('');
|
||||
|
||||
const debouncedChangeHandler = useCallback(
|
||||
debounce((q) => {
|
||||
dispatch(setQuery(q));
|
||||
debounce(q => {
|
||||
setSearchQuery(q);
|
||||
if (q.length > 0) {
|
||||
fetch(q, 1);
|
||||
}
|
||||
}, 750),
|
||||
[dispatch]
|
||||
[setSearchQuery]
|
||||
);
|
||||
|
||||
const handleKeyUp = (e) => {
|
||||
const handleKeyUp = e => {
|
||||
const { value } = e.target;
|
||||
if (e.keyCode === 8 && value === '') {
|
||||
if (e.keyCode === 8 && value === '') {
|
||||
// Value after clearing input: ""
|
||||
console.log(`Value after clearing input: "${value}"`);
|
||||
dispatch(setQuery(''));
|
||||
setSearchQuery('');
|
||||
clearSearch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const changeHandler = (e) => {
|
||||
const changeHandler = e => {
|
||||
let q = e.target.value;
|
||||
dispatch(setInputValue(q));
|
||||
setInputValue(q);
|
||||
q = q.trim();
|
||||
|
||||
if (q === '') {
|
||||
dispatch(setQuery(''));
|
||||
setSearchQuery('');
|
||||
clearSearch();
|
||||
} else {
|
||||
debouncedChangeHandler(q);
|
||||
|
|
|
|||
|
|
@ -6,67 +6,121 @@ import Pages from '../Conversations/Pages';
|
|||
import Conversations from '../Conversations';
|
||||
import NavLinks from './NavLinks';
|
||||
import { searchFetcher, swr } from '~/utils/fetchers';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { setConvos, setNewConvo, refreshConversation } from '~/store/convoSlice';
|
||||
import { setMessages } from '~/store/messageSlice';
|
||||
import { setDisabled } from '~/store/submitSlice';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function Nav({ navVisible, setNavVisible }) {
|
||||
const dispatch = useDispatch();
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
|
||||
const containerRef = useRef(null);
|
||||
const scrollPositionRef = useRef(null);
|
||||
|
||||
// const dispatch = useDispatch();
|
||||
const [conversations, setConversations] = useState([]);
|
||||
// current page
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
// total pages
|
||||
const [pages, setPages] = useState(1);
|
||||
const [pageNumber, setPage] = useState(1);
|
||||
const { search, query } = useSelector((state) => state.search);
|
||||
const { conversationId, convos, refreshConvoHint } = useSelector((state) => state.convo);
|
||||
|
||||
|
||||
// search
|
||||
const searchQuery = useRecoilValue(store.searchQuery);
|
||||
const isSearchEnabled = useRecoilValue(store.isSearchEnabled);
|
||||
const isSearching = useRecoilValue(store.isSearching);
|
||||
const { newConversation } = store.useConversation();
|
||||
|
||||
// current conversation
|
||||
const conversation = useRecoilValue(store.conversation);
|
||||
const { conversationId } = conversation || {};
|
||||
const setMessages = useSetRecoilState(store.messages);
|
||||
|
||||
// refreshConversationsHint is used for other components to ask refresh of Nav
|
||||
const refreshConversationsHint = useRecoilValue(store.refreshConversationsHint);
|
||||
|
||||
const { refreshConversations } = store.useConversations();
|
||||
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
|
||||
const onSuccess = (data, searchFetch = false) => {
|
||||
if (search) {
|
||||
if (isSearching) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { conversations, pages } = data;
|
||||
let { conversations, pages } = data;
|
||||
if (pageNumber > pages) {
|
||||
setPage(pages);
|
||||
setPageNumber(pages);
|
||||
} else {
|
||||
dispatch(setConvos({ convos: conversations, searchFetch }));
|
||||
if (!searchFetch)
|
||||
conversations = conversations.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
||||
setConversations(conversations);
|
||||
setPages(pages);
|
||||
}
|
||||
};
|
||||
|
||||
const onSearchSuccess = (data, expectedPage) => {
|
||||
const res = data;
|
||||
dispatch(setConvos({ convos: res.conversations, searchFetch: true }));
|
||||
setConversations(res.conversations);
|
||||
if (expectedPage) {
|
||||
setPage(expectedPage);
|
||||
setPageNumber(expectedPage);
|
||||
}
|
||||
setPage(res.pageNumber);
|
||||
setPageNumber(res.pageNumber);
|
||||
setPages(res.pages);
|
||||
setIsFetching(false);
|
||||
if (res.messages?.length > 0) {
|
||||
dispatch(setMessages(res.messages));
|
||||
dispatch(setDisabled(true));
|
||||
setMessages(res.messages);
|
||||
// dispatch(setDisabled(true));
|
||||
}
|
||||
};
|
||||
|
||||
const fetch = useCallback(_.partialRight(searchFetcher.bind(null, () => setIsFetching(true)), onSearchSuccess), [dispatch]);
|
||||
// TODO: dont need this
|
||||
const fetch = useCallback(
|
||||
_.partialRight(
|
||||
searchFetcher.bind(null, () => setIsFetching(true)),
|
||||
onSearchSuccess
|
||||
),
|
||||
[setIsFetching]
|
||||
);
|
||||
|
||||
const clearSearch = () => {
|
||||
setPage(1);
|
||||
dispatch(refreshConversation());
|
||||
if (!conversationId) {
|
||||
dispatch(setNewConvo());
|
||||
dispatch(setMessages([]));
|
||||
setPageNumber(1);
|
||||
refreshConversations();
|
||||
if (conversationId == 'search') {
|
||||
newConversation();
|
||||
}
|
||||
dispatch(setDisabled(false));
|
||||
// dispatch(setDisabled(false));
|
||||
};
|
||||
|
||||
const { data, isLoading, mutate } = swr(`/api/convos?pageNumber=${pageNumber}`, onSuccess, {
|
||||
revalidateOnMount: false,
|
||||
revalidateOnMount: false
|
||||
});
|
||||
|
||||
const containerRef = useRef(null);
|
||||
const scrollPositionRef = useRef(null);
|
||||
const nextPage = async () => {
|
||||
moveToTop();
|
||||
|
||||
if (!isSearching) {
|
||||
setPageNumber(prev => prev + 1);
|
||||
await mutate();
|
||||
} else {
|
||||
await fetch(searchQuery, +pageNumber + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const previousPage = async () => {
|
||||
moveToTop();
|
||||
|
||||
if (!isSearching) {
|
||||
setPageNumber(prev => prev - 1);
|
||||
await mutate();
|
||||
} else {
|
||||
await fetch(searchQuery, +pageNumber - 1);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSearching) {
|
||||
mutate();
|
||||
}
|
||||
}, [pageNumber, conversationId, refreshConversationsHint]);
|
||||
|
||||
const moveToTop = () => {
|
||||
const container = containerRef.current;
|
||||
|
|
@ -75,35 +129,7 @@ export default function Nav({ navVisible, setNavVisible }) {
|
|||
}
|
||||
};
|
||||
|
||||
const nextPage = async () => {
|
||||
moveToTop();
|
||||
|
||||
if (!search) {
|
||||
setPage((prev) => prev + 1);
|
||||
await mutate();
|
||||
} else {
|
||||
await fetch(query, +pageNumber + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const previousPage = async () => {
|
||||
moveToTop();
|
||||
|
||||
if (!search) {
|
||||
setPage((prev) => prev - 1);
|
||||
await mutate();
|
||||
} else {
|
||||
await fetch(query, +pageNumber - 1);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!search) {
|
||||
mutate();
|
||||
}
|
||||
}, [pageNumber, conversationId, refreshConvoHint]);
|
||||
|
||||
useEffect(() => {
|
||||
const moveTo = () => {
|
||||
const container = containerRef.current;
|
||||
|
||||
if (container && scrollPositionRef.current !== null) {
|
||||
|
|
@ -112,18 +138,20 @@ export default function Nav({ navVisible, setNavVisible }) {
|
|||
|
||||
container.scrollTop = Math.min(maxScrollTop, scrollPositionRef.current);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleNavVisible = () => {
|
||||
setNavVisible(prev => !prev);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
moveTo();
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
setNavVisible(false);
|
||||
}, [conversationId]);
|
||||
|
||||
const toggleNavVisible = () => {
|
||||
setNavVisible((prev) => {
|
||||
return !prev;
|
||||
});
|
||||
};
|
||||
|
||||
const containerClasses =
|
||||
isLoading && pageNumber === 1
|
||||
? 'flex flex-col gap-2 text-gray-100 text-sm h-full justify-center items-center'
|
||||
|
|
@ -151,11 +179,11 @@ export default function Nav({ navVisible, setNavVisible }) {
|
|||
>
|
||||
<div className={containerClasses}>
|
||||
{/* {(isLoading && pageNumber === 1) ? ( */}
|
||||
{(isLoading && pageNumber === 1) || (isFetching) ? (
|
||||
{(isLoading && pageNumber === 1) || isFetching ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<Conversations
|
||||
conversations={convos}
|
||||
conversations={conversations}
|
||||
conversationId={conversationId}
|
||||
moveToTop={moveToTop}
|
||||
/>
|
||||
|
|
@ -172,6 +200,7 @@ export default function Nav({ navVisible, setNavVisible }) {
|
|||
fetch={fetch}
|
||||
onSearchSuccess={onSearchSuccess}
|
||||
clearSearch={clearSearch}
|
||||
isSearchEnabled={isSearchEnabled}
|
||||
/>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
67
client/src/routes/Chat.jsx
Normal file
67
client/src/routes/Chat.jsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
|
||||
|
||||
import Landing from "../components/ui/Landing";
|
||||
import Messages from "../components/Messages";
|
||||
import TextChat from "../components/Input";
|
||||
|
||||
import store from "~/store";
|
||||
import manualSWR from "~/utils/fetchers";
|
||||
// import TextChat from './components/Main/TextChat';
|
||||
|
||||
// {/* <TextChat messages={messages} /> */}
|
||||
|
||||
export default function Chat() {
|
||||
const [conversation, setConversation] = useRecoilState(store.conversation);
|
||||
const setMessages = useSetRecoilState(store.messages);
|
||||
const messagesTree = useRecoilValue(store.messagesTree);
|
||||
const { newConversation } = store.useConversation();
|
||||
const { conversationId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { trigger: messagesTrigger } = manualSWR(
|
||||
`/api/messages/${conversation?.conversationId}`,
|
||||
"get"
|
||||
);
|
||||
|
||||
const { trigger: conversationTrigger } = manualSWR(
|
||||
`/api/convos/${conversationId}`,
|
||||
"get"
|
||||
);
|
||||
|
||||
// when conversation changed or conversationId (in url) changed
|
||||
useEffect(() => {
|
||||
if (conversation === null) {
|
||||
// no current conversation, we need to do something
|
||||
if (conversationId == "new") {
|
||||
// create new
|
||||
newConversation();
|
||||
} else {
|
||||
// fetch it from server
|
||||
conversationTrigger().then(setConversation);
|
||||
setMessages(null);
|
||||
console.log("NEED TO FETCH DATA");
|
||||
}
|
||||
} else if (conversation?.conversationId !== conversationId)
|
||||
// conversationId (in url) should always follow conversation?.conversationId, unless conversation is null
|
||||
navigate(`/chat/${conversation?.conversationId}`);
|
||||
}, [conversation, conversationId]);
|
||||
|
||||
// when messagesTree is null (<=> messages is null)
|
||||
// we need to fetch message list from server
|
||||
useEffect(() => {
|
||||
if (messagesTree === null) {
|
||||
messagesTrigger().then(setMessages);
|
||||
}
|
||||
}, [conversation?.conversationId]);
|
||||
|
||||
if (conversation?.conversationId !== conversationId) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{conversationId == "new" ? <Landing /> : <Messages />}
|
||||
<TextChat />
|
||||
</>
|
||||
);
|
||||
}
|
||||
29
client/src/routes/Root.jsx
Normal file
29
client/src/routes/Root.jsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import MessageHandler from '../components/MessageHandler';
|
||||
import Nav from '../components/Nav';
|
||||
import MobileNav from '../components/Nav/MobileNav';
|
||||
|
||||
export default function Root() {
|
||||
const [navVisible, setNavVisible] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-screen">
|
||||
<Nav
|
||||
navVisible={navVisible}
|
||||
setNavVisible={setNavVisible}
|
||||
/>
|
||||
<div className="flex h-full w-full flex-1 flex-col bg-gray-50 md:pl-[260px]">
|
||||
<div className="transition-width relative flex h-full w-full flex-1 flex-col items-stretch overflow-hidden bg-white pt-10 dark:bg-gray-800 md:pt-0">
|
||||
<MobileNav setNavVisible={setNavVisible} />
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MessageHandler />
|
||||
</>
|
||||
);
|
||||
}
|
||||
106
client/src/store/conversation.js
Normal file
106
client/src/store/conversation.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import models from './models';
|
||||
import { atom, selector, useRecoilValue, useSetRecoilState, useResetRecoilState } from 'recoil';
|
||||
import buildTree from '~/utils/buildTree';
|
||||
|
||||
// current conversation, can be null (need to be fetched from server)
|
||||
// sample structure
|
||||
// {
|
||||
// conversationId: "new",
|
||||
// title: "New Chat",
|
||||
// jailbreakConversationId: null,
|
||||
// conversationSignature: null,
|
||||
// clientId: null,
|
||||
// invocationId: null,
|
||||
// model: "chatgpt",
|
||||
// chatGptLabel: null,
|
||||
// promptPrefix: null,
|
||||
// user: null,
|
||||
// suggestions: [],
|
||||
// toneStyle: null,
|
||||
// }
|
||||
const conversation = atom({
|
||||
key: 'conversation',
|
||||
default: null
|
||||
});
|
||||
|
||||
// current messages of the conversation, must be an array
|
||||
// sample structure
|
||||
// [{text, sender, messageId, parentMessageId, isCreatedByUser}]
|
||||
const messages = atom({
|
||||
key: 'messages',
|
||||
default: []
|
||||
});
|
||||
|
||||
const messagesTree = selector({
|
||||
key: 'messagesTree',
|
||||
get: ({ get }) => {
|
||||
return buildTree(get(messages));
|
||||
}
|
||||
});
|
||||
|
||||
const latestMessage = atom({
|
||||
key: 'latestMessage',
|
||||
default: null
|
||||
});
|
||||
|
||||
const useConversation = () => {
|
||||
const modelsFilter = useRecoilValue(models.modelsFilter);
|
||||
const setConversation = useSetRecoilState(conversation);
|
||||
const setMessages = useSetRecoilState(messages);
|
||||
const resetLatestMessage = useResetRecoilState(latestMessage);
|
||||
|
||||
const newConversation = ({ model = null, chatGptLabel = null, promptPrefix = null } = {}) => {
|
||||
const getDefaultModel = () => {
|
||||
try {
|
||||
// try to read latest selected model from local storage
|
||||
const lastSelected = JSON.parse(localStorage.getItem('model'));
|
||||
const { model: _model, chatGptLabel: _chatGptLabel, promptPrefix: _promptPrefix } = lastSelected;
|
||||
|
||||
if (modelsFilter[_model]) {
|
||||
model = _model;
|
||||
chatGptLabel = _chatGptLabel;
|
||||
promptPrefix = _promptPrefix;
|
||||
return;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
// if anything happens, reset to default model
|
||||
if (modelsFilter?.chatgpt) model = 'chatgpt';
|
||||
else if (modelsFilter?.bingai) model = 'bingai';
|
||||
else if (modelsFilter?.chatgptBrowser) model = 'chatgptBrowser';
|
||||
chatGptLabel = null;
|
||||
promptPrefix = null;
|
||||
};
|
||||
|
||||
if (model === null)
|
||||
// get the default model
|
||||
getDefaultModel();
|
||||
|
||||
setConversation({
|
||||
conversationId: 'new',
|
||||
title: 'New Chat',
|
||||
jailbreakConversationId: null,
|
||||
conversationSignature: null,
|
||||
clientId: null,
|
||||
invocationId: null,
|
||||
model: model,
|
||||
chatGptLabel: chatGptLabel,
|
||||
promptPrefix: promptPrefix,
|
||||
user: null,
|
||||
suggestions: [],
|
||||
toneStyle: null
|
||||
});
|
||||
setMessages([]);
|
||||
resetLatestMessage();
|
||||
};
|
||||
|
||||
return { newConversation };
|
||||
};
|
||||
|
||||
export default {
|
||||
conversation,
|
||||
messages,
|
||||
messagesTree,
|
||||
latestMessage,
|
||||
useConversation
|
||||
};
|
||||
27
client/src/store/conversations.js
Normal file
27
client/src/store/conversations.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import React from "react";
|
||||
import {
|
||||
RecoilRoot,
|
||||
atom,
|
||||
selector,
|
||||
useRecoilState,
|
||||
useRecoilValue,
|
||||
useSetRecoilState,
|
||||
} from "recoil";
|
||||
|
||||
const refreshConversationsHint = atom({
|
||||
key: "refreshConversationsHint",
|
||||
default: 1,
|
||||
});
|
||||
|
||||
const useConversations = () => {
|
||||
const setRefreshConversationsHint = useSetRecoilState(
|
||||
refreshConversationsHint
|
||||
);
|
||||
|
||||
const refreshConversations = () =>
|
||||
setRefreshConversationsHint((prevState) => prevState + 1);
|
||||
|
||||
return { refreshConversations };
|
||||
};
|
||||
|
||||
export default { refreshConversationsHint, useConversations };
|
||||
|
|
@ -1,22 +1,15 @@
|
|||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import conversation from './conversation';
|
||||
import conversations from './conversations';
|
||||
import models from './models';
|
||||
import user from './user';
|
||||
import submission from './submission';
|
||||
import search from './search';
|
||||
|
||||
import convoReducer from './convoSlice.js';
|
||||
import messageReducer from './messageSlice.js';
|
||||
import modelReducer from './modelSlice.js';
|
||||
import submitReducer from './submitSlice.js';
|
||||
import textReducer from './textSlice.js';
|
||||
import userReducer from './userReducer.js';
|
||||
import searchReducer from './searchSlice.js';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
convo: convoReducer,
|
||||
messages: messageReducer,
|
||||
models: modelReducer,
|
||||
text: textReducer,
|
||||
submit: submitReducer,
|
||||
user: userReducer,
|
||||
search: searchReducer
|
||||
},
|
||||
devTools: true
|
||||
});
|
||||
export default {
|
||||
...conversation,
|
||||
...conversations,
|
||||
...models,
|
||||
...user,
|
||||
...submission,
|
||||
...search
|
||||
};
|
||||
|
|
|
|||
80
client/src/store/models.js
Normal file
80
client/src/store/models.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import React from "react";
|
||||
import {
|
||||
RecoilRoot,
|
||||
atom,
|
||||
selector,
|
||||
useRecoilState,
|
||||
useRecoilValue,
|
||||
} from "recoil";
|
||||
|
||||
const customGPTModels = atom({
|
||||
key: "customGPTModels",
|
||||
default: [],
|
||||
});
|
||||
|
||||
const models = selector({
|
||||
key: "models",
|
||||
get: ({ get }) => {
|
||||
return [
|
||||
{
|
||||
_id: "0",
|
||||
name: "ChatGPT",
|
||||
value: "chatgpt",
|
||||
model: "chatgpt",
|
||||
},
|
||||
{
|
||||
_id: "1",
|
||||
name: "CustomGPT",
|
||||
value: "chatgptCustom",
|
||||
model: "chatgptCustom",
|
||||
},
|
||||
{
|
||||
_id: "2",
|
||||
name: "BingAI",
|
||||
value: "bingai",
|
||||
model: "bingai",
|
||||
},
|
||||
{
|
||||
_id: "3",
|
||||
name: "Sydney",
|
||||
value: "sydney",
|
||||
model: "sydney",
|
||||
},
|
||||
{
|
||||
_id: "4",
|
||||
name: "ChatGPT",
|
||||
value: "chatgptBrowser",
|
||||
model: "chatgptBrowser",
|
||||
},
|
||||
...get(customGPTModels),
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
const modelsFilter = atom({
|
||||
key: "modelsFilter",
|
||||
default: {
|
||||
chatgpt: false,
|
||||
chatgptCustom: false,
|
||||
bingai: false,
|
||||
sydney: false,
|
||||
chatgptBrowser: false,
|
||||
},
|
||||
});
|
||||
|
||||
const availableModels = selector({
|
||||
key: "availableModels",
|
||||
get: ({ get }) => {
|
||||
const m = get(models);
|
||||
const f = get(modelsFilter);
|
||||
return m.filter(({ model }) => f[model]);
|
||||
},
|
||||
});
|
||||
// const modelAvailable
|
||||
|
||||
export default {
|
||||
customGPTModels,
|
||||
models,
|
||||
modelsFilter,
|
||||
availableModels,
|
||||
};
|
||||
25
client/src/store/search.js
Normal file
25
client/src/store/search.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { atom, selector } from 'recoil';
|
||||
|
||||
const isSearchEnabled = atom({
|
||||
key: 'isSearchEnabled',
|
||||
default: null
|
||||
});
|
||||
|
||||
const searchQuery = atom({
|
||||
key: 'searchQuery',
|
||||
default: ''
|
||||
});
|
||||
|
||||
const isSearching = selector({
|
||||
key: 'isSearching',
|
||||
get: ({ get }) => {
|
||||
const data = get(searchQuery);
|
||||
return !!data;
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
isSearchEnabled,
|
||||
isSearching,
|
||||
searchQuery
|
||||
};
|
||||
37
client/src/store/submission.js
Normal file
37
client/src/store/submission.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
RecoilRoot,
|
||||
atom,
|
||||
selector,
|
||||
useRecoilState,
|
||||
useRecoilValue,
|
||||
useSetRecoilState,
|
||||
} from "recoil";
|
||||
import buildTree from "~/utils/buildTree";
|
||||
|
||||
// current submission
|
||||
// submit any new value to this state will cause new message to be send.
|
||||
// set to null to give up any submission
|
||||
// {
|
||||
// conversation, // target submission, must have: model, chatGptLabel, promptPrefix
|
||||
// messages, // old messages
|
||||
// message, // request message
|
||||
// initialResponse, // response message
|
||||
// isRegenerate=false, // isRegenerate?
|
||||
// }
|
||||
|
||||
const submission = atom({
|
||||
key: "submission",
|
||||
default: null,
|
||||
});
|
||||
|
||||
const isSubmitting = atom({
|
||||
key: "isSubmitting",
|
||||
default: false,
|
||||
});
|
||||
|
||||
export default {
|
||||
submission,
|
||||
isSubmitting,
|
||||
};
|
||||
17
client/src/store/user.js
Normal file
17
client/src/store/user.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import React from "react";
|
||||
import {
|
||||
RecoilRoot,
|
||||
atom,
|
||||
selector,
|
||||
useRecoilState,
|
||||
useRecoilValue,
|
||||
} from "recoil";
|
||||
|
||||
const user = atom({
|
||||
key: "user",
|
||||
default: null,
|
||||
});
|
||||
|
||||
export default {
|
||||
user,
|
||||
};
|
||||
22
client/src/store2/index.js
Normal file
22
client/src/store2/index.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { configureStore } from '@reduxjs/toolkit';
|
||||
|
||||
import convoReducer from './convoSlice.js';
|
||||
import messageReducer from './messageSlice.js';
|
||||
import modelReducer from './modelSlice.js';
|
||||
import submitReducer from './submitSlice.js';
|
||||
import textReducer from './textSlice.js';
|
||||
import userReducer from './userReducer.js';
|
||||
import searchReducer from './searchSlice.js';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
convo: convoReducer,
|
||||
messages: messageReducer,
|
||||
models: modelReducer,
|
||||
text: textReducer,
|
||||
submit: submitReducer,
|
||||
user: userReducer,
|
||||
search: searchReducer
|
||||
},
|
||||
devTools: true
|
||||
});
|
||||
|
|
@ -1,164 +1,123 @@
|
|||
import resetConvo from './resetConvo';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { setNewConvo } from '~/store/convoSlice';
|
||||
import { setMessages } from '~/store/messageSlice';
|
||||
import { setSubmitState, setSubmission } from '~/store/submitSlice';
|
||||
import { setText } from '~/store/textSlice';
|
||||
import { setError } from '~/store/convoSlice';
|
||||
import {v4} from 'uuid';
|
||||
// import resetConvo from './resetConvo';
|
||||
// import { useSelector, useDispatch } from 'react-redux';
|
||||
// import { setNewConvo } from '~/store/convoSlice';
|
||||
// import { setMessages } from '~/store/messageSlice';
|
||||
// import { setSubmitState, setSubmission } from '~/store/submitSlice';
|
||||
// import { setText } from '~/store/textSlice';
|
||||
// import { setError } from '~/store/convoSlice';
|
||||
import { v4 } from 'uuid';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
const useMessageHandler = () => {
|
||||
const dispatch = useDispatch();
|
||||
const convo = useSelector((state) => state.convo);
|
||||
const { initial } = useSelector((state) => state.models);
|
||||
const { messages } = useSelector((state) => state.messages);
|
||||
const { model, chatGptLabel, promptPrefix, isSubmitting } = useSelector((state) => state.submit);
|
||||
const { latestMessage, error } = convo;
|
||||
// const dispatch = useDispatch();
|
||||
// const convo = useSelector((state) => state.convo);
|
||||
// const { initial } = useSelector((state) => state.models);
|
||||
// const { messages } = useSelector((state) => state.messages);
|
||||
// const { model, chatGptLabel, promptPrefix, isSubmitting } = useSelector((state) => state.submit);
|
||||
// const { latestMessage, error } = convo;
|
||||
|
||||
const ask = ({ text, parentMessageId=null, conversationId=null, messageId=null}, { isRegenerate=false }={}) => {
|
||||
if (error) {
|
||||
dispatch(setError(false));
|
||||
}
|
||||
const [currentConversation, setCurrentConversation] = useRecoilState(store.conversation) || {};
|
||||
const setSubmission = useSetRecoilState(store.submission);
|
||||
const isSubmitting = useRecoilValue(store.isSubmitting);
|
||||
|
||||
const latestMessage = useRecoilValue(store.latestMessage);
|
||||
const { error } = currentConversation;
|
||||
|
||||
const [messages, setMessages] = useRecoilState(store.messages);
|
||||
|
||||
const ask = (
|
||||
{ text, parentMessageId = null, conversationId = null, messageId = null },
|
||||
{ isRegenerate = false } = {}
|
||||
) => {
|
||||
if (!!isSubmitting || text === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// determine the model to be used
|
||||
const { model = null, chatGptLabel = null, promptPrefix = null } = currentConversation;
|
||||
|
||||
// construct the query message
|
||||
// this is not a real messageId, it is used as placeholder before real messageId returned
|
||||
text = text.trim();
|
||||
const fakeMessageId = v4();
|
||||
const isCustomModel = model === 'chatgptCustom' || !initial[model];
|
||||
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
|
||||
// const isCustomModel = model === 'chatgptCustom' || !initial[model];
|
||||
// const sender = model === 'chatgptCustom' ? chatGptLabel : model;
|
||||
parentMessageId = parentMessageId || latestMessage?.messageId || '00000000-0000-0000-0000-000000000000';
|
||||
let currentMessages = messages;
|
||||
if (resetConvo(currentMessages, sender)) {
|
||||
parentMessageId = '00000000-0000-0000-0000-000000000000';
|
||||
conversationId = null;
|
||||
dispatch(setNewConvo());
|
||||
currentMessages = [];
|
||||
conversationId = conversationId || currentConversation?.conversationId;
|
||||
if (conversationId == 'search') {
|
||||
console.error('cannot send any message under search view!');
|
||||
return;
|
||||
}
|
||||
const currentMsg = { sender: 'User', text, current: true, isCreatedByUser: true, parentMessageId, conversationId, messageId: fakeMessageId };
|
||||
const initialResponse = { sender, text: '', parentMessageId: isRegenerate?messageId:fakeMessageId, messageId: (isRegenerate?messageId:fakeMessageId) + '_', submitting: true };
|
||||
if (conversationId == 'new') {
|
||||
parentMessageId = '00000000-0000-0000-0000-000000000000';
|
||||
currentMessages = [];
|
||||
conversationId = null;
|
||||
}
|
||||
const currentMsg = {
|
||||
sender: 'User',
|
||||
text,
|
||||
current: true,
|
||||
isCreatedByUser: true,
|
||||
parentMessageId,
|
||||
conversationId,
|
||||
messageId: fakeMessageId
|
||||
};
|
||||
|
||||
// construct the placeholder response message
|
||||
const initialResponse = {
|
||||
sender: chatGptLabel || model,
|
||||
text: '',
|
||||
parentMessageId: isRegenerate ? messageId : fakeMessageId,
|
||||
messageId: (isRegenerate ? messageId : fakeMessageId) + '_',
|
||||
conversationId,
|
||||
submitting: true
|
||||
};
|
||||
|
||||
const submission = {
|
||||
convo,
|
||||
isCustomModel,
|
||||
message: {
|
||||
conversation: {
|
||||
...currentConversation,
|
||||
conversationId,
|
||||
model,
|
||||
chatGptLabel,
|
||||
promptPrefix
|
||||
},
|
||||
message: {
|
||||
...currentMsg,
|
||||
model,
|
||||
chatGptLabel,
|
||||
promptPrefix,
|
||||
overrideParentMessageId: isRegenerate?messageId:null
|
||||
overrideParentMessageId: isRegenerate ? messageId : null
|
||||
},
|
||||
messages: currentMessages,
|
||||
isRegenerate,
|
||||
initialResponse,
|
||||
sender,
|
||||
initialResponse
|
||||
};
|
||||
|
||||
console.log('User Input:', text);
|
||||
|
||||
if (isRegenerate) {
|
||||
dispatch(setMessages([...currentMessages, initialResponse]));
|
||||
setMessages([...currentMessages, initialResponse]);
|
||||
} else {
|
||||
dispatch(setMessages([...currentMessages, currentMsg, initialResponse]));
|
||||
dispatch(setText(''));
|
||||
setMessages([...currentMessages, currentMsg, initialResponse]);
|
||||
}
|
||||
dispatch(setSubmitState(true));
|
||||
dispatch(setSubmission(submission));
|
||||
}
|
||||
setSubmission(submission);
|
||||
};
|
||||
|
||||
const regenerate = ({ parentMessageId }) => {
|
||||
const parentMessage = messages?.find(element => element.messageId == parentMessageId);
|
||||
|
||||
if (parentMessage && parentMessage.isCreatedByUser)
|
||||
ask({ ...parentMessage }, { isRegenerate: true })
|
||||
else
|
||||
console.error('Failed to regenerate the message: parentMessage not found or not created by user.');
|
||||
}
|
||||
if (parentMessage && parentMessage.isCreatedByUser) ask({ ...parentMessage }, { isRegenerate: true });
|
||||
else console.error('Failed to regenerate the message: parentMessage not found or not created by user.');
|
||||
};
|
||||
|
||||
const stopGenerating = () => {
|
||||
dispatch(setSubmission({}));
|
||||
}
|
||||
setSubmission(null);
|
||||
};
|
||||
|
||||
return { ask, regenerate, stopGenerating }
|
||||
}
|
||||
return { ask, regenerate, stopGenerating };
|
||||
};
|
||||
|
||||
export { useMessageHandler };
|
||||
|
||||
// deprecated
|
||||
// export default function handleSubmit({
|
||||
// model,
|
||||
// text,
|
||||
// convo,
|
||||
// messageHandler,
|
||||
// convoHandler,
|
||||
// errorHandler,
|
||||
// chatGptLabel,
|
||||
// promptPrefix
|
||||
// }) {
|
||||
// const endpoint = `/api/ask`;
|
||||
// let payload = { model, text, chatGptLabel, promptPrefix };
|
||||
// if (convo.conversationId && convo.parentMessageId) {
|
||||
// payload = {
|
||||
// ...payload,
|
||||
// conversationId: convo.conversationId,
|
||||
// parentMessageId: convo.parentMessageId
|
||||
// };
|
||||
// }
|
||||
|
||||
// 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;
|
||||
|
||||
// const events = new SSE(server, {
|
||||
// payload: JSON.stringify(payload),
|
||||
// headers: { 'Content-Type': 'application/json' }
|
||||
// });
|
||||
|
||||
// events.onopen = function () {
|
||||
// console.log('connection is opened');
|
||||
// };
|
||||
|
||||
// events.onmessage = function (e) {
|
||||
// const data = JSON.parse(e.data);
|
||||
// let text = data.text || data.response;
|
||||
// if (data.message) {
|
||||
// messageHandler(text, events);
|
||||
// }
|
||||
|
||||
// if (data.final) {
|
||||
// convoHandler(data);
|
||||
// console.log('final', data);
|
||||
// } else {
|
||||
// // console.log('dataStream', data);
|
||||
// }
|
||||
// };
|
||||
|
||||
// events.onerror = function (e) {
|
||||
// console.log('error in opening conn.');
|
||||
// events.close();
|
||||
// errorHandler(e);
|
||||
// };
|
||||
|
||||
// events.addEventListener('stop', () => {
|
||||
// // Close the SSE stream
|
||||
// console.log('stop event received');
|
||||
// events.close();
|
||||
// });
|
||||
|
||||
// events.stream();
|
||||
// }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue