From c53778e7c15167c5878d3deca91ef19e5b6f41ac Mon Sep 17 00:00:00 2001 From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech> Date: Fri, 31 Mar 2023 00:20:45 +0800 Subject: [PATCH] feat: new endpoint-style structure in client side NOT FINISHED. model menu and icon and send message not work. --- .../components/Conversations/Conversation.jsx | 87 +-------------- client/src/components/Input/index.jsx | 4 +- .../src/components/Messages/HoverButtons.jsx | 15 +-- client/src/components/Messages/Message.jsx | 36 ++---- client/src/components/Messages/MessageBar.jsx | 38 ------- client/src/components/Messages/index.jsx | 25 ++++- client/src/store/conversation.js | 105 +++++------------- client/src/store/endpoints.js | 26 +++++ client/src/utils/getDefaultConversation.js | 77 +++++++++++++ client/src/utils/index.js | 2 +- 10 files changed, 172 insertions(+), 243 deletions(-) delete mode 100644 client/src/components/Messages/MessageBar.jsx create mode 100644 client/src/store/endpoints.js create mode 100644 client/src/utils/getDefaultConversation.js diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index 59128f6244..f3327861da 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -1,5 +1,5 @@ import React, { useState, useRef } from 'react'; -import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; +import { useRecoilState, useSetRecoilState } from 'recoil'; import RenameButton from './RenameButton'; import DeleteButton from './DeleteButton'; @@ -10,9 +10,7 @@ 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(); const { switchToConversation } = store.useConversation(); @@ -21,33 +19,10 @@ export default function Conversation({ conversation, retainView }) { const [titleInput, setTitleInput] = useState(title); const inputRef = useRef(null); - const { - model, - parentMessageId, - conversationId, - title, - chatGptLabel = null, - promptPrefix = null, - jailbreakConversationId, - conversationSignature, - clientId, - invocationId, - toneStyle - } = conversation; + const { conversationId, title } = 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 (currentConversation?.conversationId === conversationId) { return; @@ -58,64 +33,6 @@ export default function Conversation({ conversation, retainView }) { // set conversation to the new conversation switchToConversation(conversation); - - // if (!stopStream) { - // dispatch(setStopStream(true)); - // dispatch(setSubmission({})); - // } - // dispatch(setEmptyMessage()); - - // const convo = { title, error: false, conversationId: id, chatGptLabel, promptPrefix }; - - // 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 => { diff --git a/client/src/components/Input/index.jsx b/client/src/components/Input/index.jsx index 9412093dec..4b3a268d5d 100644 --- a/client/src/components/Input/index.jsx +++ b/client/src/components/Input/index.jsx @@ -3,7 +3,7 @@ import { useRecoilValue, useRecoilState } from 'recoil'; import SubmitButton from './SubmitButton'; import AdjustToneButton from './AdjustToneButton'; import BingStyles from './BingStyles'; -import ModelMenu from './Models/ModelMenu'; +// import ModelMenu from './Models/ModelMenu'; import Footer from './Footer'; import TextareaAutosize from 'react-textarea-autosize'; import RegenerateIcon from '../svg/RegenerateIcon'; @@ -167,7 +167,7 @@ export default function TextChat({ isSearchView = false }) { disabled ? 'dark:bg-gray-900' : 'dark:bg-gray-700' } dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] md:py-3 md:pl-4`} > - + {/* */} - {(visible&&enabled)?( + {visible && enabled ? ( <> - - ):null} + ) : null} {/* */} diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx index fd4b7ef02d..47b56ec2cb 100644 --- a/client/src/components/Messages/Message.jsx +++ b/client/src/components/Messages/Message.jsx @@ -6,7 +6,7 @@ import MultiMessage from './MultiMessage'; import HoverButtons from './HoverButtons'; import SiblingSwitch from './SiblingSwitch'; import { fetchById } from '~/utils/fetchers'; -import { getIconOfModel } from '~/utils'; +import { getIconOfAi } from '~/utils'; import { useMessageHandler } from '~/utils/handleSubmit'; import store from '~/store'; @@ -23,32 +23,15 @@ export default function Message({ }) { const isSubmitting = useRecoilValue(store.isSubmitting); const setLatestMessage = useSetRecoilState(store.latestMessage); - const { model, chatGptLabel, promptPrefix } = conversation; + // const { model, chatGptLabel, promptPrefix } = conversation; const [abortScroll, setAbort] = useState(false); - const { - sender, - text, - searchResult, - isCreatedByUser, - error, - submitting, - model: messageModel, - chatGptLabel: messageChatGptLabel, - searchResult: isSearchResult - } = message; + const { text, searchResult, isCreatedByUser, error, submitting } = message; const textEditor = useRef(null); const last = !message?.children?.length; const edit = message.messageId == currentEditId; const { ask } = useMessageHandler(); const { switchToConversation } = store.useConversation(); const blinker = submitting && isSubmitting; - const generateCursor = useCallback(() => { - if (!blinker) { - return ''; - } - - return ; - }, [blinker]); useEffect(() => { if (blinker && !abortScroll) { @@ -77,14 +60,9 @@ export default function Message({ '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: isSearchResult ? messageModel : model, - searchResult, - chatGptLabel: isSearchResult ? messageChatGptLabel : chatGptLabel, - promptPrefix, - error + const icon = getIconOfAi({ + ...conversation, + ...message }); if (!isCreatedByUser) @@ -199,7 +177,7 @@ export default function Message({ )} enterEdit()} /> diff --git a/client/src/components/Messages/MessageBar.jsx b/client/src/components/Messages/MessageBar.jsx deleted file mode 100644 index 76611e52a3..0000000000 --- a/client/src/components/Messages/MessageBar.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useRef, useEffect, useState } from 'react'; - -const MessageBar = ({ children, dynamicProps, handleWheel, clickSearchResult }) => { - const ref = useRef(null); - const [isVisible, setIsVisible] = useState(false); - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - setIsVisible(true); - observer.unobserve(ref.current); - } - }, - { threshold: 0.1 } - ); - - observer.observe(ref.current); - - return () => { - observer.unobserve(ref.current); - }; - }, []); - - return ( -
- {isVisible ? children : null} -
- ); -}; - -export default MessageBar; diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx index b112fff57e..e74ff8ba71 100644 --- a/client/src/components/Messages/index.jsx +++ b/client/src/components/Messages/index.jsx @@ -20,10 +20,10 @@ export default function Messages({ isSearchView = false }) { const _messagesTree = isSearchView ? searchResultMessagesTree : messagesTree; const conversation = useRecoilValue(store.conversation) || {}; - const { conversationId, model, chatGptLabel, toneStyle } = conversation; + const { conversationId, endpoint } = conversation; - const models = useRecoilValue(store.models) || []; - const modelName = models.find(element => element.model == model)?.name; + // const models = useRecoilValue(store.models) || []; + // const modelName = models.find(element => element.model == model)?.name; const searchQuery = useRecoilValue(store.searchQuery); @@ -82,9 +82,22 @@ export default function Messages({ isSearchView = false }) { const getConversationTitle = () => { if (isSearchView) return `Search: ${searchQuery}`; else { - let _title = `Model: ${modelName}`; - if (chatGptLabel) _title += ` of ${chatGptLabel}`; - if (toneStyle) _title += ` (${toneStyle})`; + let _title = `${endpoint}`; + + if (endpoint === 'azureOpenAI' || endpoint === 'openAI') { + const { chatGptLabel, model } = conversation; + if (model) _title += `: ${model}`; + if (chatGptLabel) _title += ` as ${chatGptLabel}`; + } else if (endpoint === 'bingAI') { + const { jailbreak, toneStyle } = conversation; + if (toneStyle) _title += `: ${toneStyle}`; + if (jailbreak) _title += ` as Sydney`; + } else if (endpoint === 'chatGPTBrowser') { + const { model } = conversation; + if (model) _title += `: ${model}`; + } else if (endpoint === null) { + null; + } return _title; } }; diff --git a/client/src/store/conversation.js b/client/src/store/conversation.js index b59aef4af5..c06e6834f0 100644 --- a/client/src/store/conversation.js +++ b/client/src/store/conversation.js @@ -1,23 +1,34 @@ -import models from './models'; +import endpoints from './endpoints'; import { atom, selector, useSetRecoilState, useResetRecoilState, useRecoilCallback } from 'recoil'; import buildTree from '~/utils/buildTree'; +import getDefaultConversation from '~/utils/getDefaultConversation'; // current conversation, can be null (need to be fetched from server) // sample structure // { -// conversationId: "new", -// title: "New Chat", +// conversationId: 'new', +// title: 'New Chat', +// user: null, +// // endpoint: [azureOpenAI, openAI, bingAI, chatGPTBrowser] +// endpoint: 'azureOpenAI', +// // for azureOpenAI, openAI, chatGPTBrowser only +// model: 'gpt-3.5-turbo', +// // for azureOpenAI, openAI only +// chatGptLabel: null, +// promptPrefix: null, +// temperature: 0.8, +// top_p: 1, +// presence_penalty: 1, +// // for bingAI only +// jailbreak: false, // jailbreakConversationId: null, // conversationSignature: null, // clientId: null, // invocationId: null, -// model: "chatgpt", -// chatGptLabel: null, -// promptPrefix: null, -// user: null, -// suggestions: [], // toneStyle: null, -// } +// suggestions: [] +// }; + const conversation = atom({ key: 'conversation', default: null @@ -52,8 +63,8 @@ const useConversation = () => { ({ snapshot }) => async (_conversation, messages = null) => { const prevConversation = await snapshot.getPromise(conversation); - const prevModelsFilter = await snapshot.getPromise(models.modelsFilter); - _switchToConversation(_conversation, messages, { prevModelsFilter, prevConversation }); + const availableEndpoints = await snapshot.getPromise(endpoints.availableEndpoints); + _switchToConversation(_conversation, messages, { availableEndpoints, prevConversation }); }, [] ); @@ -61,71 +72,25 @@ const useConversation = () => { const _switchToConversation = ( conversation, messages = null, - { prevModelsFilter = {}, prev_conversation = {} } + { availableEndpoints = [], prevConversation = {} } ) => { - let { model = null, chatGptLabel = null, promptPrefix = null } = conversation; - const getDefaultModel = () => { - try { - // try to use current model - const { _model = null, _chatGptLabel = null, _promptPrefix = null } = prev_conversation || {}; - if (prevModelsFilter[_model]) { - model = _model; - chatGptLabel = _chatGptLabel; - promptPrefix = _promptPrefix; - return; - } - } catch (error) {} + let { endpoint = null } = conversation; - 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 (prevModelsFilter[_model]) { - model = _model; - chatGptLabel = _chatGptLabel; - promptPrefix = _promptPrefix; - return; - } - } catch (error) {} - - // if anything happens, reset to default model - if (prevModelsFilter?.chatgpt) model = 'chatgpt'; - else if (prevModelsFilter?.bingai) model = 'bingai'; - else if (prevModelsFilter?.chatgptBrowser) model = 'chatgptBrowser'; - chatGptLabel = null; - promptPrefix = null; - }; - - if (model === null) + if (endpoint === null) // get the default model - getDefaultModel(); + conversation = getDefaultConversation({ conversation, availableEndpoints, prevConversation }); - setConversation({ - ...conversation, - model: model, - chatGptLabel: chatGptLabel, - promptPrefix: promptPrefix - }); + setConversation(conversation); setMessages(messages); resetLatestMessage(); }; - const newConversation = ({ model = null, chatGptLabel = null, promptPrefix = null } = {}) => { + const newConversation = (template = {}) => { switchToConversation( { conversationId: 'new', title: 'New Chat', - jailbreakConversationId: null, - conversationSignature: null, - clientId: null, - invocationId: null, - model: model, - chatGptLabel: chatGptLabel, - promptPrefix: promptPrefix, - user: null, - suggestions: [], - toneStyle: null + ...template }, [] ); @@ -135,17 +100,7 @@ const useConversation = () => { switchToConversation( { conversationId: 'search', - title: 'Search', - jailbreakConversationId: null, - conversationSignature: null, - clientId: null, - invocationId: null, - model: null, - chatGptLabel: null, - promptPrefix: null, - user: null, - suggestions: [], - toneStyle: null + title: 'Search' }, [] ); diff --git a/client/src/store/endpoints.js b/client/src/store/endpoints.js new file mode 100644 index 0000000000..e4f10ce195 --- /dev/null +++ b/client/src/store/endpoints.js @@ -0,0 +1,26 @@ +import { atom, selector } from 'recoil'; + +const endpointsFilter = atom({ + key: 'endpointsFilter', + default: { + azureOpenAI: false, + openAI: false, + bingAI: false, + chatGPTBrowser: false + } +}); + +const availableEndpoints = selector({ + key: 'availableEndpoints', + get: ({ get }) => { + const endpoints = ['azureOpenAI', 'openAI', 'bingAI', 'chatGPTBrowser']; + const f = get(endpointsFilter); + return endpoints.filter(endpoint => f[endpoint]); + } +}); +// const modelAvailable + +export default { + endpointsFilter, + availableEndpoints +}; diff --git a/client/src/utils/getDefaultConversation.js b/client/src/utils/getDefaultConversation.js new file mode 100644 index 0000000000..3fb35d4ad0 --- /dev/null +++ b/client/src/utils/getDefaultConversation.js @@ -0,0 +1,77 @@ +const buildDefaultConversation = ({ conversation, endpoint, lastConversationSetup = {} }) => { + if (endpoint === 'azureOpenAI' || endpoint === 'openAI') { + conversation = { + ...conversation, + endpoint, + model: lastConversationSetup?.model || 'gpt-3.5-turbo', + chatGptLabel: lastConversationSetup?.chatGptLabel || null, + promptPrefix: lastConversationSetup?.promptPrefix || null, + temperature: lastConversationSetup?.temperature || 0.8, + top_p: lastConversationSetup?.top_p || 1, + presence_penalty: lastConversationSetup?.presence_penalty || 1 + }; + } else if (endpoint === 'bingAI') { + conversation = { + ...conversation, + endpoint, + jailbreak: lastConversationSetup?.jailbreak || false, + jailbreakConversationId: lastConversationSetup?.jailbreakConversationId || null, + conversationSignature: lastConversationSetup?.conversationSignature || null, + clientId: lastConversationSetup?.clientId || null, + invocationId: lastConversationSetup?.invocationId || null, + toneStyle: lastConversationSetup?.toneStyle || null, + suggestions: lastConversationSetup?.suggestions || [] + }; + } else if (endpoint === 'chatGPTBrowser') { + conversation = { + ...conversation, + endpoint, + model: lastConversationSetup?.model || 'text-davinci-002-render-sha' + }; + } else if (endpoint === null) { + conversation = { + ...conversation, + endpoint + }; + } + + return conversation; +}; + +const getDefaultConversation = ({ conversation, prevConversation, availableEndpoints }) => { + try { + // try to use current model + const { endpoint = null } = prevConversation || {}; + if (endpoint in availableEndpoints) { + conversation = buildDefaultConversation({ + conversation, + endpoint, + lastConversationSetup: prevConversation + }); + return conversation; + } + } catch (error) {} + + try { + // try to read latest selected model from local storage + const lastConversationSetup = JSON.parse(localStorage.getItem('lastConversationSetup')); + const { endpoint = null } = lastConversationSetup; + + if (endpoint in availableEndpoints) { + conversation = buildDefaultConversation({ conversation, endpoint, lastConversationSetup }); + return conversation; + } + } catch (error) {} + + // if anything happens, reset to default model + const endpoint = availableEndpoints?.[0]; + if (endpoint) { + conversation = buildDefaultConversation({ conversation, endpoint }); + return conversation; + } else { + conversation = buildDefaultConversation({ conversation, endpoint: null }); + return conversation; + } +}; + +export default getDefaultConversation; diff --git a/client/src/utils/index.js b/client/src/utils/index.js index 4ff806ebaa..2e402e4735 100644 --- a/client/src/utils/index.js +++ b/client/src/utils/index.js @@ -38,7 +38,7 @@ export const languages = [ 'pascal' ]; -export const getIconOfModel = ({ +export const getIconOfAi = ({ size = 30, sender, isCreatedByUser,