From 8e082e622b09dbe7d606d98966921363ab6f2dc6 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 12 Oct 2024 01:07:36 +0300 Subject: [PATCH 1/8] Chat Completion: switch to async token handling --- public/scripts/PromptManager.js | 2 +- public/scripts/openai.js | 164 ++++++++++++++++++++------------ 2 files changed, 104 insertions(+), 62 deletions(-) diff --git a/public/scripts/PromptManager.js b/public/scripts/PromptManager.js index 00a34c86b..eb39c5b3a 100644 --- a/public/scripts/PromptManager.js +++ b/public/scripts/PromptManager.js @@ -315,7 +315,7 @@ class PromptManager { */ init(moduleConfiguration, serviceSettings) { this.configuration = Object.assign(this.configuration, moduleConfiguration); - this.tokenHandler = this.tokenHandler || new TokenHandler(); + this.tokenHandler = this.tokenHandler || new TokenHandler(() => { throw new Error('Token handler not set'); }); this.serviceSettings = serviceSettings; this.containerElement = document.getElementById(this.configuration.containerIdentifier); diff --git a/public/scripts/openai.js b/public/scripts/openai.js index b3938dc6d..544a09cf7 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -60,7 +60,7 @@ import { resetScrollHeight, stringFormat, } from './utils.js'; -import { countTokensOpenAI, getTokenizerModel } from './tokenizers.js'; +import { countTokensOpenAIAsync, getTokenizerModel } from './tokenizers.js'; import { isMobile } from './RossAscends-mods.js'; import { saveLogprobsForActiveMessage } from './logprobs.js'; import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; @@ -671,14 +671,14 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul // Reserve budget for new chat message const newChat = selected_group ? oai_settings.new_group_chat_prompt : oai_settings.new_chat_prompt; - const newChatMessage = new Message('system', substituteParams(newChat), 'newMainChat'); + const newChatMessage = await Message.createAsync('system', substituteParams(newChat), 'newMainChat'); chatCompletion.reserveBudget(newChatMessage); // Reserve budget for group nudge let groupNudgeMessage = null; const noGroupNudgeTypes = ['impersonate']; if (selected_group && prompts.has('groupNudge') && !noGroupNudgeTypes.includes(type)) { - groupNudgeMessage = Message.fromPrompt(prompts.get('groupNudge')); + groupNudgeMessage = await Message.fromPromptAsync(prompts.get('groupNudge')); chatCompletion.reserveBudget(groupNudgeMessage); } @@ -693,12 +693,12 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul }; const continuePrompt = new Prompt(promptObject); const preparedPrompt = promptManager.preparePrompt(continuePrompt); - continueMessage = Message.fromPrompt(preparedPrompt); + continueMessage = await Message.fromPromptAsync(preparedPrompt); chatCompletion.reserveBudget(continueMessage); } const lastChatPrompt = messages[messages.length - 1]; - const message = new Message('user', oai_settings.send_if_empty, 'emptyUserMessageReplacement'); + const message = await Message.createAsync('user', oai_settings.send_if_empty, 'emptyUserMessageReplacement'); if (lastChatPrompt && lastChatPrompt.role === 'assistant' && oai_settings.send_if_empty && chatCompletion.canAfford(message)) { chatCompletion.insert(message, 'chatHistory'); } @@ -715,11 +715,11 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul // We do not want to mutate the prompt const prompt = new Prompt(chatPrompt); prompt.identifier = `chatHistory-${messages.length - index}`; - const chatMessage = Message.fromPrompt(promptManager.preparePrompt(prompt)); + const chatMessage = await Message.fromPromptAsync(promptManager.preparePrompt(prompt)); if (promptManager.serviceSettings.names_behavior === character_names_behavior.COMPLETION && prompt.name) { const messageName = promptManager.isValidName(prompt.name) ? prompt.name : promptManager.sanitizeName(prompt.name); - chatMessage.setName(messageName); + await chatMessage.setName(messageName); } if (imageInlining && chatPrompt.image) { @@ -729,9 +729,9 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul if (canUseTools && Array.isArray(chatPrompt.invocations)) { /** @type {import('./tool-calling.js').ToolInvocation[]} */ const invocations = chatPrompt.invocations; - const toolCallMessage = new Message(chatMessage.role, undefined, 'toolCall-' + chatMessage.identifier); - const toolResultMessages = invocations.slice().reverse().map((invocation) => new Message('tool', invocation.result || '[No content]', invocation.id)); - toolCallMessage.setToolCalls(invocations); + const toolCallMessage = await Message.createAsync(chatMessage.role, undefined, 'toolCall-' + chatMessage.identifier); + const toolResultMessages = await Promise.all(invocations.slice().reverse().map((invocation) => Message.createAsync('tool', invocation.result || '[No content]', invocation.id))); + await toolCallMessage.setToolCalls(invocations); if (chatCompletion.canAffordAll([toolCallMessage, ...toolResultMessages])) { for (const resultMessage of toolResultMessages) { chatCompletion.insertAtStart(resultMessage, 'chatHistory'); @@ -748,7 +748,8 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) { // in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message if (chatPrompt.role === 'assistant') { - const collection = new MessageCollection('continuePrefill', new Message(chatMessage.role, substituteParams(oai_settings.assistant_prefill + '\n\n') + chatMessage.content, chatMessage.identifier)); + const continueMessage = await Message.createAsync(chatMessage.role, substituteParams(oai_settings.assistant_prefill + '\n\n') + chatMessage.content, chatMessage.identifier); + const collection = new MessageCollection('continuePrefill', ); chatCompletion.add(collection, -1); continue; } @@ -787,15 +788,16 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul * @param {ChatCompletion} chatCompletion - An instance of ChatCompletion class that will be populated with the prompts. * @param {Object[]} messageExamples - Array containing all message examples. */ -function populateDialogueExamples(prompts, chatCompletion, messageExamples) { +async function populateDialogueExamples(prompts, chatCompletion, messageExamples) { if (!prompts.has('dialogueExamples')) { return; } chatCompletion.add(new MessageCollection('dialogueExamples'), prompts.index('dialogueExamples')); if (Array.isArray(messageExamples) && messageExamples.length) { - const newExampleChat = new Message('system', substituteParams(oai_settings.new_example_chat_prompt), 'newChat'); - [...messageExamples].forEach((dialogue, dialogueIndex) => { + const newExampleChat = await Message.createAsync('system', substituteParams(oai_settings.new_example_chat_prompt), 'newChat'); + for (const dialogue of [...messageExamples]) { + const dialogueIndex = messageExamples.indexOf(dialogue); let examplesAdded = 0; if (chatCompletion.canAfford(newExampleChat)) chatCompletion.insert(newExampleChat, 'dialogueExamples'); @@ -806,8 +808,8 @@ function populateDialogueExamples(prompts, chatCompletion, messageExamples) { const content = prompt.content || ''; const identifier = `dialogueExamples ${dialogueIndex}-${promptIndex}`; - const chatMessage = new Message(role, content, identifier); - chatMessage.setName(prompt.name); + const chatMessage = await Message.createAsync(role, content, identifier); + await chatMessage.setName(prompt.name); if (!chatCompletion.canAfford(chatMessage)) { break; } @@ -818,7 +820,7 @@ function populateDialogueExamples(prompts, chatCompletion, messageExamples) { if (0 === examplesAdded) { chatCompletion.removeLastFrom('dialogueExamples'); } - }); + } } } @@ -873,7 +875,7 @@ function getPromptRole(role) { */ async function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples }) { // Helper function for preparing a prompt, that already exists within the prompt collection, for completion - const addToChatCompletion = (source, target = null) => { + const addToChatCompletion = async (source, target = null) => { // We need the prompts array to determine a position for the source. if (false === prompts.has(source)) return; @@ -891,30 +893,31 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm const index = target ? prompts.index(target) : prompts.index(source); const collection = new MessageCollection(source); - collection.add(Message.fromPrompt(prompt)); + const message = await Message.fromPromptAsync(prompt); + collection.add(message); chatCompletion.add(collection, index); }; chatCompletion.reserveBudget(3); // every reply is primed with <|start|>assistant<|message|> // Character and world information - addToChatCompletion('worldInfoBefore'); - addToChatCompletion('main'); - addToChatCompletion('worldInfoAfter'); - addToChatCompletion('charDescription'); - addToChatCompletion('charPersonality'); - addToChatCompletion('scenario'); - addToChatCompletion('personaDescription'); + await addToChatCompletion('worldInfoBefore'); + await addToChatCompletion('main'); + await addToChatCompletion('worldInfoAfter'); + await addToChatCompletion('charDescription'); + await addToChatCompletion('charPersonality'); + await addToChatCompletion('scenario'); + await addToChatCompletion('personaDescription'); // Collection of control prompts that will always be positioned last chatCompletion.setOverriddenPrompts(prompts.overriddenPrompts); const controlPrompts = new MessageCollection('controlPrompts'); - const impersonateMessage = Message.fromPrompt(prompts.get('impersonate')) ?? null; + const impersonateMessage = await Message.fromPromptAsync(prompts.get('impersonate')) ?? null; if (type === 'impersonate') controlPrompts.add(impersonateMessage); // Add quiet prompt to control prompts // This should always be last, even in control prompts. Add all further control prompts BEFORE this prompt - const quietPromptMessage = Message.fromPrompt(prompts.get('quietPrompt')) ?? null; + const quietPromptMessage = await Message.fromPromptAsync(prompts.get('quietPrompt')) ?? null; if (quietPromptMessage && quietPromptMessage.content) { if (isImageInliningSupported() && quietImage) { await quietPromptMessage.addImage(quietImage); @@ -940,20 +943,23 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm return acc; }, []); - [...systemPrompts, ...userRelativePrompts].forEach(identifier => addToChatCompletion(identifier)); + for (const identifier of [...systemPrompts, ...userRelativePrompts]) { + await addToChatCompletion(identifier); + } // Add enhance definition instruction - if (prompts.has('enhanceDefinitions')) addToChatCompletion('enhanceDefinitions'); + if (prompts.has('enhanceDefinitions')) await addToChatCompletion('enhanceDefinitions'); // Bias - if (bias && bias.trim().length) addToChatCompletion('bias'); + if (bias && bias.trim().length) await addToChatCompletion('bias'); // Tavern Extras - Summary if (prompts.has('summary')) { const summary = prompts.get('summary'); if (summary.position) { - chatCompletion.insert(Message.fromPrompt(summary), 'main', summary.position); + const message = await Message.fromPromptAsync(summary); + chatCompletion.insert(message, 'main', summary.position); } } @@ -962,7 +968,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm const authorsNote = prompts.get('authorsNote'); if (authorsNote.position) { - chatCompletion.insert(Message.fromPrompt(authorsNote), 'main', authorsNote.position); + const message = await Message.fromPromptAsync(authorsNote); + chatCompletion.insert(message, 'main', authorsNote.position); } } @@ -971,7 +978,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm const vectorsMemory = prompts.get('vectorsMemory'); if (vectorsMemory.position) { - chatCompletion.insert(Message.fromPrompt(vectorsMemory), 'main', vectorsMemory.position); + const message = await Message.fromPromptAsync(vectorsMemory); + chatCompletion.insert(message, 'main', vectorsMemory.position); } } @@ -980,7 +988,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm const vectorsDataBank = prompts.get('vectorsDataBank'); if (vectorsDataBank.position) { - chatCompletion.insert(Message.fromPrompt(vectorsDataBank), 'main', vectorsDataBank.position); + const message = await Message.fromPromptAsync(vectorsDataBank); + chatCompletion.insert(message, 'main', vectorsDataBank.position); } } @@ -989,13 +998,15 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm const smartContext = prompts.get('smartContext'); if (smartContext.position) { - chatCompletion.insert(Message.fromPrompt(smartContext), 'main', smartContext.position); + const message = await Message.fromPromptAsync(smartContext); + chatCompletion.insert(message, 'main', smartContext.position); } } // Other relative extension prompts for (const prompt of prompts.collection.filter(p => p.extension && p.position)) { - chatCompletion.insert(Message.fromPrompt(prompt), 'main', prompt.position); + const message = await Message.fromPromptAsync(prompt); + chatCompletion.insert(message, 'main', prompt.position); } // Pre-allocation of tokens for tool data @@ -1003,7 +1014,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm const toolData = {}; await ToolManager.registerFunctionToolsOpenAI(toolData); const toolMessage = [{ role: 'user', content: JSON.stringify(toolData) }]; - const toolTokens = tokenHandler.count(toolMessage); + const toolTokens = await tokenHandler.countAsync(toolMessage); chatCompletion.reserveBudget(toolTokens); } @@ -1012,11 +1023,11 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm // Decide whether dialogue examples should always be added if (power_user.pin_examples) { - populateDialogueExamples(prompts, chatCompletion, messageExamples); + await populateDialogueExamples(prompts, chatCompletion, messageExamples); await populateChatHistory(messages, prompts, chatCompletion, type, cyclePrompt); } else { await populateChatHistory(messages, prompts, chatCompletion, type, cyclePrompt); - populateDialogueExamples(prompts, chatCompletion, messageExamples); + await populateDialogueExamples(prompts, chatCompletion, messageExamples); } chatCompletion.freeBudget(controlPrompts); @@ -1281,7 +1292,7 @@ export async function prepareOpenAIMessages({ promptManager.setChatCompletion(chatCompletion); if (oai_settings.squash_system_messages && dryRun == false) { - chatCompletion.squashSystemMessages(); + await chatCompletion.squashSystemMessages(); } // All information is up-to-date, render. @@ -2127,8 +2138,11 @@ async function calculateLogitBias() { } class TokenHandler { - constructor(countTokenFn) { - this.countTokenFn = countTokenFn; + /** + * @param {(messages: object[] | object, full?: boolean) => Promise} countTokenAsyncFn Function to count tokens + */ + constructor(countTokenAsyncFn) { + this.countTokenAsyncFn = countTokenAsyncFn; this.counts = { 'start_chat': 0, 'prompt': 0, @@ -2157,8 +2171,15 @@ class TokenHandler { this.counts[type] -= value; } - count(messages, full, type) { - const token_count = this.countTokenFn(messages, full); + /** + * Count tokens for a message or messages. + * @param {object|any[]} messages Messages to count tokens for + * @param {boolean} [full] Count full tokens + * @param {string} [type] Identifier for the token count + * @returns {Promise} The token count + */ + async countAsync(messages, full, type) { + const token_count = await this.countTokenAsyncFn(messages, full); this.counts[type] += token_count; return token_count; @@ -2178,7 +2199,7 @@ class TokenHandler { } -const tokenHandler = new TokenHandler(countTokensOpenAI); +const tokenHandler = new TokenHandler(countTokensOpenAIAsync); // Thrown by ChatCompletion when a requested prompt couldn't be found. class IdentifierNotFoundError extends Error { @@ -2228,6 +2249,7 @@ class Message { * @param {string} role - The role of the entity creating the message. * @param {string} content - The actual content of the message. * @param {string} identifier - A unique identifier for the message. + * @private Don't use this constructor directly. Use createAsync instead. */ constructor(role, content, identifier) { this.identifier = identifier; @@ -2239,18 +2261,32 @@ class Message { this.role = 'system'; } - if (typeof this.content === 'string' && this.content.length > 0) { - this.tokens = tokenHandler.count({ role: this.role, content: this.content }); - } else { - this.tokens = 0; + this.tokens = 0; + } + + /** + * Create a new Message instance. + * @param {string} role + * @param {string} content + * @param {string} identifier + * @returns {Promise} Message instance + */ + static async createAsync(role, content, identifier) { + const message = new Message(role, content, identifier); + + if (typeof message.content === 'string' && message.content.length > 0) { + message.tokens = await tokenHandler.countAsync({ role: message.role, content: message.content }); } + + return message; } /** * Reconstruct the message from a tool invocation. - * @param {import('./tool-calling.js').ToolInvocation[]} invocations + * @param {import('./tool-calling.js').ToolInvocation[]} invocations - The tool invocations to reconstruct the message from. + * @returns {Promise} */ - setToolCalls(invocations) { + async setToolCalls(invocations) { this.tool_calls = invocations.map(i => ({ id: i.id, type: 'function', @@ -2259,12 +2295,17 @@ class Message { name: i.name, }, })); - this.tokens = tokenHandler.count({ role: this.role, tool_calls: JSON.stringify(this.tool_calls) }); + this.tokens = await tokenHandler.countAsync({ role: this.role, tool_calls: JSON.stringify(this.tool_calls) }); } - setName(name) { + /** + * Add a name to the message. + * @param {string} name Name to set for the message. + * @returns {Promise} + */ + async setName(name) { this.name = name; - this.tokens = tokenHandler.count({ role: this.role, content: this.content, name: this.name }); + this.tokens = await tokenHandler.countAsync({ role: this.role, content: this.content, name: this.name }); } async addImage(image) { @@ -2356,13 +2397,13 @@ class Message { } /** - * Create a new Message instance from a prompt. + * Create a new Message instance from a prompt asynchronously. * @static * @param {Object} prompt - The prompt object. - * @returns {Message} A new instance of Message. + * @returns {Promise} A new instance of Message. */ - static fromPrompt(prompt) { - return new Message(prompt.role, prompt.content, prompt.identifier); + static async fromPromptAsync(prompt) { + return Message.createAsync(prompt.role, prompt.content, prompt.identifier); } /** @@ -2488,8 +2529,9 @@ export class ChatCompletion { /** * Combines consecutive system messages into one if they have no name attached. + * @returns {Promise} */ - squashSystemMessages() { + async squashSystemMessages() { const excludeList = ['newMainChat', 'newChat', 'groupNudge']; this.messages.collection = this.messages.flatten(); @@ -2509,7 +2551,7 @@ export class ChatCompletion { if (shouldSquash(message)) { if (lastMessage && shouldSquash(lastMessage)) { lastMessage.content += '\n' + message.content; - lastMessage.tokens = tokenHandler.count({ role: lastMessage.role, content: lastMessage.content }); + lastMessage.tokens = await tokenHandler.countAsync({ role: lastMessage.role, content: lastMessage.content }); } else { squashedMessages.push(message); From e93a2fbed0e367caa68562ee4ea20fb232c0ccfd Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 12 Oct 2024 01:10:22 +0300 Subject: [PATCH 2/8] Fix continue prefill not getting added. --- public/scripts/openai.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 544a09cf7..15c30a6d3 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -749,7 +749,7 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul // in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message if (chatPrompt.role === 'assistant') { const continueMessage = await Message.createAsync(chatMessage.role, substituteParams(oai_settings.assistant_prefill + '\n\n') + chatMessage.content, chatMessage.identifier); - const collection = new MessageCollection('continuePrefill', ); + const collection = new MessageCollection('continuePrefill', continueMessage); chatCompletion.add(collection, -1); continue; } From a12051ee24804bd740048fbb7a9dac546e49a341 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 12 Oct 2024 12:56:52 +0300 Subject: [PATCH 3/8] CC: Only fit entire example dialogue blocks at once --- public/scripts/openai.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 15c30a6d3..b85c3db97 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -798,9 +798,7 @@ async function populateDialogueExamples(prompts, chatCompletion, messageExamples const newExampleChat = await Message.createAsync('system', substituteParams(oai_settings.new_example_chat_prompt), 'newChat'); for (const dialogue of [...messageExamples]) { const dialogueIndex = messageExamples.indexOf(dialogue); - let examplesAdded = 0; - - if (chatCompletion.canAfford(newExampleChat)) chatCompletion.insert(newExampleChat, 'dialogueExamples'); + const chatMessages = []; for (let promptIndex = 0; promptIndex < dialogue.length; promptIndex++) { const prompt = dialogue[promptIndex]; @@ -810,15 +808,17 @@ async function populateDialogueExamples(prompts, chatCompletion, messageExamples const chatMessage = await Message.createAsync(role, content, identifier); await chatMessage.setName(prompt.name); - if (!chatCompletion.canAfford(chatMessage)) { - break; - } - chatCompletion.insert(chatMessage, 'dialogueExamples'); - examplesAdded++; + chatMessages.push(chatMessage); } - if (0 === examplesAdded) { - chatCompletion.removeLastFrom('dialogueExamples'); + const canAffordBlock = chatCompletion.canAfford(newExampleChat) && chatCompletion.canAffordAll(chatMessages); + if (!canAffordBlock) { + break; + } + + chatCompletion.insert(newExampleChat, 'dialogueExamples'); + for (const chatMessage of chatMessages) { + chatCompletion.insert(chatMessage, 'dialogueExamples'); } } } From 3e9bad6845c5f63cf5fe452e5cd83b18a5b278ff Mon Sep 17 00:00:00 2001 From: Honey Tree Date: Sat, 12 Oct 2024 12:30:15 -0300 Subject: [PATCH 4/8] Preserving the entry extensions field through v2 char exports and imports --- public/scripts/world-info.js | 1 + src/endpoints/characters.js | 1 + 2 files changed, 2 insertions(+) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 05e6c6630..6351053e2 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -4565,6 +4565,7 @@ function convertCharacterBook(characterBook) { sticky: entry.extensions?.sticky ?? null, cooldown: entry.extensions?.cooldown ?? null, delay: entry.extensions?.delay ?? null, + extensions: entry.extensions ?? {}, }; }); diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index 7a75d4884..c629ef645 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -468,6 +468,7 @@ function convertWorldInfoToCharacterBook(name, entries) { position: entry.position == 0 ? 'before_char' : 'after_char', use_regex: true, // ST keys are always regex extensions: { + ...entry.extensions, position: entry.position, exclude_recursion: entry.excludeRecursion, display_index: entry.displayIndex, From 663f4ed7982b5caebd40ed3976e590084ee24568 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 12 Oct 2024 20:17:20 +0300 Subject: [PATCH 5/8] CC: Actually check if can afford both the block and the header --- public/scripts/openai.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index b85c3db97..cf24350ae 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -811,8 +811,7 @@ async function populateDialogueExamples(prompts, chatCompletion, messageExamples chatMessages.push(chatMessage); } - const canAffordBlock = chatCompletion.canAfford(newExampleChat) && chatCompletion.canAffordAll(chatMessages); - if (!canAffordBlock) { + if (!chatCompletion.canAffordAll([newExampleChat, ...chatMessages])) { break; } From 1ef31b63acecaa436b3f381e2b3b600df27fc3e1 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 12 Oct 2024 20:29:44 +0300 Subject: [PATCH 6/8] No awaitless async. Add JSDocs. --- public/scripts/openai.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/scripts/openai.js b/public/scripts/openai.js index cf24350ae..9bd922907 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -2307,6 +2307,11 @@ class Message { this.tokens = await tokenHandler.countAsync({ role: this.role, content: this.content, name: this.name }); } + /** + * Adds an image to the message. + * @param {string} image Image URL or Data URL. + * @returns {Promise} + */ async addImage(image) { const textContent = this.content; const isDataUrl = isDataURL(image); @@ -2401,7 +2406,7 @@ class Message { * @param {Object} prompt - The prompt object. * @returns {Promise} A new instance of Message. */ - static async fromPromptAsync(prompt) { + static fromPromptAsync(prompt) { return Message.createAsync(prompt.role, prompt.content, prompt.identifier); } From 1c3d33c282c6a7b4068e895be215b3574c87ba8e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:01:41 +0300 Subject: [PATCH 7/8] [eslint] Fix commonjs module config --- .eslintrc.js => .eslintrc.cjs | 10 ++++++++++ 1 file changed, 10 insertions(+) rename .eslintrc.js => .eslintrc.cjs (92%) diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 92% rename from .eslintrc.js rename to .eslintrc.cjs index be84dc2de..8e4587736 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -20,6 +20,15 @@ module.exports = { sourceType: 'module', }, }, + { + files: ['*.cjs'], + parserOptions: { + sourceType: 'commonjs', + }, + env: { + node: true, + }, + }, { files: ['src/**/*.mjs'], parserOptions: { @@ -74,6 +83,7 @@ module.exports = { 'docker/**', 'plugins/**', '**/*.min.js', + 'public/scripts/extensions/quick-reply/lib/**', ], rules: { 'no-unused-vars': ['error', { args: 'none' }], From 93137e3e2abb139d80123fd769b988faa7817102 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:02:53 +0300 Subject: [PATCH 8/8] [chore] Fix lint errors --- public/jsconfig.json | 3 ++- public/scripts/BulkEditOverlay.js | 1 - public/scripts/dynamic-styles.js | 2 +- public/scripts/extensions/caption/index.js | 2 +- public/scripts/extensions/stable-diffusion/index.js | 4 +--- public/scripts/login.js | 4 ++-- public/scripts/macros.js | 2 +- public/scripts/textgen-models.js | 1 - public/scripts/variables.js | 3 +-- 9 files changed, 9 insertions(+), 13 deletions(-) diff --git a/public/jsconfig.json b/public/jsconfig.json index 593c2df94..4b6d97541 100644 --- a/public/jsconfig.json +++ b/public/jsconfig.json @@ -12,7 +12,8 @@ "**/dist/**", "**/.git/**", "lib/**", - "**/*.min.js" + "**/*.min.js", + "scripts/extensions/quick-reply/lib/**" ], "typeAcquisition": { "include": [ diff --git a/public/scripts/BulkEditOverlay.js b/public/scripts/BulkEditOverlay.js index dce7e61ab..34c46543d 100644 --- a/public/scripts/BulkEditOverlay.js +++ b/public/scripts/BulkEditOverlay.js @@ -7,7 +7,6 @@ import { event_types, eventSource, getCharacters, - getPastCharacterChats, getRequestHeaders, buildAvatarList, characterToEntity, diff --git a/public/scripts/dynamic-styles.js b/public/scripts/dynamic-styles.js index dd5462501..ad9a8fbd1 100644 --- a/public/scripts/dynamic-styles.js +++ b/public/scripts/dynamic-styles.js @@ -148,7 +148,7 @@ export function initDynamicStyles() { // Start observing the head for any new added stylesheets observer.observe(document.head, { childList: true, - subtree: true + subtree: true, }); // Process all stylesheets on initial load diff --git a/public/scripts/extensions/caption/index.js b/public/scripts/extensions/caption/index.js index 04812cead..19c35fe71 100644 --- a/public/scripts/extensions/caption/index.js +++ b/public/scripts/extensions/caption/index.js @@ -1,6 +1,6 @@ import { ensureImageFormatSupported, getBase64Async, isTrueBoolean, saveBase64AsFile } from '../../utils.js'; import { getContext, getApiUrl, doExtrasFetch, extension_settings, modules, renderExtensionTemplateAsync } from '../../extensions.js'; -import { appendMediaToMessage, callPopup, eventSource, event_types, getRequestHeaders, main_api, saveChatConditional, saveSettingsDebounced, substituteParamsExtended } from '../../../script.js'; +import { appendMediaToMessage, callPopup, eventSource, event_types, getRequestHeaders, saveChatConditional, saveSettingsDebounced, substituteParamsExtended } from '../../../script.js'; import { getMessageTimeStamp } from '../../RossAscends-mods.js'; import { SECRET_KEYS, secret_state } from '../../secrets.js'; import { getMultimodalCaption } from '../shared.js'; diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index d32874b5c..af8b29ea8 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -1,8 +1,6 @@ import { saveSettingsDebounced, systemUserName, - hideSwipeButtons, - showSwipeButtons, getRequestHeaders, event_types, eventSource, @@ -2210,7 +2208,7 @@ function processReply(str) { str = str.replaceAll('“', ''); str = str.replaceAll('\n', ', '); str = str.normalize('NFD'); - str = str.replace(/[^a-zA-Z0-9\.,:_(){}<>[\]\-']+/g, ' '); + str = str.replace(/[^a-zA-Z0-9.,:_(){}<>[\]\-']+/g, ' '); str = str.replace(/\s+/g, ' '); // Collapse multiple whitespaces into one str = str.trim(); diff --git a/public/scripts/login.js b/public/scripts/login.js index dec6a90e9..bb5f67fbe 100644 --- a/public/scripts/login.js +++ b/public/scripts/login.js @@ -183,9 +183,9 @@ function redirectToHome() { // After a login theres no need to preserve the // noauto (if present) const urlParams = new URLSearchParams(window.location.search); - + urlParams.delete('noauto'); - + window.location.href = '/' + urlParams.toString(); } diff --git a/public/scripts/macros.js b/public/scripts/macros.js index 55bf386c0..793400f8c 100644 --- a/public/scripts/macros.js +++ b/public/scripts/macros.js @@ -464,7 +464,7 @@ export function evaluateMacros(content, env) { content = content.replace(/{{firstIncludedMessageId}}/gi, () => String(getFirstIncludedMessageId() ?? '')); content = content.replace(/{{lastSwipeId}}/gi, () => String(getLastSwipeId() ?? '')); content = content.replace(/{{currentSwipeId}}/gi, () => String(getCurrentSwipeId() ?? '')); - content = content.replace(/{{reverse\:(.+?)}}/gi, (_, str) => Array.from(str).reverse().join('')); + content = content.replace(/{{reverse:(.+?)}}/gi, (_, str) => Array.from(str).reverse().join('')); content = content.replace(/\{\{\/\/([\s\S]*?)\}\}/gm, ''); diff --git a/public/scripts/textgen-models.js b/public/scripts/textgen-models.js index df0b53482..32b288b39 100644 --- a/public/scripts/textgen-models.js +++ b/public/scripts/textgen-models.js @@ -4,7 +4,6 @@ import { textgenerationwebui_settings as textgen_settings, textgen_types } from import { tokenizers } from './tokenizers.js'; import { renderTemplateAsync } from './templates.js'; import { POPUP_TYPE, callGenericPopup } from './popup.js'; -import { PAGINATION_TEMPLATE } from './utils.js'; let mancerModels = []; let togetherModels = []; diff --git a/public/scripts/variables.js b/public/scripts/variables.js index 7fc12aa54..ce8c76578 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -1,6 +1,5 @@ -import { chat_metadata, getCurrentChatId, saveSettingsDebounced, sendSystemMessage, system_message_types } from '../script.js'; +import { chat_metadata, getCurrentChatId, saveSettingsDebounced } from '../script.js'; import { extension_settings, saveMetadataDebounced } from './extensions.js'; -import { callGenericPopup, POPUP_TYPE } from './popup.js'; import { executeSlashCommandsWithOptions } from './slash-commands.js'; import { SlashCommand } from './slash-commands/SlashCommand.js'; import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js';