Merge pull request #173 from danny-avila/feat-new-titleconvo

Feat new titleconvo
This commit is contained in:
Danny Avila 2023-04-07 20:50:14 -04:00 committed by GitHub
commit 625f63b072
11 changed files with 98 additions and 101 deletions

View file

@ -41,6 +41,11 @@ OPENAI_KEY=
# Leave it and BINGAI_USER_TOKEN blank to disable this endpoint. # Leave it and BINGAI_USER_TOKEN blank to disable this endpoint.
BINGAI_TOKEN= BINGAI_TOKEN=
# BingAI Host:
# Necessary for some people in different countries, e.g. China (https://cn.bing.com)
# Leave it blank to use default server.
# BINGAI_HOST="https://cn.bing.com"
# BingAI User defined Token # BingAI User defined Token
# Allow user to set their own token by client # Allow user to set their own token by client
# Uncomment this to enable this feature. # Uncomment this to enable this feature.

View file

@ -27,6 +27,7 @@ const askBing = async ({
// cookies: '', // cookies: '',
debug: false, debug: false,
cache: store, cache: store,
host: process.env.BINGAI_HOST || null,
proxy: process.env.PROXY || null proxy: process.env.PROXY || null
}); });

View file

@ -18,49 +18,38 @@ const proxyEnvToAxiosProxy = proxyString => {
const titleConvo = async ({ endpoint, text, response }) => { const titleConvo = async ({ endpoint, text, response }) => {
let title = 'New Chat'; let title = 'New Chat';
const messages = [ const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default;
{
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, using the same language. The requirement are: 1. If possible, generate in 5 words or less, 2. Using title case, 3. must give the title using the language as the user said. 4. Don't refer to the participants of the conversation. 5. Do not include punctuation or quotation marks. 6. Your response should be in title case, exclusively containing the title. 7. don't say anything except the title.
`Detect user language and write in the same language an extremely concise title for this conversation, which you must accurately detect. Write in the detected language. Title in 5 Words or Less. No Punctuation/Quotation. All first letters of every word should be capitalized and complete only the title in User Language only.
||>User:
"${text}"
||>Response:
"${JSON.stringify(response?.text)}"
||>Title:`
}
// {
// role: 'user',
// content: `User:\n "${text}"\n\n${model}: \n"${JSON.stringify(response?.text)}"\n\n`
// }
];
// console.log('Title Prompt', messages[0]);
const request = {
model: 'gpt-3.5-turbo',
messages,
temperature: 0,
presence_penalty: 0,
frequency_penalty: 0
};
// console.log('REQUEST', request);
try { try {
const configuration = new Configuration({ const instructionsPayload = {
apiKey: process.env.OPENAI_KEY role: 'system',
}); content: `Detect user language and write in the same language an extremely concise title for this conversation, which you must accurately detect. Write in the detected language. Title in 5 Words or Less. No Punctuation or Quotation. All first letters of every word should be capitalized and complete only the title in User Language only.
const openai = new OpenAIApi(configuration);
const completion = await openai.createChatCompletion(request, {
proxy: proxyEnvToAxiosProxy(process.env.PROXY || null)
});
//eslint-disable-next-line ||>User:
title = completion.data.choices[0].message.content.replace(/["\.]/g, ''); "${text}"
||>Response:
"${JSON.stringify(response?.text)}"
||>Title:`
};
const options = {
reverseProxyUrl: process.env.OPENAI_REVERSE_PROXY || null,
proxy: process.env.PROXY || null
};
const titleGenClientOptions = JSON.parse(JSON.stringify(options));
titleGenClientOptions.modelOptions = {
model: 'gpt-3.5-turbo',
temperature: 0,
presence_penalty: 0,
frequency_penalty: 0
};
const titleGenClient = new ChatGPTClient(process.env.OPENAI_KEY, titleGenClientOptions);
const result = await titleGenClient.getCompletion([instructionsPayload], null);
title = result.choices[0].message.content.replace(/\s+/g, ' ').trim();
} catch (e) { } catch (e) {
console.error(e); console.error(e);
console.log('There was an issue generating title, see error above'); console.log('There was an issue generating title, see error above');

View file

@ -35,21 +35,21 @@ router.post('/', async (req, res) => {
let endpointOption = {}; let endpointOption = {};
if (req.body?.jailbreak) if (req.body?.jailbreak)
endpointOption = { endpointOption = {
jailbreak: req.body?.jailbreak || false, jailbreak: req.body?.jailbreak ?? false,
jailbreakConversationId: req.body?.jailbreakConversationId || null, jailbreakConversationId: req.body?.jailbreakConversationId ?? null,
systemMessage: req.body?.systemMessage || null, systemMessage: req.body?.systemMessage ?? null,
context: req.body?.context || null, context: req.body?.context ?? null,
toneStyle: req.body?.toneStyle || 'fast' toneStyle: req.body?.toneStyle ?? 'fast'
}; };
else else
endpointOption = { endpointOption = {
jailbreak: req.body?.jailbreak || false, jailbreak: req.body?.jailbreak ?? false,
systemMessage: req.body?.systemMessage || null, systemMessage: req.body?.systemMessage ?? null,
context: req.body?.context || null, context: req.body?.context ?? null,
conversationSignature: req.body?.conversationSignature || null, conversationSignature: req.body?.conversationSignature ?? null,
clientId: req.body?.clientId || null, clientId: req.body?.clientId ?? null,
invocationId: req.body?.invocationId || null, invocationId: req.body?.invocationId ?? null,
toneStyle: req.body?.toneStyle || 'fast' toneStyle: req.body?.toneStyle ?? 'fast'
}; };
console.log('ask log', { console.log('ask log', {
@ -122,31 +122,23 @@ const ask = async ({
console.log('BING RESPONSE', response); console.log('BING RESPONSE', response);
const newConversationId = endpointOption?.jailbreak
? response.jailbreakConversationId
: response.conversationId || conversationId;
const newUserMassageId = response.parentMessageId || response.details.requestId || userMessageId;
const newResponseMessageId = response.messageId || response.details.messageId;
// STEP1 generate response message // STEP1 generate response message
response.text = response.response || response.details.spokenText || '**Bing refused to answer.**'; response.text = response.response || response.details.spokenText || '**Bing refused to answer.**';
let responseMessage = { let responseMessage = {
conversationId: newConversationId,
messageId: newResponseMessageId,
parentMessageId: overrideParentMessageId || newUserMassageId,
sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI',
text: await handleText(response, true), text: await handleText(response, true),
suggestions: suggestions: response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text)
response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text),
jailbreak: endpointOption?.jailbreak
}; };
// // response.text = await handleText(response, true);
// response.suggestions =
// response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text);
if (endpointOption?.jailbreak) {
responseMessage.conversationId = response.jailbreakConversationId;
responseMessage.messageId = response.messageId || response.details.messageId;
responseMessage.parentMessageId = overrideParentMessageId || response.parentMessageId || userMessageId;
responseMessage.sender = 'Sydney';
} else {
responseMessage.conversationId = response.conversationId;
responseMessage.messageId = response.messageId || response.details.messageId;
responseMessage.parentMessageId =
overrideParentMessageId || response.parentMessageId || response.details.requestId || userMessageId;
responseMessage.sender = 'BingAI';
}
await saveMessage(responseMessage); await saveMessage(responseMessage);
@ -159,14 +151,22 @@ const ask = async ({
// Attition: the api will also create new conversationId while using invalid userMessage.parentMessageId, // 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. // but in this situation, don't change the conversationId, but create new convo.
let conversationUpdate = { conversationId, endpoint: 'bingAI' }; let conversationUpdate = { conversationId: newConversationId, endpoint: 'bingAI' };
if (conversationId != responseMessage.conversationId && isNewConversation) if (conversationId != newConversationId)
conversationUpdate = { if (isNewConversation) {
...conversationUpdate, // change the conversationId to new one
conversationId: conversationId, conversationUpdate = {
newConversationId: responseMessage.conversationId || conversationId ...conversationUpdate,
}; conversationId: conversationId,
conversationId = responseMessage.conversationId || conversationId; newConversationId: newConversationId
};
} else {
// create new conversation
conversationUpdate = {
...conversationUpdate,
...endpointOption
};
}
if (endpointOption?.jailbreak) { if (endpointOption?.jailbreak) {
conversationUpdate.jailbreak = true; conversationUpdate.jailbreak = true;
@ -179,17 +179,16 @@ const ask = async ({
} }
await saveConvo(req?.session?.user?.username, conversationUpdate); await saveConvo(req?.session?.user?.username, conversationUpdate);
conversationId = newConversationId;
// STEP3 update the user message // STEP3 update the user message
userMessage.conversationId = conversationId; userMessage.conversationId = newConversationId;
userMessage.messageId = responseMessage.parentMessageId; userMessage.messageId = newUserMassageId;
// If response has parentMessageId, the fake userMessage.messageId should be updated to the real one. // If response has parentMessageId, the fake userMessage.messageId should be updated to the real one.
if (!overrideParentMessageId) { if (!overrideParentMessageId)
const oldUserMessageId = userMessageId; await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId });
await saveMessage({ ...userMessage, messageId: oldUserMessageId, newMessageId: userMessage.messageId }); userMessageId = newUserMassageId;
}
userMessageId = userMessage.messageId;
sendMessage(res, { sendMessage(res, {
title: await getConvoTitle(req?.session?.user?.username, conversationId), title: await getConvoTitle(req?.session?.user?.username, conversationId),
@ -223,4 +222,4 @@ const ask = async ({
} }
}; };
module.exports = router; module.exports = router;

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import EndpointItem from './EndpointItem'; import EndpointItem from './EndpointItem.jsx';
export default function EndpointItems({ endpoints, onSelect }) { export default function EndpointItems({ endpoints, onSelect }) {
return ( return (

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { DropdownMenuRadioItem } from '../../ui/DropdownMenu.tsx'; import { DropdownMenuRadioItem } from '../../ui/DropdownMenu.tsx';
import EditIcon from '../../svg/EditIcon'; import EditIcon from '../../svg/EditIcon.jsx';
import TrashIcon from '../../svg/TrashIcon'; import TrashIcon from '../../svg/TrashIcon.jsx';
import getIcon from '~/utils/getIcon'; import getIcon from '~/utils/getIcon';
export default function PresetItem({ preset = {}, value, onSelect, onChangePreset, onDeletePreset }) { export default function PresetItem({ preset = {}, value, onSelect, onChangePreset, onDeletePreset }) {
@ -56,7 +56,7 @@ export default function PresetItem({ preset = {}, value, onSelect, onChangePrese
/> */} /> */}
<div className="flex w-4 flex-1" /> <div className="flex w-4 flex-1" />
<button <button
className="invisible m-0 p-2 mr-1 rounded-md text-gray-400 hover:text-gray-700 group-hover:visible dark:text-gray-400 dark:hover:text-gray-200 " className="invisible m-0 mr-1 rounded-md p-2 text-gray-400 hover:text-gray-700 group-hover:visible dark:text-gray-400 dark:hover:text-gray-200 "
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
onChangePreset(preset); onChangePreset(preset);

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import PresetItem from './PresetItem'; import PresetItem from './PresetItem.jsx';
export default function PresetItems({ presets, onSelect, onChangePreset, onDeletePreset }) { export default function PresetItems({ presets, onSelect, onChangePreset, onDeletePreset }) {
return ( return (

View file

@ -1,9 +1,9 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useRecoilValue, useRecoilState } from 'recoil'; import { useRecoilValue, useRecoilState } from 'recoil';
import EditPresetDialog from '../../Endpoints/EditPresetDialog'; import EditPresetDialog from '../../Endpoints/EditPresetDialog.jsx';
import EndpointItems from './EndpointItems'; import EndpointItems from './EndpointItems.jsx';
import PresetItems from './PresetItems'; import PresetItems from './PresetItems.jsx';
import FileUpload from './FileUpload'; import FileUpload from './FileUpload.jsx';
import getIcon from '~/utils/getIcon'; import getIcon from '~/utils/getIcon';
import manualSWR, { handleFileSelected } from '~/utils/fetchers'; import manualSWR, { handleFileSelected } from '~/utils/fetchers';
@ -17,7 +17,7 @@ import {
DropdownMenuTrigger DropdownMenuTrigger
} from '../../ui/DropdownMenu.tsx'; } from '../../ui/DropdownMenu.tsx';
import { Dialog, DialogTrigger } from '../../ui/Dialog.tsx'; import { Dialog, DialogTrigger } from '../../ui/Dialog.tsx';
import DialogTemplate from '../../ui/DialogTemplate'; import DialogTemplate from '../../ui/DialogTemplate.jsx';
import store from '~/store'; import store from '~/store';
@ -110,9 +110,12 @@ export default function NewConversationMenu() {
<Button <Button
variant="outline" variant="outline"
// style={{backgroundColor: 'rgb(16, 163, 127)'}} // style={{backgroundColor: 'rgb(16, 163, 127)'}}
className={`absolute top-[0.25px] mb-0 ml-1 items-center rounded-md border-0 p-1 outline-none focus:ring-0 focus:ring-offset-0 disabled:top-[0.25px] dark:data-[state=open]:bg-opacity-50 md:top-1 md:left-1 md:ml-0 md:pl-1 md:disabled:top-1`} className={`group relative mt-[-8px] mb-[-12px] ml-0 items-center rounded-md border-0 p-1 outline-none focus:ring-0 focus:ring-offset-0 dark:data-[state=open]:bg-opacity-50 md:left-1 md:ml-[-12px] md:pl-1`}
> >
{icon} {icon}
<span className="max-w-0 overflow-hidden whitespace-nowrap px-0 text-slate-600 transition-all group-hover:max-w-[80px] group-hover:px-2 group-data-[state=open]:max-w-[80px] group-data-[state=open]:px-2 dark:text-slate-300">
New Topic
</span>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent <DropdownMenuContent

View file

@ -5,7 +5,7 @@ import OpenAIOptions from './OpenAIOptions';
import ChatGPTOptions from './ChatGPTOptions'; import ChatGPTOptions from './ChatGPTOptions';
import BingAIOptions from './BingAIOptions'; import BingAIOptions from './BingAIOptions';
// import BingStyles from './BingStyles'; // import BingStyles from './BingStyles';
import NewConversationMenu from './Endpoints/NewConversationMenu'; import NewConversationMenu from './NewConversationMenu';
import Footer from './Footer'; import Footer from './Footer';
import TextareaAutosize from 'react-textarea-autosize'; import TextareaAutosize from 'react-textarea-autosize';
import { useMessageHandler } from '../../utils/handleSubmit'; import { useMessageHandler } from '../../utils/handleSubmit';
@ -139,7 +139,7 @@ export default function TextChat({ isSearchView = false }) {
<form className="stretch mx-2 flex flex-row gap-3 last:mb-2 md:pt-2 md:last:mb-6 lg:mx-auto lg:max-w-3xl lg:pt-6"> <form className="stretch mx-2 flex flex-row gap-3 last:mb-2 md:pt-2 md:last:mb-6 lg:mx-auto lg:max-w-3xl lg:pt-6">
<div className="relative flex h-full flex-1 md:flex-col"> <div className="relative flex h-full flex-1 md:flex-col">
<div <div
className={`relative flex flex-grow flex-col rounded-md border border-black/10 ${ className={`relative flex flex-grow flex-row rounded-md border border-black/10 ${
disabled ? 'bg-gray-100' : 'bg-white' disabled ? 'bg-gray-100' : 'bg-white'
} py-2 shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-gray-900/50 ${ } py-2 shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-gray-900/50 ${
disabled ? 'dark:bg-gray-900' : 'dark:bg-gray-700' disabled ? 'dark:bg-gray-900' : 'dark:bg-gray-700'
@ -160,7 +160,7 @@ export default function TextChat({ isSearchView = false }) {
onCompositionEnd={handleCompositionEnd} onCompositionEnd={handleCompositionEnd}
placeholder={getPlaceholderText()} placeholder={getPlaceholderText()}
disabled={disabled || isNotAppendable} disabled={disabled || isNotAppendable}
className="m-0 h-auto max-h-52 resize-none overflow-auto border-0 bg-transparent p-0 pl-12 pr-8 leading-6 placeholder:text-sm placeholder:text-gray-600 focus:outline-none focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:placeholder:text-gray-500 md:pl-8" className="m-0 flex h-auto max-h-52 flex-1 resize-none overflow-auto border-0 bg-transparent p-0 pl-2 pr-12 leading-6 placeholder:text-sm placeholder:text-gray-600 focus:outline-none focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:placeholder:text-gray-500 md:pl-2"
/> />
<SubmitButton <SubmitButton
submitMessage={submitMessage} submitMessage={submitMessage}