diff --git a/.env.example b/.env.example index 17a8e1f209..4c90e38f65 100644 --- a/.env.example +++ b/.env.example @@ -73,6 +73,16 @@ OPENAI_API_KEY=user_provided # Leave it blank to use internal settings. # OPENAI_MODELS=gpt-3.5-turbo,gpt-3.5-turbo-16k,gpt-3.5-turbo-0301,text-davinci-003,gpt-4,gpt-4-0314,gpt-4-0613 +# Titling is enabled by default when initiating a conversation. +# Uncomment the following variable to disable this feature. +# TITLE_CONVO=false + +# The model used for titling by default is gpt-3.5-turbo-0613 to assure it works with the default method. +# gpt-3.5-turbo should also work when using the official API (and not a reverse proxy). +# You can change the model used by uncommenting the following and setting it to the model you want +# Must be compatible with the OpenAI Endpoint. +# OPENAI_TITLE_MODEL=gpt-3.5-turbo + # Reverse proxy settings for OpenAI: # https://github.com/waylaidwanderer/node-chatgpt-api#using-a-reverse-proxy # OPENAI_REVERSE_PROXY= diff --git a/api/app/clients/OpenAIClient.js b/api/app/clients/OpenAIClient.js index 866891ea38..ad31a6b4ba 100644 --- a/api/app/clients/OpenAIClient.js +++ b/api/app/clients/OpenAIClient.js @@ -2,6 +2,7 @@ const BaseClient = require('./BaseClient'); const ChatGPTClient = require('./ChatGPTClient'); const { encoding_for_model: encodingForModel, get_encoding: getEncoding } = require('tiktoken'); const { maxTokensMap, genAzureChatCompletion } = require('../../utils'); +const { truncateText } = require('./prompts'); const { runTitleChain } = require('./chains'); const { createLLM } = require('./llm'); @@ -406,12 +407,14 @@ class OpenAIClient extends BaseClient { async titleConvo({ text, responseText = '' }) { let title = 'New Chat'; const convo = `||>User: -"${text}" +"${truncateText(text)}" ||>Response: -"${JSON.stringify(responseText)}"`; +"${JSON.stringify(truncateText(responseText))}"`; + + const { OPENAI_TITLE_MODEL } = process.env ?? {}; const modelOptions = { - model: 'gpt-3.5-turbo-0613', + model: OPENAI_TITLE_MODEL ?? 'gpt-3.5-turbo-0613', temperature: 0.2, presence_penalty: 0, frequency_penalty: 0, @@ -446,7 +449,7 @@ class OpenAIClient extends BaseClient { } catch (e) { console.error(e.message); console.log('There was an issue generating title with LangChain, trying the old method...'); - modelOptions.model = 'gpt-3.5-turbo'; + modelOptions.model = OPENAI_TITLE_MODEL ?? 'gpt-3.5-turbo'; const instructionsPayload = [ { role: 'system', diff --git a/api/app/clients/chains/runTitleChain.js b/api/app/clients/chains/runTitleChain.js index 1178f15063..8f9df1381d 100644 --- a/api/app/clients/chains/runTitleChain.js +++ b/api/app/clients/chains/runTitleChain.js @@ -15,7 +15,7 @@ const createLanguageChain = ({ llm }) => }); const titleSchema = z.object({ - title: z.string().describe('The title-cased title of the conversation in the given language.'), + title: z.string().describe('The conversation title in title-case, in the given language.'), }); const createTitleChain = ({ llm, convo }) => { const titlePrompt = createTitlePrompt({ convo }); diff --git a/api/app/clients/prompts/index.js b/api/app/clients/prompts/index.js index 8cf3bb468f..1cd4da772f 100644 --- a/api/app/clients/prompts/index.js +++ b/api/app/clients/prompts/index.js @@ -1,9 +1,11 @@ const instructions = require('./instructions'); const titlePrompts = require('./titlePrompts'); const refinePrompts = require('./refinePrompts'); +const truncateText = require('./truncateText'); module.exports = { ...refinePrompts, ...instructions, ...titlePrompts, + truncateText, }; diff --git a/api/app/clients/prompts/titlePrompts.js b/api/app/clients/prompts/titlePrompts.js index 573aff5411..1e893ba295 100644 --- a/api/app/clients/prompts/titlePrompts.js +++ b/api/app/clients/prompts/titlePrompts.js @@ -16,7 +16,7 @@ const createTitlePrompt = ({ convo }) => { const titlePrompt = new ChatPromptTemplate({ promptMessages: [ SystemMessagePromptTemplate.fromTemplate( - `Write a concise title for this conversation in the given language. Title in 5 Words or Less. No Punctuation or Quotation. All first letters of every word must be capitalized (resembling title-case), written in the given Language. + `Write a concise title for this conversation in the given language. Title in 5 Words or Less. No Punctuation or Quotation. Must be in Title Case, written in the given Language. ${convo}`, ), HumanMessagePromptTemplate.fromTemplate('Language: {language}'), diff --git a/api/app/clients/prompts/truncateText.js b/api/app/clients/prompts/truncateText.js new file mode 100644 index 0000000000..003b1bc9af --- /dev/null +++ b/api/app/clients/prompts/truncateText.js @@ -0,0 +1,10 @@ +const MAX_CHAR = 255; + +function truncateText(text) { + if (text.length > MAX_CHAR) { + return `${text.slice(0, MAX_CHAR)}... [text truncated for brevity]`; + } + return text; +} + +module.exports = truncateText; diff --git a/api/app/titleConvoBing.js b/api/app/titleConvoBing.js index cb75bd8591..8f95bd9f20 100644 --- a/api/app/titleConvoBing.js +++ b/api/app/titleConvoBing.js @@ -1,7 +1,13 @@ +const { isEnabled } = require('../server/utils'); const throttle = require('lodash/throttle'); const titleConvo = async ({ text, response }) => { let title = 'New Chat'; + const { TITLE_CONVO = 'true' } = process.env ?? {}; + if (!isEnabled(TITLE_CONVO)) { + return title; + } + const { BingAIClient } = await import('@waylaidwanderer/chatgpt-api'); const titleGenerator = new BingAIClient({ userToken: process.env.BINGAI_TOKEN, diff --git a/api/server/routes/endpoints/openAI/addTitle.js b/api/server/routes/endpoints/openAI/addTitle.js index 87b846ba00..eb43e51735 100644 --- a/api/server/routes/endpoints/openAI/addTitle.js +++ b/api/server/routes/endpoints/openAI/addTitle.js @@ -1,6 +1,12 @@ +const { isEnabled } = require('../../../utils'); const { saveConvo } = require('../../../../models'); const addTitle = async (req, { text, response, client }) => { + const { TITLE_CONVO = 'true' } = process.env ?? {}; + if (!isEnabled(TITLE_CONVO)) { + return; + } + const title = await client.titleConvo({ text, responseText: response?.text }); await saveConvo(req.user.id, { conversationId: response.conversationId,