diff --git a/api/.env.example b/api/.env.example index c952ebe491..4fa5a432a1 100644 --- a/api/.env.example +++ b/api/.env.example @@ -41,6 +41,11 @@ OPENAI_KEY= # Leave it and BINGAI_USER_TOKEN blank to disable this endpoint. 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 # Allow user to set their own token by client # Uncomment this to enable this feature. diff --git a/api/app/clients/bingai.js b/api/app/clients/bingai.js index 015da79069..ca790ec43d 100644 --- a/api/app/clients/bingai.js +++ b/api/app/clients/bingai.js @@ -27,6 +27,7 @@ const askBing = async ({ // cookies: '', debug: false, cache: store, + host: process.env.BINGAI_HOST || null, proxy: process.env.PROXY || null }); diff --git a/api/app/titleConvo.js b/api/app/titleConvo.js index dc55ea0c8f..280c66cbcf 100644 --- a/api/app/titleConvo.js +++ b/api/app/titleConvo.js @@ -18,49 +18,38 @@ const proxyEnvToAxiosProxy = proxyString => { const titleConvo = async ({ endpoint, text, response }) => { let title = 'New Chat'; - const messages = [ - { - role: 'system', - content: - // `You are a title-generator with one job: giving a conversation, detect the language and titling the conversation provided by a user, 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); + const ChatGPTClient = (await import('@waylaidwanderer/chatgpt-api')).default; try { - const configuration = new Configuration({ - apiKey: process.env.OPENAI_KEY - }); - const openai = new OpenAIApi(configuration); - const completion = await openai.createChatCompletion(request, { - proxy: proxyEnvToAxiosProxy(process.env.PROXY || null) - }); + const instructionsPayload = { + 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. - //eslint-disable-next-line - title = completion.data.choices[0].message.content.replace(/["\.]/g, ''); + ||>User: + "${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) { console.error(e); console.log('There was an issue generating title, see error above'); diff --git a/api/server/routes/ask/askBingAI.js b/api/server/routes/ask/askBingAI.js index 9f18fd9d01..527bfe9e59 100644 --- a/api/server/routes/ask/askBingAI.js +++ b/api/server/routes/ask/askBingAI.js @@ -35,21 +35,21 @@ router.post('/', async (req, res) => { let endpointOption = {}; if (req.body?.jailbreak) endpointOption = { - jailbreak: req.body?.jailbreak || false, - jailbreakConversationId: req.body?.jailbreakConversationId || null, - systemMessage: req.body?.systemMessage || null, - context: req.body?.context || null, - toneStyle: req.body?.toneStyle || 'fast' + jailbreak: req.body?.jailbreak ?? false, + jailbreakConversationId: req.body?.jailbreakConversationId ?? null, + systemMessage: req.body?.systemMessage ?? null, + context: req.body?.context ?? null, + toneStyle: req.body?.toneStyle ?? 'fast' }; else endpointOption = { - jailbreak: req.body?.jailbreak || false, - systemMessage: req.body?.systemMessage || null, - context: req.body?.context || null, - conversationSignature: req.body?.conversationSignature || null, - clientId: req.body?.clientId || null, - invocationId: req.body?.invocationId || null, - toneStyle: req.body?.toneStyle || 'fast' + jailbreak: req.body?.jailbreak ?? false, + systemMessage: req.body?.systemMessage ?? null, + context: req.body?.context ?? null, + conversationSignature: req.body?.conversationSignature ?? null, + clientId: req.body?.clientId ?? null, + invocationId: req.body?.invocationId ?? null, + toneStyle: req.body?.toneStyle ?? 'fast' }; console.log('ask log', { @@ -122,31 +122,23 @@ const ask = async ({ 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 response.text = response.response || response.details.spokenText || '**Bing refused to answer.**'; let responseMessage = { + conversationId: newConversationId, + messageId: newResponseMessageId, + parentMessageId: overrideParentMessageId || newUserMassageId, + sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI', text: await handleText(response, true), - suggestions: - response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text), - jailbreak: endpointOption?.jailbreak + suggestions: response.details.suggestedResponses && response.details.suggestedResponses.map(s => s.text) }; - // // 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); @@ -159,14 +151,22 @@ const ask = async ({ // 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. - let conversationUpdate = { conversationId, endpoint: 'bingAI' }; - if (conversationId != responseMessage.conversationId && isNewConversation) - conversationUpdate = { - ...conversationUpdate, - conversationId: conversationId, - newConversationId: responseMessage.conversationId || conversationId - }; - conversationId = responseMessage.conversationId || conversationId; + let conversationUpdate = { conversationId: newConversationId, endpoint: 'bingAI' }; + if (conversationId != newConversationId) + if (isNewConversation) { + // change the conversationId to new one + conversationUpdate = { + ...conversationUpdate, + conversationId: conversationId, + newConversationId: newConversationId + }; + } else { + // create new conversation + conversationUpdate = { + ...conversationUpdate, + ...endpointOption + }; + } if (endpointOption?.jailbreak) { conversationUpdate.jailbreak = true; @@ -179,17 +179,16 @@ const ask = async ({ } await saveConvo(req?.session?.user?.username, conversationUpdate); + conversationId = newConversationId; // STEP3 update the user message - userMessage.conversationId = conversationId; - userMessage.messageId = responseMessage.parentMessageId; + userMessage.conversationId = newConversationId; + userMessage.messageId = newUserMassageId; // If response has parentMessageId, the fake userMessage.messageId should be updated to the real one. - if (!overrideParentMessageId) { - const oldUserMessageId = userMessageId; - await saveMessage({ ...userMessage, messageId: oldUserMessageId, newMessageId: userMessage.messageId }); - } - userMessageId = userMessage.messageId; + if (!overrideParentMessageId) + await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId }); + userMessageId = newUserMassageId; sendMessage(res, { title: await getConvoTitle(req?.session?.user?.username, conversationId), @@ -223,4 +222,4 @@ const ask = async ({ } }; -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/client/src/components/Input/Endpoints/EndpointItem.jsx b/client/src/components/Input/NewConversationMenu/EndpointItem.jsx similarity index 100% rename from client/src/components/Input/Endpoints/EndpointItem.jsx rename to client/src/components/Input/NewConversationMenu/EndpointItem.jsx diff --git a/client/src/components/Input/Endpoints/EndpointItems.jsx b/client/src/components/Input/NewConversationMenu/EndpointItems.jsx similarity index 87% rename from client/src/components/Input/Endpoints/EndpointItems.jsx rename to client/src/components/Input/NewConversationMenu/EndpointItems.jsx index 1828344b21..1d403e95a8 100644 --- a/client/src/components/Input/Endpoints/EndpointItems.jsx +++ b/client/src/components/Input/NewConversationMenu/EndpointItems.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import EndpointItem from './EndpointItem'; +import EndpointItem from './EndpointItem.jsx'; export default function EndpointItems({ endpoints, onSelect }) { return ( diff --git a/client/src/components/Input/Endpoints/FileUpload.jsx b/client/src/components/Input/NewConversationMenu/FileUpload.jsx similarity index 100% rename from client/src/components/Input/Endpoints/FileUpload.jsx rename to client/src/components/Input/NewConversationMenu/FileUpload.jsx diff --git a/client/src/components/Input/Endpoints/PresetItem.jsx b/client/src/components/Input/NewConversationMenu/PresetItem.jsx similarity index 92% rename from client/src/components/Input/Endpoints/PresetItem.jsx rename to client/src/components/Input/NewConversationMenu/PresetItem.jsx index ad7e2e2b69..60d9d72819 100644 --- a/client/src/components/Input/Endpoints/PresetItem.jsx +++ b/client/src/components/Input/NewConversationMenu/PresetItem.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { DropdownMenuRadioItem } from '../../ui/DropdownMenu.tsx'; -import EditIcon from '../../svg/EditIcon'; -import TrashIcon from '../../svg/TrashIcon'; +import EditIcon from '../../svg/EditIcon.jsx'; +import TrashIcon from '../../svg/TrashIcon.jsx'; import getIcon from '~/utils/getIcon'; export default function PresetItem({ preset = {}, value, onSelect, onChangePreset, onDeletePreset }) { @@ -47,7 +47,7 @@ export default function PresetItem({ preset = {}, value, onSelect, onChangePrese ({getPresetTitle()})
Clear All @@ -154,7 +159,7 @@ export default function NewConversationMenu() { title="Clear presets" description="Are you sure you want to clear all presets? This is irreversible." selection={{ - selectHandler: clearAllPresets, + selectHandler: clearPreset, selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white', selectText: 'Clear' }} @@ -171,7 +176,7 @@ export default function NewConversationMenu() { presets={presets} onSelect={onSelectPreset} onChangePreset={onChangePreset} - onDeletePreset={onDeletePreset} + onDeletePreset={clearPresetsTrigger} /> ) : ( No preset yet. diff --git a/client/src/components/Input/index.jsx b/client/src/components/Input/index.jsx index 460466a4f7..c43717f11d 100644 --- a/client/src/components/Input/index.jsx +++ b/client/src/components/Input/index.jsx @@ -5,7 +5,7 @@ import OpenAIOptions from './OpenAIOptions'; import ChatGPTOptions from './ChatGPTOptions'; import BingAIOptions from './BingAIOptions'; // import BingStyles from './BingStyles'; -import NewConversationMenu from './Endpoints/NewConversationMenu'; +import NewConversationMenu from './NewConversationMenu'; import Footer from './Footer'; import TextareaAutosize from 'react-textarea-autosize'; import { useMessageHandler } from '../../utils/handleSubmit'; @@ -139,7 +139,7 @@ export default function TextChat({ isSearchView = false }) {
{ + if (id.includes("node_modules")) { + return "vendor"; + } + }, + }, + }, }, resolve: { alias: { @@ -31,3 +41,22 @@ export default defineConfig({ } } }); + +interface SourcemapExclude { + excludeNodeModules?: boolean; +} +export function sourcemapExclude(opts?: SourcemapExclude): Plugin { + return { + name: "sourcemap-exclude", + transform(code: string, id: string) { + if (opts?.excludeNodeModules && id.includes("node_modules")) { + return { + code, + // https://github.com/rollup/rollup/blob/master/docs/plugin-development/index.md#source-code-transformations + map: { mappings: "" }, + }; + } + }, + }; +} +