diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js
index 115a70c01..313a002ec 100644
--- a/public/scripts/extensions.js
+++ b/public/scripts/extensions.js
@@ -8,12 +8,38 @@ export {
defaultRequestArgs,
modules,
extension_settings,
+ ModuleWorkerWrapper,
};
let extensionNames = [];
let manifests = [];
const defaultUrl = "http://localhost:5100";
+// Disables parallel updates
+class ModuleWorkerWrapper {
+ constructor(callback) {
+ this.isBusy = false;
+ this.callback = callback;
+ }
+
+ // Called by the extension
+ async update() {
+ // Don't touch me I'm busy...
+ if (this.isBusy) {
+ return;
+ }
+
+ // I'm free. Let's update!
+ try {
+ this.isBusy = true;
+ await this.callback();
+ }
+ finally {
+ this.isBusy = false;
+ }
+ }
+}
+
const extension_settings = {
apiUrl: defaultUrl,
autoConnect: false,
diff --git a/public/scripts/extensions/caption/index.js b/public/scripts/extensions/caption/index.js
index 535be7620..314f57b7c 100644
--- a/public/scripts/extensions/caption/index.js
+++ b/public/scripts/extensions/caption/index.js
@@ -1,122 +1,118 @@
-import { getBase64Async } from "../../utils.js";
-import { getContext, getApiUrl } from "../../extensions.js";
-export { MODULE_NAME };
-
-const MODULE_NAME = 'caption';
-const UPDATE_INTERVAL = 1000;
-
-async function moduleWorker() {
- const context = getContext();
-
- context.onlineStatus === 'no_connection'
- ? $('#send_picture').hide(200)
- : $('#send_picture').show(200);
-}
-
-async function setImageIcon() {
- try {
- const sendButton = $('#send_picture .extensionsMenuExtensionButton');
- sendButton.addClass('fa-image');
- sendButton.removeClass('fa-hourglass-half');
- }
- catch (error) {
- console.log(error);
- }
-}
-
-async function setSpinnerIcon() {
- try {
- const sendButton = $('#send_picture .extensionsMenuExtensionButton');
- sendButton.removeClass('fa-image');
- sendButton.addClass('fa-hourglass-half');
- }
- catch (error) {
- console.log(error);
- }
-}
-
-async function sendCaptionedMessage(caption, image) {
- const context = getContext();
- const messageText = `[${context.name1} sends ${context.name2 ?? ''} a picture that contains: ${caption}]`;
- const message = {
- name: context.name1,
- is_user: true,
- is_name: true,
- send_date: Date.now(),
- mes: messageText,
- extra: {
- image: image,
- title: caption,
- },
- };
- context.chat.push(message);
- context.addOneMessage(message);
- await context.generate();
-}
-
-async function onSelectImage(e) {
- setSpinnerIcon();
- const file = e.target.files[0];
-
- if (!file) {
- return;
- }
-
- try {
- const base64Img = await getBase64Async(file);
- const url = new URL(getApiUrl());
- url.pathname = '/api/caption';
-
- const apiResult = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Bypass-Tunnel-Reminder': 'bypass',
- },
- body: JSON.stringify({ image: base64Img.split(',')[1] })
- });
-
- if (apiResult.ok) {
- const data = await apiResult.json();
- const caption = data.caption;
- const imageToSave = data.thumbnail ? `data:image/jpeg;base64,${data.thumbnail}` : base64Img;
- await sendCaptionedMessage(caption, imageToSave);
- }
- }
- catch (error) {
- console.log(error);
- }
- finally {
- e.target.form.reset();
- setImageIcon();
- }
-}
-
-jQuery(function () {
- function addSendPictureButton() {
- const sendButton = $(`
-
-
- Send a picture
-
`);
-
- $('#extensionsMenu').prepend(sendButton);
- $(sendButton).hide();
- $(sendButton).on('click', () => $('#img_file').trigger('click'));
- }
- function addPictureSendForm() {
- const inputHtml = ``;
- const imgForm = document.createElement('form');
- imgForm.id = 'img_form';
- $(imgForm).append(inputHtml);
- $(imgForm).hide();
- $('#form_sheld').append(imgForm);
- $('#img_file').on('change', onSelectImage);
- }
-
- addPictureSendForm();
- addSendPictureButton();
- setImageIcon();
- moduleWorker();
- setInterval(moduleWorker, UPDATE_INTERVAL);
-});
\ No newline at end of file
+import { getBase64Async } from "../../utils.js";
+import { getContext, getApiUrl } from "../../extensions.js";
+export { MODULE_NAME };
+
+const MODULE_NAME = 'caption';
+const UPDATE_INTERVAL = 1000;
+
+async function moduleWorker() {
+ $('#send_picture').toggle(getContext().onlineStatus !== 'no_connection');
+}
+
+async function setImageIcon() {
+ try {
+ const sendButton = $('#send_picture .extensionsMenuExtensionButton');
+ sendButton.addClass('fa-image');
+ sendButton.removeClass('fa-hourglass-half');
+ }
+ catch (error) {
+ console.log(error);
+ }
+}
+
+async function setSpinnerIcon() {
+ try {
+ const sendButton = $('#send_picture .extensionsMenuExtensionButton');
+ sendButton.removeClass('fa-image');
+ sendButton.addClass('fa-hourglass-half');
+ }
+ catch (error) {
+ console.log(error);
+ }
+}
+
+async function sendCaptionedMessage(caption, image) {
+ const context = getContext();
+ const messageText = `[${context.name1} sends ${context.name2 ?? ''} a picture that contains: ${caption}]`;
+ const message = {
+ name: context.name1,
+ is_user: true,
+ is_name: true,
+ send_date: Date.now(),
+ mes: messageText,
+ extra: {
+ image: image,
+ title: caption,
+ },
+ };
+ context.chat.push(message);
+ context.addOneMessage(message);
+ await context.generate();
+}
+
+async function onSelectImage(e) {
+ setSpinnerIcon();
+ const file = e.target.files[0];
+
+ if (!file) {
+ return;
+ }
+
+ try {
+ const base64Img = await getBase64Async(file);
+ const url = new URL(getApiUrl());
+ url.pathname = '/api/caption';
+
+ const apiResult = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Bypass-Tunnel-Reminder': 'bypass',
+ },
+ body: JSON.stringify({ image: base64Img.split(',')[1] })
+ });
+
+ if (apiResult.ok) {
+ const data = await apiResult.json();
+ const caption = data.caption;
+ const imageToSave = data.thumbnail ? `data:image/jpeg;base64,${data.thumbnail}` : base64Img;
+ await sendCaptionedMessage(caption, imageToSave);
+ }
+ }
+ catch (error) {
+ console.log(error);
+ }
+ finally {
+ e.target.form.reset();
+ setImageIcon();
+ }
+}
+
+jQuery(function () {
+ function addSendPictureButton() {
+ const sendButton = $(`
+
+
+ Send a picture
+
`);
+
+ $('#extensionsMenu').prepend(sendButton);
+ $(sendButton).hide();
+ $(sendButton).on('click', () => $('#img_file').trigger('click'));
+ }
+ function addPictureSendForm() {
+ const inputHtml = ``;
+ const imgForm = document.createElement('form');
+ imgForm.id = 'img_form';
+ $(imgForm).append(inputHtml);
+ $(imgForm).hide();
+ $('#form_sheld').append(imgForm);
+ $('#img_file').on('change', onSelectImage);
+ }
+
+ addPictureSendForm();
+ addSendPictureButton();
+ setImageIcon();
+ moduleWorker();
+ setInterval(moduleWorker, UPDATE_INTERVAL);
+});
diff --git a/public/scripts/extensions/dice/index.js b/public/scripts/extensions/dice/index.js
index ce161a40d..c49cdcdfc 100644
--- a/public/scripts/extensions/dice/index.js
+++ b/public/scripts/extensions/dice/index.js
@@ -1,100 +1,97 @@
-import { callPopup } from "../../../script.js";
-import { getContext } from "../../extensions.js";
-export { MODULE_NAME };
-
-const MODULE_NAME = 'dice';
-const UPDATE_INTERVAL = 1000;
-
-function setDiceIcon() {
- const sendButton = document.getElementById('roll_dice');
- /* sendButton.style.backgroundImage = `url(/img/dice-solid.svg)`; */
- //sendButton.classList.remove('spin');
-}
-
-async function doDiceRoll() {
- let value = $(this).data('value');
-
- if (value == 'custom') {
- value = await callPopup('Enter the dice formula:
(for example, 2d6)', 'input');
- }
-
- const isValid = droll.validate(value);
-
- if (isValid) {
- const result = droll.roll(value);
- const context = getContext();
- context.sendSystemMessage('generic', `${context.name1} rolls a ${value}. The result is: ${result.total} (${result.rolls})`, { isSmallSys: true });
- }
-}
-
-function addDiceRollButton() {
- const buttonHtml = `
-
-
- Roll Dice
-
- `;
- const dropdownHtml = `
-
-
- - d4
- - d6
- - d8
- - d10
- - d12
- - d20
- - d100
- - ...
-
-
`;
-
- $('#extensionsMenu').prepend(buttonHtml);
-
- $(document.body).append(dropdownHtml)
- $('#dice_dropdown li').on('click', doDiceRoll);
- const button = $('#roll_dice');
- const dropdown = $('#dice_dropdown');
- dropdown.hide();
- button.hide();
-
- let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
- placement: 'bottom',
- });
-
- $(document).on('click touchend', function (e) {
- const target = $(e.target);
- if (target.is(dropdown)) return;
- if (target.is(button) && !dropdown.is(":visible")) {
- e.preventDefault();
-
- dropdown.show(200);
- popper.update();
- } else {
- dropdown.hide(200);
- }
- });
-}
-
-function addDiceScript() {
- if (!window.droll) {
- const script = document.createElement('script');
- script.src = "/scripts/extensions/dice/droll.js";
- document.body.appendChild(script);
- }
-}
-
-async function moduleWorker() {
- const context = getContext();
-
- context.onlineStatus === 'no_connection'
- ? $('#roll_dice').hide(200)
- : $('#roll_dice').show(200);
-}
-
-$(document).ready(function () {
- addDiceScript();
- addDiceRollButton();
- setDiceIcon();
- moduleWorker();
- setInterval(moduleWorker, UPDATE_INTERVAL);
-});
\ No newline at end of file
+import { callPopup } from "../../../script.js";
+import { getContext } from "../../extensions.js";
+import { registerSlashCommand } from "../../slash-commands.js";
+export { MODULE_NAME };
+
+const MODULE_NAME = 'dice';
+const UPDATE_INTERVAL = 1000;
+
+async function doDiceRoll(customDiceFormula) {
+ let value = typeof customDiceFormula === 'string' ? customDiceFormula.trim() : $(this).data('value');
+
+ if (value == 'custom') {
+ value = await callPopup('Enter the dice formula:
(for example, 2d6)', 'input');x
+ }
+
+ if (!value) {
+ return;
+ }
+
+ const isValid = droll.validate(value);
+
+ if (isValid) {
+ const result = droll.roll(value);
+ const context = getContext();
+ context.sendSystemMessage('generic', `${context.name1} rolls a ${value}. The result is: ${result.total} (${result.rolls})`, { isSmallSys: true });
+ } else {
+ toastr.warning('Invalid dice formula');
+ }
+}
+
+function addDiceRollButton() {
+ const buttonHtml = `
+
+
+ Roll Dice
+
+ `;
+ const dropdownHtml = `
+
+
+ - d4
+ - d6
+ - d8
+ - d10
+ - d12
+ - d20
+ - d100
+ - ...
+
+
`;
+
+ $('#extensionsMenu').prepend(buttonHtml);
+
+ $(document.body).append(dropdownHtml)
+ $('#dice_dropdown li').on('click', doDiceRoll);
+ const button = $('#roll_dice');
+ const dropdown = $('#dice_dropdown');
+ dropdown.hide();
+ button.hide();
+
+ let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
+ placement: 'bottom',
+ });
+
+ $(document).on('click touchend', function (e) {
+ const target = $(e.target);
+ if (target.is(dropdown)) return;
+ if (target.is(button) && !dropdown.is(":visible")) {
+ e.preventDefault();
+
+ dropdown.show(200);
+ popper.update();
+ } else {
+ dropdown.hide(200);
+ }
+ });
+}
+
+function addDiceScript() {
+ if (!window.droll) {
+ const script = document.createElement('script');
+ script.src = "/scripts/extensions/dice/droll.js";
+ document.body.appendChild(script);
+ }
+}
+
+async function moduleWorker() {
+ $('#roll_dice').toggle(getContext().onlineStatus !== 'no_connection');
+}
+
+jQuery(function () {
+ addDiceScript();
+ addDiceRollButton();
+ moduleWorker();
+ setInterval(moduleWorker, UPDATE_INTERVAL);
+ registerSlashCommand('roll', (_, value) => doDiceRoll(value), [], "(dice formula) – roll the dice. For example, /roll 2d6", false, true);
+});
diff --git a/public/scripts/extensions/expressions/index.js b/public/scripts/extensions/expressions/index.js
index 14141dce1..5426837ec 100644
--- a/public/scripts/extensions/expressions/index.js
+++ b/public/scripts/extensions/expressions/index.js
@@ -1,5 +1,5 @@
import { callPopup, getRequestHeaders, saveSettingsDebounced } from "../../../script.js";
-import { getContext, getApiUrl, modules, extension_settings } from "../../extensions.js";
+import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper } from "../../extensions.js";
export { MODULE_NAME };
const MODULE_NAME = 'expressions';
@@ -58,24 +58,6 @@ function onExpressionsShowDefaultInput() {
}
}
-let isWorkerBusy = false;
-
-async function moduleWorkerWrapper() {
- // Don't touch me I'm busy...
- if (isWorkerBusy) {
- return;
- }
-
- // I'm free. Let's update!
- try {
- isWorkerBusy = true;
- await moduleWorker();
- }
- finally {
- isWorkerBusy = false;
- }
-}
-
async function moduleWorker() {
const context = getContext();
@@ -509,6 +491,7 @@ async function onClickExpressionDelete(event) {
addExpressionImage();
addSettings();
- setInterval(moduleWorkerWrapper, UPDATE_INTERVAL);
- moduleWorkerWrapper();
+ const wrapper = new ModuleWorkerWrapper(moduleWorker);
+ setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
+ moduleWorker();
})();
diff --git a/public/scripts/extensions/floating-prompt/index.js b/public/scripts/extensions/floating-prompt/index.js
index f1763732b..a454d56b5 100644
--- a/public/scripts/extensions/floating-prompt/index.js
+++ b/public/scripts/extensions/floating-prompt/index.js
@@ -1,234 +1,217 @@
-import { chat_metadata, saveSettingsDebounced } from "../../../script.js";
-import { extension_settings, getContext } from "../../extensions.js";
-import { registerSlashCommand } from "../../slash-commands.js";
-import { debounce } from "../../utils.js";
-export { MODULE_NAME };
-
-const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
-
-const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
-const UPDATE_INTERVAL = 1000;
-
-const DEFAULT_DEPTH = 4;
-const DEFAULT_POSITION = 1;
-const DEFAULT_INTERVAL = 1;
-
-const metadata_keys = {
- prompt: 'note_prompt',
- interval: 'note_interval',
- depth: 'note_depth',
- position: 'note_position',
-}
-
-function setNoteTextCommand(_, text) {
- $('#extension_floating_prompt').val(text).trigger('input');
- toastr.success("Author's Note text updated");
-}
-
-function setNoteDepthCommand(_, text) {
- const value = Number(text);
-
- if (Number.isNaN(value)) {
- toastr.error('Not a valid number');
- return;
- }
-
- $('#extension_floating_depth').val(Math.abs(value)).trigger('input');
- toastr.success("Author's Note depth updated");
-}
-
-function setNoteIntervalCommand(_, text) {
- const value = Number(text);
-
- if (Number.isNaN(value)) {
- toastr.error('Not a valid number');
- return;
- }
-
- $('#extension_floating_interval').val(Math.abs(value)).trigger('input');
- toastr.success("Author's Note frequency updated");
-}
-
-function setNotePositionCommand(_, text) {
- const validPositions = {
- 'scenario': 0,
- 'chat': 1,
- };
-
- const position = validPositions[text?.trim()];
-
- if (Number.isNaN(position)) {
- toastr.error('Not a valid position');
- return;
- }
-
- $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');
- toastr.info("Author's Note position updated");
-}
-
-async function onExtensionFloatingPromptInput() {
- chat_metadata[metadata_keys.prompt] = $(this).val();
- saveMetadataDebounced();
-}
-
-async function onExtensionFloatingIntervalInput() {
- chat_metadata[metadata_keys.interval] = Number($(this).val());
- saveMetadataDebounced();
-}
-
-async function onExtensionFloatingDepthInput() {
- let value = Number($(this).val());
-
- if (value < 0) {
- value = Math.abs(value);
- $(this).val(value);
- }
-
- chat_metadata[metadata_keys.depth] = value;
- saveMetadataDebounced();
-}
-
-async function onExtensionFloatingPositionInput(e) {
- chat_metadata[metadata_keys.position] = e.target.value;
- saveMetadataDebounced();
-}
-
-function onExtensionFloatingDefaultInput() {
- extension_settings.note.default = $(this).val();
- saveSettingsDebounced();
-}
-
-function loadSettings() {
- chat_metadata[metadata_keys.prompt] = chat_metadata[metadata_keys.prompt] ?? extension_settings.note.default ?? '';
- chat_metadata[metadata_keys.interval] = chat_metadata[metadata_keys.interval] ?? DEFAULT_INTERVAL;
- chat_metadata[metadata_keys.position] = chat_metadata[metadata_keys.position] ?? DEFAULT_POSITION;
- chat_metadata[metadata_keys.depth] = chat_metadata[metadata_keys.depth] ?? DEFAULT_DEPTH;
- $('#extension_floating_prompt').val(chat_metadata[metadata_keys.prompt]);
- $('#extension_floating_interval').val(chat_metadata[metadata_keys.interval]);
- $('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]);
- $(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true);
- $('#extension_floating_default').val(extension_settings.note.default);
-}
-
-let isWorkerBusy = false;
-
-async function moduleWorkerWrapper() {
- // Don't touch me I'm busy...
- if (isWorkerBusy) {
- return;
- }
-
- // I'm free. Let's update!
- try {
- isWorkerBusy = true;
- await moduleWorker();
- }
- finally {
- isWorkerBusy = false;
- }
-}
-
-async function moduleWorker() {
- const context = getContext();
-
- if (!context.groupId && context.characterId === undefined) {
- return;
- }
-
- loadSettings();
-
- // take the count of messages
- let lastMessageNumber = Array.isArray(context.chat) && context.chat.length ? context.chat.filter(m => m.is_user).length : 0;
-
- // special case for new chat
- if (Array.isArray(context.chat) && context.chat.length === 1) {
- lastMessageNumber = 1;
- }
-
- if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
- context.setExtensionPrompt(MODULE_NAME, '');
- $('#extension_floating_counter').text('(disabled)');
- return;
- }
-
- const messagesTillInsertion = lastMessageNumber >= chat_metadata[metadata_keys.interval]
- ? (lastMessageNumber % chat_metadata[metadata_keys.interval])
- : (chat_metadata[metadata_keys.interval] - lastMessageNumber);
- const shouldAddPrompt = messagesTillInsertion == 0;
- const prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : '';
- context.setExtensionPrompt(MODULE_NAME, prompt, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
- $('#extension_floating_counter').text(shouldAddPrompt ? '0' : messagesTillInsertion);
-}
-
-(function () {
- function addExtensionsSettings() {
- const settingsHtml = `
-
-
-
-
-
-
-
-
- Will be automatically added as the Author's Note for all new chats.
-
-
-
-
-
-
- `;
-
- $('#movingDivs').append(settingsHtml);
- $('#extension_floating_prompt').on('input', onExtensionFloatingPromptInput);
- $('#extension_floating_interval').on('input', onExtensionFloatingIntervalInput);
- $('#extension_floating_depth').on('input', onExtensionFloatingDepthInput);
- $('#extension_floating_default').on('input', onExtensionFloatingDefaultInput);
- $('input[name="extension_floating_position"]').on('change', onExtensionFloatingPositionInput);
- }
-
- addExtensionsSettings();
- setInterval(moduleWorkerWrapper, UPDATE_INTERVAL);
- registerSlashCommand('note', setNoteTextCommand, [], "(text) – sets an author's note for the currently selected chat", true, true);
- registerSlashCommand('depth', setNoteDepthCommand, [], "(number) – sets an author's note depth for in-chat positioning", true, true);
- registerSlashCommand('freq', setNoteIntervalCommand, ['interval'], "(number) – sets an author's note insertion frequency", true, true);
- registerSlashCommand('pos', setNotePositionCommand, ['position'], "(chat or scenario) – sets an author's note position", true, true);
-})();
\ No newline at end of file
+import { chat_metadata, saveSettingsDebounced } from "../../../script.js";
+import { ModuleWorkerWrapper, extension_settings, getContext } from "../../extensions.js";
+import { registerSlashCommand } from "../../slash-commands.js";
+import { debounce } from "../../utils.js";
+export { MODULE_NAME };
+
+const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
+
+const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
+const UPDATE_INTERVAL = 1000;
+
+const DEFAULT_DEPTH = 4;
+const DEFAULT_POSITION = 1;
+const DEFAULT_INTERVAL = 1;
+
+const metadata_keys = {
+ prompt: 'note_prompt',
+ interval: 'note_interval',
+ depth: 'note_depth',
+ position: 'note_position',
+}
+
+function setNoteTextCommand(_, text) {
+ $('#extension_floating_prompt').val(text).trigger('input');
+ toastr.success("Author's Note text updated");
+}
+
+function setNoteDepthCommand(_, text) {
+ const value = Number(text);
+
+ if (Number.isNaN(value)) {
+ toastr.error('Not a valid number');
+ return;
+ }
+
+ $('#extension_floating_depth').val(Math.abs(value)).trigger('input');
+ toastr.success("Author's Note depth updated");
+}
+
+function setNoteIntervalCommand(_, text) {
+ const value = Number(text);
+
+ if (Number.isNaN(value)) {
+ toastr.error('Not a valid number');
+ return;
+ }
+
+ $('#extension_floating_interval').val(Math.abs(value)).trigger('input');
+ toastr.success("Author's Note frequency updated");
+}
+
+function setNotePositionCommand(_, text) {
+ const validPositions = {
+ 'scenario': 0,
+ 'chat': 1,
+ };
+
+ const position = validPositions[text?.trim()];
+
+ if (Number.isNaN(position)) {
+ toastr.error('Not a valid position');
+ return;
+ }
+
+ $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');
+ toastr.info("Author's Note position updated");
+}
+
+async function onExtensionFloatingPromptInput() {
+ chat_metadata[metadata_keys.prompt] = $(this).val();
+ saveMetadataDebounced();
+}
+
+async function onExtensionFloatingIntervalInput() {
+ chat_metadata[metadata_keys.interval] = Number($(this).val());
+ saveMetadataDebounced();
+}
+
+async function onExtensionFloatingDepthInput() {
+ let value = Number($(this).val());
+
+ if (value < 0) {
+ value = Math.abs(value);
+ $(this).val(value);
+ }
+
+ chat_metadata[metadata_keys.depth] = value;
+ saveMetadataDebounced();
+}
+
+async function onExtensionFloatingPositionInput(e) {
+ chat_metadata[metadata_keys.position] = e.target.value;
+ saveMetadataDebounced();
+}
+
+function onExtensionFloatingDefaultInput() {
+ extension_settings.note.default = $(this).val();
+ saveSettingsDebounced();
+}
+
+function loadSettings() {
+ chat_metadata[metadata_keys.prompt] = chat_metadata[metadata_keys.prompt] ?? extension_settings.note.default ?? '';
+ chat_metadata[metadata_keys.interval] = chat_metadata[metadata_keys.interval] ?? DEFAULT_INTERVAL;
+ chat_metadata[metadata_keys.position] = chat_metadata[metadata_keys.position] ?? DEFAULT_POSITION;
+ chat_metadata[metadata_keys.depth] = chat_metadata[metadata_keys.depth] ?? DEFAULT_DEPTH;
+ $('#extension_floating_prompt').val(chat_metadata[metadata_keys.prompt]);
+ $('#extension_floating_interval').val(chat_metadata[metadata_keys.interval]);
+ $('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]);
+ $(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true);
+ $('#extension_floating_default').val(extension_settings.note.default);
+}
+
+async function moduleWorker() {
+ const context = getContext();
+
+ if (!context.groupId && context.characterId === undefined) {
+ return;
+ }
+
+ loadSettings();
+
+ // take the count of messages
+ let lastMessageNumber = Array.isArray(context.chat) && context.chat.length ? context.chat.filter(m => m.is_user).length : 0;
+
+ // special case for new chat
+ if (Array.isArray(context.chat) && context.chat.length === 1) {
+ lastMessageNumber = 1;
+ }
+
+ if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
+ context.setExtensionPrompt(MODULE_NAME, '');
+ $('#extension_floating_counter').text('(disabled)');
+ return;
+ }
+
+ const messagesTillInsertion = lastMessageNumber >= chat_metadata[metadata_keys.interval]
+ ? (lastMessageNumber % chat_metadata[metadata_keys.interval])
+ : (chat_metadata[metadata_keys.interval] - lastMessageNumber);
+ const shouldAddPrompt = messagesTillInsertion == 0;
+ const prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : '';
+ context.setExtensionPrompt(MODULE_NAME, prompt, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
+ $('#extension_floating_counter').text(shouldAddPrompt ? '0' : messagesTillInsertion);
+}
+
+(function () {
+ function addExtensionsSettings() {
+ const settingsHtml = `
+
+
+
+
+
+
+
+
+ Will be automatically added as the Author's Note for all new chats.
+
+
+
+
+
+
+ `;
+
+ $('#movingDivs').append(settingsHtml);
+ $('#extension_floating_prompt').on('input', onExtensionFloatingPromptInput);
+ $('#extension_floating_interval').on('input', onExtensionFloatingIntervalInput);
+ $('#extension_floating_depth').on('input', onExtensionFloatingDepthInput);
+ $('#extension_floating_default').on('input', onExtensionFloatingDefaultInput);
+ $('input[name="extension_floating_position"]').on('change', onExtensionFloatingPositionInput);
+ }
+
+ addExtensionsSettings();
+ const wrapper = new ModuleWorkerWrapper(moduleWorker);
+ setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
+ registerSlashCommand('note', setNoteTextCommand, [], "(text) – sets an author's note for the currently selected chat", true, true);
+ registerSlashCommand('depth', setNoteDepthCommand, [], "(number) – sets an author's note depth for in-chat positioning", true, true);
+ registerSlashCommand('freq', setNoteIntervalCommand, ['interval'], "(number) – sets an author's note insertion frequency", true, true);
+ registerSlashCommand('pos', setNotePositionCommand, ['position'], "(chat or scenario) – sets an author's note position", true, true);
+})();
diff --git a/public/scripts/extensions/memory/index.js b/public/scripts/extensions/memory/index.js
index a508c2d5a..c58888710 100644
--- a/public/scripts/extensions/memory/index.js
+++ b/public/scripts/extensions/memory/index.js
@@ -1,388 +1,371 @@
-import { getStringHash, debounce } from "../../utils.js";
-import { getContext, getApiUrl, extension_settings } from "../../extensions.js";
-import { extension_prompt_types, is_send_press, saveSettingsDebounced } from "../../../script.js";
-export { MODULE_NAME };
-
-const MODULE_NAME = '1_memory';
-const UPDATE_INTERVAL = 5000;
-
-let lastCharacterId = null;
-let lastGroupId = null;
-let lastChatId = null;
-let lastMessageHash = null;
-let lastMessageId = null;
-let inApiCall = false;
-
-const formatMemoryValue = (value) => value ? `Context: ${value.trim()}` : '';
-const saveChatDebounced = debounce(() => getContext().saveChat(), 2000);
-
-const defaultSettings = {
- minLongMemory: 16,
- maxLongMemory: 1024,
- longMemoryLength: 128,
- shortMemoryLength: 512,
- minShortMemory: 128,
- maxShortMemory: 1024,
- shortMemoryStep: 16,
- longMemoryStep: 8,
- repetitionPenaltyStep: 0.05,
- repetitionPenalty: 1.2,
- maxRepetitionPenalty: 2.0,
- minRepetitionPenalty: 1.0,
- temperature: 1.0,
- minTemperature: 0.1,
- maxTemperature: 2.0,
- temperatureStep: 0.05,
- lengthPenalty: 1,
- minLengthPenalty: -4,
- maxLengthPenalty: 4,
- lengthPenaltyStep: 0.1,
- memoryFrozen: false,
-};
-
-function loadSettings() {
- if (Object.keys(extension_settings.memory).length === 0) {
- Object.assign(extension_settings.memory, defaultSettings);
- }
-
- $('#memory_long_length').val(extension_settings.memory.longMemoryLength).trigger('input');
- $('#memory_short_length').val(extension_settings.memory.shortMemoryLength).trigger('input');
- $('#memory_repetition_penalty').val(extension_settings.memory.repetitionPenalty).trigger('input');
- $('#memory_temperature').val(extension_settings.memory.temperature).trigger('input');
- $('#memory_length_penalty').val(extension_settings.memory.lengthPenalty).trigger('input');
- $('#memory_frozen').prop('checked', extension_settings.memory.memoryFrozen).trigger('input');
-}
-
-function onMemoryShortInput() {
- const value = $(this).val();
- extension_settings.memory.shortMemoryLength = Number(value);
- $('#memory_short_length_tokens').text(value);
- saveSettingsDebounced();
-
- // Don't let long buffer be bigger than short
- if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) {
- $('#memory_long_length').val(extension_settings.memory.shortMemoryLength).trigger('input');
- }
-}
-
-function onMemoryLongInput() {
- const value = $(this).val();
- extension_settings.memory.longMemoryLength = Number(value);
- $('#memory_long_length_tokens').text(value);
- saveSettingsDebounced();
-
- // Don't let long buffer be bigger than short
- if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) {
- $('#memory_short_length').val(extension_settings.memory.longMemoryLength).trigger('input');
- }
-}
-
-function onMemoryRepetitionPenaltyInput() {
- const value = $(this).val();
- extension_settings.memory.repetitionPenalty = Number(value);
- $('#memory_repetition_penalty_value').text(extension_settings.memory.repetitionPenalty.toFixed(2));
- saveSettingsDebounced();
-}
-
-function onMemoryTemperatureInput() {
- const value = $(this).val();
- extension_settings.memory.temperature = Number(value);
- $('#memory_temperature_value').text(extension_settings.memory.temperature.toFixed(2));
- saveSettingsDebounced();
-}
-
-function onMemoryLengthPenaltyInput() {
- const value = $(this).val();
- extension_settings.memory.lengthPenalty = Number(value);
- $('#memory_length_penalty_value').text(extension_settings.memory.lengthPenalty.toFixed(2));
- saveSettingsDebounced();
-}
-
-function onMemoryFrozenInput() {
- const value = Boolean($(this).prop('checked'));
- extension_settings.memory.memoryFrozen = value;
- saveSettingsDebounced();
-}
-
-function saveLastValues() {
- const context = getContext();
- lastGroupId = context.groupId;
- lastCharacterId = context.characterId;
- lastChatId = context.chatId;
- lastMessageId = context.chat?.length ?? null;
- lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['mes']) ?? '');
-}
-
-function getLatestMemoryFromChat(chat) {
- if (!Array.isArray(chat) || !chat.length) {
- return '';
- }
-
- const reversedChat = chat.slice().reverse();
- reversedChat.shift();
- for (let mes of reversedChat) {
- if (mes.extra && mes.extra.memory) {
- return mes.extra.memory;
- }
- }
-
- return '';
-}
-
-let isWorkerBusy = false;
-
-async function moduleWorkerWrapper() {
- // Don't touch me I'm busy...
- if (isWorkerBusy) {
- return;
- }
-
- // I'm free. Let's update!
- try {
- isWorkerBusy = true;
- await moduleWorker();
- }
- finally {
- isWorkerBusy = false;
- }
-}
-
-async function moduleWorker() {
- const context = getContext();
- const chat = context.chat;
-
- // no characters or group selected
- if (!context.groupId && context.characterId === undefined) {
- return;
- }
-
- // Generation is in progress, summary prevented
- if (is_send_press) {
- return;
- }
-
- // Chat/character/group changed
- if ((context.groupId && lastGroupId !== context.groupId) || (context.characterId !== lastCharacterId) || (context.chatId !== lastChatId)) {
- const latestMemory = getLatestMemoryFromChat(chat);
- setMemoryContext(latestMemory, false);
- saveLastValues();
- return;
- }
-
- // Currently summarizing or frozen state - skip
- if (inApiCall || extension_settings.memory.memoryFrozen) {
- return;
- }
-
- // No new messages - do nothing
- if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) {
- return;
- }
-
- // Messages has been deleted - rewrite the context with the latest available memory
- if (chat.length < lastMessageId) {
- const latestMemory = getLatestMemoryFromChat(chat);
- setMemoryContext(latestMemory, false);
- }
-
- // Message has been edited / regenerated - delete the saved memory
- if (chat.length
- && chat[chat.length - 1].extra
- && chat[chat.length - 1].extra.memory
- && lastMessageId === chat.length
- && getStringHash(chat[chat.length - 1].mes) !== lastMessageHash) {
- delete chat[chat.length - 1].extra.memory;
- }
-
- try {
- await summarizeChat(context);
- }
- catch (error) {
- console.log(error);
- }
- finally {
- saveLastValues();
- }
-}
-
-async function summarizeChat(context) {
- function getMemoryString() {
- return (longMemory + '\n\n' + memoryBuffer.slice().reverse().join('\n\n')).trim();
- }
-
- const chat = context.chat;
- const longMemory = getLatestMemoryFromChat(chat);
- const reversedChat = chat.slice().reverse();
- reversedChat.shift();
- let memoryBuffer = [];
-
- for (let mes of reversedChat) {
- // we reached the point of latest memory
- if (longMemory && mes.extra && mes.extra.memory == longMemory) {
- break;
- }
-
- // don't care about system
- if (mes.is_system) {
- continue;
- }
-
- // determine the sender's name
- const name = mes.is_user ? (context.name1 ?? 'You') : (mes.force_avatar ? mes.name : context.name2);
- const entry = `${name}:\n${mes['mes']}`;
- memoryBuffer.push(entry);
-
- // check if token limit was reached
- if (context.getTokenCount(getMemoryString()) >= extension_settings.memory.shortMemoryLength) {
- break;
- }
- }
-
- const resultingString = getMemoryString();
-
- if (context.getTokenCount(resultingString) < extension_settings.memory.shortMemoryLength) {
- return;
- }
-
- // perform the summarization API call
- try {
- inApiCall = true;
- const url = new URL(getApiUrl());
- url.pathname = '/api/summarize';
-
- const apiResult = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Bypass-Tunnel-Reminder': 'bypass',
- },
- body: JSON.stringify({
- text: resultingString,
- params: {
- min_length: extension_settings.memory.longMemoryLength * 0, // testing how it behaves 0 min length
- max_length: extension_settings.memory.longMemoryLength,
- repetition_penalty: extension_settings.memory.repetitionPenalty,
- temperature: extension_settings.memory.temperature,
- length_penalty: extension_settings.memory.lengthPenalty,
- }
- })
- });
-
- if (apiResult.ok) {
- const data = await apiResult.json();
- const summary = data.summary;
-
- const newContext = getContext();
-
- // something changed during summarization request
- if (newContext.groupId !== context.groupId
- || newContext.chatId !== context.chatId
- || (!newContext.groupId && (newContext.characterId !== context.characterId))) {
- console.log('Context changed, summary discarded');
- return;
- }
-
- setMemoryContext(summary, true);
- }
- }
- catch (error) {
- console.log(error);
- }
- finally {
- inApiCall = false;
- }
-}
-
-function onMemoryRestoreClick() {
- const context = getContext();
- const content = $('#memory_contents').val();
- const reversedChat = context.chat.slice().reverse();
- reversedChat.shift();
-
- for (let mes of reversedChat) {
- if (mes.extra && mes.extra.memory == content) {
- delete mes.extra.memory;
- break;
- }
- }
-
- const newContent = getLatestMemoryFromChat(context.chat);
- setMemoryContext(newContent, false);
-}
-
-function onMemoryContentInput() {
- const value = $(this).val();
- setMemoryContext(value, true);
-}
-
-function setMemoryContext(value, saveToMessage) {
- const context = getContext();
- context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_prompt_types.AFTER_SCENARIO);
- $('#memory_contents').val(value);
-
- if (saveToMessage && context.chat.length) {
- const idx = context.chat.length - 2;
- const mes = context.chat[idx < 0 ? 0 : idx];
-
- if (!mes.extra) {
- mes.extra = {};
- }
-
- mes.extra.memory = value;
- saveChatDebounced();
- }
-}
-
-$(document).ready(function () {
- function addExtensionControls() {
- const settingsHtml = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- $('#extensions_settings').append(settingsHtml);
- $('#memory_restore').on('click', onMemoryRestoreClick);
- $('#memory_contents').on('input', onMemoryContentInput);
- $('#memory_long_length').on('input', onMemoryLongInput);
- $('#memory_short_length').on('input', onMemoryShortInput);
- $('#memory_repetition_penalty').on('input', onMemoryRepetitionPenaltyInput);
- $('#memory_temperature').on('input', onMemoryTemperatureInput);
- $('#memory_length_penalty').on('input', onMemoryLengthPenaltyInput);
- $('#memory_frozen').on('input', onMemoryFrozenInput);
- }
-
- addExtensionControls();
- loadSettings();
- setInterval(moduleWorkerWrapper, UPDATE_INTERVAL);
-});
\ No newline at end of file
+import { getStringHash, debounce } from "../../utils.js";
+import { getContext, getApiUrl, extension_settings, ModuleWorkerWrapper } from "../../extensions.js";
+import { extension_prompt_types, is_send_press, saveSettingsDebounced } from "../../../script.js";
+export { MODULE_NAME };
+
+const MODULE_NAME = '1_memory';
+const UPDATE_INTERVAL = 5000;
+
+let lastCharacterId = null;
+let lastGroupId = null;
+let lastChatId = null;
+let lastMessageHash = null;
+let lastMessageId = null;
+let inApiCall = false;
+
+const formatMemoryValue = (value) => value ? `Context: ${value.trim()}` : '';
+const saveChatDebounced = debounce(() => getContext().saveChat(), 2000);
+
+const defaultSettings = {
+ minLongMemory: 16,
+ maxLongMemory: 1024,
+ longMemoryLength: 128,
+ shortMemoryLength: 512,
+ minShortMemory: 128,
+ maxShortMemory: 1024,
+ shortMemoryStep: 16,
+ longMemoryStep: 8,
+ repetitionPenaltyStep: 0.05,
+ repetitionPenalty: 1.2,
+ maxRepetitionPenalty: 2.0,
+ minRepetitionPenalty: 1.0,
+ temperature: 1.0,
+ minTemperature: 0.1,
+ maxTemperature: 2.0,
+ temperatureStep: 0.05,
+ lengthPenalty: 1,
+ minLengthPenalty: -4,
+ maxLengthPenalty: 4,
+ lengthPenaltyStep: 0.1,
+ memoryFrozen: false,
+};
+
+function loadSettings() {
+ if (Object.keys(extension_settings.memory).length === 0) {
+ Object.assign(extension_settings.memory, defaultSettings);
+ }
+
+ $('#memory_long_length').val(extension_settings.memory.longMemoryLength).trigger('input');
+ $('#memory_short_length').val(extension_settings.memory.shortMemoryLength).trigger('input');
+ $('#memory_repetition_penalty').val(extension_settings.memory.repetitionPenalty).trigger('input');
+ $('#memory_temperature').val(extension_settings.memory.temperature).trigger('input');
+ $('#memory_length_penalty').val(extension_settings.memory.lengthPenalty).trigger('input');
+ $('#memory_frozen').prop('checked', extension_settings.memory.memoryFrozen).trigger('input');
+}
+
+function onMemoryShortInput() {
+ const value = $(this).val();
+ extension_settings.memory.shortMemoryLength = Number(value);
+ $('#memory_short_length_tokens').text(value);
+ saveSettingsDebounced();
+
+ // Don't let long buffer be bigger than short
+ if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) {
+ $('#memory_long_length').val(extension_settings.memory.shortMemoryLength).trigger('input');
+ }
+}
+
+function onMemoryLongInput() {
+ const value = $(this).val();
+ extension_settings.memory.longMemoryLength = Number(value);
+ $('#memory_long_length_tokens').text(value);
+ saveSettingsDebounced();
+
+ // Don't let long buffer be bigger than short
+ if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) {
+ $('#memory_short_length').val(extension_settings.memory.longMemoryLength).trigger('input');
+ }
+}
+
+function onMemoryRepetitionPenaltyInput() {
+ const value = $(this).val();
+ extension_settings.memory.repetitionPenalty = Number(value);
+ $('#memory_repetition_penalty_value').text(extension_settings.memory.repetitionPenalty.toFixed(2));
+ saveSettingsDebounced();
+}
+
+function onMemoryTemperatureInput() {
+ const value = $(this).val();
+ extension_settings.memory.temperature = Number(value);
+ $('#memory_temperature_value').text(extension_settings.memory.temperature.toFixed(2));
+ saveSettingsDebounced();
+}
+
+function onMemoryLengthPenaltyInput() {
+ const value = $(this).val();
+ extension_settings.memory.lengthPenalty = Number(value);
+ $('#memory_length_penalty_value').text(extension_settings.memory.lengthPenalty.toFixed(2));
+ saveSettingsDebounced();
+}
+
+function onMemoryFrozenInput() {
+ const value = Boolean($(this).prop('checked'));
+ extension_settings.memory.memoryFrozen = value;
+ saveSettingsDebounced();
+}
+
+function saveLastValues() {
+ const context = getContext();
+ lastGroupId = context.groupId;
+ lastCharacterId = context.characterId;
+ lastChatId = context.chatId;
+ lastMessageId = context.chat?.length ?? null;
+ lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['mes']) ?? '');
+}
+
+function getLatestMemoryFromChat(chat) {
+ if (!Array.isArray(chat) || !chat.length) {
+ return '';
+ }
+
+ const reversedChat = chat.slice().reverse();
+ reversedChat.shift();
+ for (let mes of reversedChat) {
+ if (mes.extra && mes.extra.memory) {
+ return mes.extra.memory;
+ }
+ }
+
+ return '';
+}
+
+async function moduleWorker() {
+ const context = getContext();
+ const chat = context.chat;
+
+ // no characters or group selected
+ if (!context.groupId && context.characterId === undefined) {
+ return;
+ }
+
+ // Generation is in progress, summary prevented
+ if (is_send_press) {
+ return;
+ }
+
+ // Chat/character/group changed
+ if ((context.groupId && lastGroupId !== context.groupId) || (context.characterId !== lastCharacterId) || (context.chatId !== lastChatId)) {
+ const latestMemory = getLatestMemoryFromChat(chat);
+ setMemoryContext(latestMemory, false);
+ saveLastValues();
+ return;
+ }
+
+ // Currently summarizing or frozen state - skip
+ if (inApiCall || extension_settings.memory.memoryFrozen) {
+ return;
+ }
+
+ // No new messages - do nothing
+ if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) {
+ return;
+ }
+
+ // Messages has been deleted - rewrite the context with the latest available memory
+ if (chat.length < lastMessageId) {
+ const latestMemory = getLatestMemoryFromChat(chat);
+ setMemoryContext(latestMemory, false);
+ }
+
+ // Message has been edited / regenerated - delete the saved memory
+ if (chat.length
+ && chat[chat.length - 1].extra
+ && chat[chat.length - 1].extra.memory
+ && lastMessageId === chat.length
+ && getStringHash(chat[chat.length - 1].mes) !== lastMessageHash) {
+ delete chat[chat.length - 1].extra.memory;
+ }
+
+ try {
+ await summarizeChat(context);
+ }
+ catch (error) {
+ console.log(error);
+ }
+ finally {
+ saveLastValues();
+ }
+}
+
+async function summarizeChat(context) {
+ function getMemoryString() {
+ return (longMemory + '\n\n' + memoryBuffer.slice().reverse().join('\n\n')).trim();
+ }
+
+ const chat = context.chat;
+ const longMemory = getLatestMemoryFromChat(chat);
+ const reversedChat = chat.slice().reverse();
+ reversedChat.shift();
+ let memoryBuffer = [];
+
+ for (let mes of reversedChat) {
+ // we reached the point of latest memory
+ if (longMemory && mes.extra && mes.extra.memory == longMemory) {
+ break;
+ }
+
+ // don't care about system
+ if (mes.is_system) {
+ continue;
+ }
+
+ // determine the sender's name
+ const name = mes.is_user ? (context.name1 ?? 'You') : (mes.force_avatar ? mes.name : context.name2);
+ const entry = `${name}:\n${mes['mes']}`;
+ memoryBuffer.push(entry);
+
+ // check if token limit was reached
+ if (context.getTokenCount(getMemoryString()) >= extension_settings.memory.shortMemoryLength) {
+ break;
+ }
+ }
+
+ const resultingString = getMemoryString();
+
+ if (context.getTokenCount(resultingString) < extension_settings.memory.shortMemoryLength) {
+ return;
+ }
+
+ // perform the summarization API call
+ try {
+ inApiCall = true;
+ const url = new URL(getApiUrl());
+ url.pathname = '/api/summarize';
+
+ const apiResult = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Bypass-Tunnel-Reminder': 'bypass',
+ },
+ body: JSON.stringify({
+ text: resultingString,
+ params: {
+ min_length: extension_settings.memory.longMemoryLength * 0, // testing how it behaves 0 min length
+ max_length: extension_settings.memory.longMemoryLength,
+ repetition_penalty: extension_settings.memory.repetitionPenalty,
+ temperature: extension_settings.memory.temperature,
+ length_penalty: extension_settings.memory.lengthPenalty,
+ }
+ })
+ });
+
+ if (apiResult.ok) {
+ const data = await apiResult.json();
+ const summary = data.summary;
+
+ const newContext = getContext();
+
+ // something changed during summarization request
+ if (newContext.groupId !== context.groupId
+ || newContext.chatId !== context.chatId
+ || (!newContext.groupId && (newContext.characterId !== context.characterId))) {
+ console.log('Context changed, summary discarded');
+ return;
+ }
+
+ setMemoryContext(summary, true);
+ }
+ }
+ catch (error) {
+ console.log(error);
+ }
+ finally {
+ inApiCall = false;
+ }
+}
+
+function onMemoryRestoreClick() {
+ const context = getContext();
+ const content = $('#memory_contents').val();
+ const reversedChat = context.chat.slice().reverse();
+ reversedChat.shift();
+
+ for (let mes of reversedChat) {
+ if (mes.extra && mes.extra.memory == content) {
+ delete mes.extra.memory;
+ break;
+ }
+ }
+
+ const newContent = getLatestMemoryFromChat(context.chat);
+ setMemoryContext(newContent, false);
+}
+
+function onMemoryContentInput() {
+ const value = $(this).val();
+ setMemoryContext(value, true);
+}
+
+function setMemoryContext(value, saveToMessage) {
+ const context = getContext();
+ context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_prompt_types.AFTER_SCENARIO);
+ $('#memory_contents').val(value);
+
+ if (saveToMessage && context.chat.length) {
+ const idx = context.chat.length - 2;
+ const mes = context.chat[idx < 0 ? 0 : idx];
+
+ if (!mes.extra) {
+ mes.extra = {};
+ }
+
+ mes.extra.memory = value;
+ saveChatDebounced();
+ }
+}
+
+$(document).ready(function () {
+ function addExtensionControls() {
+ const settingsHtml = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ $('#extensions_settings').append(settingsHtml);
+ $('#memory_restore').on('click', onMemoryRestoreClick);
+ $('#memory_contents').on('input', onMemoryContentInput);
+ $('#memory_long_length').on('input', onMemoryLongInput);
+ $('#memory_short_length').on('input', onMemoryShortInput);
+ $('#memory_repetition_penalty').on('input', onMemoryRepetitionPenaltyInput);
+ $('#memory_temperature').on('input', onMemoryTemperatureInput);
+ $('#memory_length_penalty').on('input', onMemoryLengthPenaltyInput);
+ $('#memory_frozen').on('input', onMemoryFrozenInput);
+ }
+
+ addExtensionControls();
+ loadSettings();
+ const wrapper = new ModuleWorkerWrapper(moduleWorker);
+ setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
+});
diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js
index d08efcfd6..2a75accfe 100644
--- a/public/scripts/extensions/tts/index.js
+++ b/public/scripts/extensions/tts/index.js
@@ -1,5 +1,5 @@
import { callPopup, cancelTtsPlay, eventSource, event_types, isMultigenEnabled, is_send_press, saveSettingsDebounced } from '../../../script.js'
-import { extension_settings, getContext } from '../../extensions.js'
+import { ModuleWorkerWrapper, extension_settings, getContext } from '../../extensions.js'
import { getStringHash } from '../../utils.js'
import { ElevenLabsTtsProvider } from './elevenlabs.js'
import { SileroTtsProvider } from './silerotts.js'
@@ -38,24 +38,6 @@ async function onNarrateOneMessage() {
moduleWorker();
}
-let isWorkerBusy = false;
-
-async function moduleWorkerWrapper() {
- // Don't touch me I'm busy...
- if (isWorkerBusy) {
- return;
- }
-
- // I'm free. Let's update!
- try {
- isWorkerBusy = true;
- await moduleWorker();
- }
- finally {
- isWorkerBusy = false;
- }
-}
-
async function moduleWorker() {
// Primarily determining when to add new chat to the TTS queue
const enabled = $('#tts_enabled').is(':checked')
@@ -661,6 +643,7 @@ $(document).ready(function () {
loadSettings() // Depends on Extension Controls and loadTtsProvider
loadTtsProvider(extension_settings.tts.currentProvider) // No dependencies
addAudioControl() // Depends on Extension Controls
- setInterval(moduleWorkerWrapper, UPDATE_INTERVAL) // Init depends on all the things
+ const wrapper = new ModuleWorkerWrapper(moduleWorker);
+ setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL) // Init depends on all the things
eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);
})