Add slash command for dice rolls. Create class for worker wrappers

This commit is contained in:
SillyLossy
2023-05-31 13:57:08 +03:00
parent b2016fa7f3
commit ef90c31643
7 changed files with 836 additions and 885 deletions

View File

@ -8,12 +8,38 @@ export {
defaultRequestArgs, defaultRequestArgs,
modules, modules,
extension_settings, extension_settings,
ModuleWorkerWrapper,
}; };
let extensionNames = []; let extensionNames = [];
let manifests = []; let manifests = [];
const defaultUrl = "http://localhost:5100"; 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 = { const extension_settings = {
apiUrl: defaultUrl, apiUrl: defaultUrl,
autoConnect: false, autoConnect: false,

View File

@ -1,122 +1,118 @@
import { getBase64Async } from "../../utils.js"; import { getBase64Async } from "../../utils.js";
import { getContext, getApiUrl } from "../../extensions.js"; import { getContext, getApiUrl } from "../../extensions.js";
export { MODULE_NAME }; export { MODULE_NAME };
const MODULE_NAME = 'caption'; const MODULE_NAME = 'caption';
const UPDATE_INTERVAL = 1000; const UPDATE_INTERVAL = 1000;
async function moduleWorker() { async function moduleWorker() {
const context = getContext(); $('#send_picture').toggle(getContext().onlineStatus !== 'no_connection');
}
context.onlineStatus === 'no_connection'
? $('#send_picture').hide(200) async function setImageIcon() {
: $('#send_picture').show(200); try {
} const sendButton = $('#send_picture .extensionsMenuExtensionButton');
sendButton.addClass('fa-image');
async function setImageIcon() { sendButton.removeClass('fa-hourglass-half');
try { }
const sendButton = $('#send_picture .extensionsMenuExtensionButton'); catch (error) {
sendButton.addClass('fa-image'); console.log(error);
sendButton.removeClass('fa-hourglass-half'); }
} }
catch (error) {
console.log(error); async function setSpinnerIcon() {
} try {
} const sendButton = $('#send_picture .extensionsMenuExtensionButton');
sendButton.removeClass('fa-image');
async function setSpinnerIcon() { sendButton.addClass('fa-hourglass-half');
try { }
const sendButton = $('#send_picture .extensionsMenuExtensionButton'); catch (error) {
sendButton.removeClass('fa-image'); console.log(error);
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 = {
async function sendCaptionedMessage(caption, image) { name: context.name1,
const context = getContext(); is_user: true,
const messageText = `[${context.name1} sends ${context.name2 ?? ''} a picture that contains: ${caption}]`; is_name: true,
const message = { send_date: Date.now(),
name: context.name1, mes: messageText,
is_user: true, extra: {
is_name: true, image: image,
send_date: Date.now(), title: caption,
mes: messageText, },
extra: { };
image: image, context.chat.push(message);
title: caption, context.addOneMessage(message);
}, await context.generate();
}; }
context.chat.push(message);
context.addOneMessage(message); async function onSelectImage(e) {
await context.generate(); setSpinnerIcon();
} const file = e.target.files[0];
async function onSelectImage(e) { if (!file) {
setSpinnerIcon(); return;
const file = e.target.files[0]; }
if (!file) { try {
return; const base64Img = await getBase64Async(file);
} const url = new URL(getApiUrl());
url.pathname = '/api/caption';
try {
const base64Img = await getBase64Async(file); const apiResult = await fetch(url, {
const url = new URL(getApiUrl()); method: 'POST',
url.pathname = '/api/caption'; headers: {
'Content-Type': 'application/json',
const apiResult = await fetch(url, { 'Bypass-Tunnel-Reminder': 'bypass',
method: 'POST', },
headers: { body: JSON.stringify({ image: base64Img.split(',')[1] })
'Content-Type': 'application/json', });
'Bypass-Tunnel-Reminder': 'bypass',
}, if (apiResult.ok) {
body: JSON.stringify({ image: base64Img.split(',')[1] }) const data = await apiResult.json();
}); const caption = data.caption;
const imageToSave = data.thumbnail ? `data:image/jpeg;base64,${data.thumbnail}` : base64Img;
if (apiResult.ok) { await sendCaptionedMessage(caption, imageToSave);
const data = await apiResult.json(); }
const caption = data.caption; }
const imageToSave = data.thumbnail ? `data:image/jpeg;base64,${data.thumbnail}` : base64Img; catch (error) {
await sendCaptionedMessage(caption, imageToSave); console.log(error);
} }
} finally {
catch (error) { e.target.form.reset();
console.log(error); setImageIcon();
} }
finally { }
e.target.form.reset();
setImageIcon(); jQuery(function () {
} function addSendPictureButton() {
} const sendButton = $(`
<div id="send_picture" class="list-group-item flex-container flexGap5">
jQuery(function () { <div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
function addSendPictureButton() { Send a picture
const sendButton = $(` </div>`);
<div id="send_picture" class="list-group-item flex-container flexGap5">
<div class="fa-solid fa-image extensionsMenuExtensionButton"></div> $('#extensionsMenu').prepend(sendButton);
Send a picture $(sendButton).hide();
</div>`); $(sendButton).on('click', () => $('#img_file').trigger('click'));
}
$('#extensionsMenu').prepend(sendButton); function addPictureSendForm() {
$(sendButton).hide(); const inputHtml = `<input id="img_file" type="file" accept="image/*">`;
$(sendButton).on('click', () => $('#img_file').trigger('click')); const imgForm = document.createElement('form');
} imgForm.id = 'img_form';
function addPictureSendForm() { $(imgForm).append(inputHtml);
const inputHtml = `<input id="img_file" type="file" accept="image/*">`; $(imgForm).hide();
const imgForm = document.createElement('form'); $('#form_sheld').append(imgForm);
imgForm.id = 'img_form'; $('#img_file').on('change', onSelectImage);
$(imgForm).append(inputHtml); }
$(imgForm).hide();
$('#form_sheld').append(imgForm); addPictureSendForm();
$('#img_file').on('change', onSelectImage); addSendPictureButton();
} setImageIcon();
moduleWorker();
addPictureSendForm(); setInterval(moduleWorker, UPDATE_INTERVAL);
addSendPictureButton(); });
setImageIcon();
moduleWorker();
setInterval(moduleWorker, UPDATE_INTERVAL);
});

View File

@ -1,100 +1,97 @@
import { callPopup } from "../../../script.js"; import { callPopup } from "../../../script.js";
import { getContext } from "../../extensions.js"; import { getContext } from "../../extensions.js";
export { MODULE_NAME }; import { registerSlashCommand } from "../../slash-commands.js";
export { MODULE_NAME };
const MODULE_NAME = 'dice';
const UPDATE_INTERVAL = 1000; const MODULE_NAME = 'dice';
const UPDATE_INTERVAL = 1000;
function setDiceIcon() {
const sendButton = document.getElementById('roll_dice'); async function doDiceRoll(customDiceFormula) {
/* sendButton.style.backgroundImage = `url(/img/dice-solid.svg)`; */ let value = typeof customDiceFormula === 'string' ? customDiceFormula.trim() : $(this).data('value');
//sendButton.classList.remove('spin');
} if (value == 'custom') {
value = await callPopup('Enter the dice formula:<br><i>(for example, <tt>2d6</tt>)</i>', 'input');x
async function doDiceRoll() { }
let value = $(this).data('value');
if (!value) {
if (value == 'custom') { return;
value = await callPopup('Enter the dice formula:<br><i>(for example, <tt>2d6</tt>)</i>', 'input'); }
}
const isValid = droll.validate(value);
const isValid = droll.validate(value);
if (isValid) {
if (isValid) { const result = droll.roll(value);
const result = droll.roll(value); const context = getContext();
const context = getContext(); context.sendSystemMessage('generic', `${context.name1} rolls a ${value}. The result is: ${result.total} (${result.rolls})`, { isSmallSys: true });
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 = ` function addDiceRollButton() {
<div id="roll_dice" class="list-group-item flex-container flexGap5"> const buttonHtml = `
<div class="fa-solid fa-dice extensionsMenuExtensionButton" title="Roll Dice" /></div> <div id="roll_dice" class="list-group-item flex-container flexGap5">
Roll Dice <div class="fa-solid fa-dice extensionsMenuExtensionButton" title="Roll Dice" /></div>
</div> Roll Dice
`; </div>
const dropdownHtml = ` `;
<div id="dice_dropdown"> const dropdownHtml = `
<ul class="list-group"> <div id="dice_dropdown">
<li class="list-group-item" data-value="d4">d4</li> <ul class="list-group">
<li class="list-group-item" data-value="d6">d6</li> <li class="list-group-item" data-value="d4">d4</li>
<li class="list-group-item" data-value="d8">d8</li> <li class="list-group-item" data-value="d6">d6</li>
<li class="list-group-item" data-value="d10">d10</li> <li class="list-group-item" data-value="d8">d8</li>
<li class="list-group-item" data-value="d12">d12</li> <li class="list-group-item" data-value="d10">d10</li>
<li class="list-group-item" data-value="d20">d20</li> <li class="list-group-item" data-value="d12">d12</li>
<li class="list-group-item" data-value="d100">d100</li> <li class="list-group-item" data-value="d20">d20</li>
<li class="list-group-item" data-value="custom">...</li> <li class="list-group-item" data-value="d100">d100</li>
</ul> <li class="list-group-item" data-value="custom">...</li>
</div>`; </ul>
</div>`;
$('#extensionsMenu').prepend(buttonHtml);
$('#extensionsMenu').prepend(buttonHtml);
$(document.body).append(dropdownHtml)
$('#dice_dropdown li').on('click', doDiceRoll); $(document.body).append(dropdownHtml)
const button = $('#roll_dice'); $('#dice_dropdown li').on('click', doDiceRoll);
const dropdown = $('#dice_dropdown'); const button = $('#roll_dice');
dropdown.hide(); const dropdown = $('#dice_dropdown');
button.hide(); dropdown.hide();
button.hide();
let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
placement: 'bottom', let popper = Popper.createPopper(button.get(0), dropdown.get(0), {
}); placement: 'bottom',
});
$(document).on('click touchend', function (e) {
const target = $(e.target); $(document).on('click touchend', function (e) {
if (target.is(dropdown)) return; const target = $(e.target);
if (target.is(button) && !dropdown.is(":visible")) { if (target.is(dropdown)) return;
e.preventDefault(); if (target.is(button) && !dropdown.is(":visible")) {
e.preventDefault();
dropdown.show(200);
popper.update(); dropdown.show(200);
} else { popper.update();
dropdown.hide(200); } else {
} dropdown.hide(200);
}); }
} });
}
function addDiceScript() {
if (!window.droll) { function addDiceScript() {
const script = document.createElement('script'); if (!window.droll) {
script.src = "/scripts/extensions/dice/droll.js"; const script = document.createElement('script');
document.body.appendChild(script); script.src = "/scripts/extensions/dice/droll.js";
} document.body.appendChild(script);
} }
}
async function moduleWorker() {
const context = getContext(); async function moduleWorker() {
$('#roll_dice').toggle(getContext().onlineStatus !== 'no_connection');
context.onlineStatus === 'no_connection' }
? $('#roll_dice').hide(200)
: $('#roll_dice').show(200); jQuery(function () {
} addDiceScript();
addDiceRollButton();
$(document).ready(function () { moduleWorker();
addDiceScript(); setInterval(moduleWorker, UPDATE_INTERVAL);
addDiceRollButton(); registerSlashCommand('roll', (_, value) => doDiceRoll(value), [], "<span class='monospace'>(dice formula)</span> roll the dice. For example, /roll 2d6", false, true);
setDiceIcon(); });
moduleWorker();
setInterval(moduleWorker, UPDATE_INTERVAL);
});

View File

@ -1,5 +1,5 @@
import { callPopup, getRequestHeaders, saveSettingsDebounced } from "../../../script.js"; 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 }; export { MODULE_NAME };
const MODULE_NAME = 'expressions'; 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() { async function moduleWorker() {
const context = getContext(); const context = getContext();
@ -509,6 +491,7 @@ async function onClickExpressionDelete(event) {
addExpressionImage(); addExpressionImage();
addSettings(); addSettings();
setInterval(moduleWorkerWrapper, UPDATE_INTERVAL); const wrapper = new ModuleWorkerWrapper(moduleWorker);
moduleWorkerWrapper(); setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
moduleWorker();
})(); })();

View File

@ -1,234 +1,217 @@
import { chat_metadata, saveSettingsDebounced } from "../../../script.js"; import { chat_metadata, saveSettingsDebounced } from "../../../script.js";
import { extension_settings, getContext } from "../../extensions.js"; import { ModuleWorkerWrapper, extension_settings, getContext } from "../../extensions.js";
import { registerSlashCommand } from "../../slash-commands.js"; import { registerSlashCommand } from "../../slash-commands.js";
import { debounce } from "../../utils.js"; import { debounce } from "../../utils.js";
export { MODULE_NAME }; export { MODULE_NAME };
const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000); const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
const UPDATE_INTERVAL = 1000; const UPDATE_INTERVAL = 1000;
const DEFAULT_DEPTH = 4; const DEFAULT_DEPTH = 4;
const DEFAULT_POSITION = 1; const DEFAULT_POSITION = 1;
const DEFAULT_INTERVAL = 1; const DEFAULT_INTERVAL = 1;
const metadata_keys = { const metadata_keys = {
prompt: 'note_prompt', prompt: 'note_prompt',
interval: 'note_interval', interval: 'note_interval',
depth: 'note_depth', depth: 'note_depth',
position: 'note_position', position: 'note_position',
} }
function setNoteTextCommand(_, text) { function setNoteTextCommand(_, text) {
$('#extension_floating_prompt').val(text).trigger('input'); $('#extension_floating_prompt').val(text).trigger('input');
toastr.success("Author's Note text updated"); toastr.success("Author's Note text updated");
} }
function setNoteDepthCommand(_, text) { function setNoteDepthCommand(_, text) {
const value = Number(text); const value = Number(text);
if (Number.isNaN(value)) { if (Number.isNaN(value)) {
toastr.error('Not a valid number'); toastr.error('Not a valid number');
return; return;
} }
$('#extension_floating_depth').val(Math.abs(value)).trigger('input'); $('#extension_floating_depth').val(Math.abs(value)).trigger('input');
toastr.success("Author's Note depth updated"); toastr.success("Author's Note depth updated");
} }
function setNoteIntervalCommand(_, text) { function setNoteIntervalCommand(_, text) {
const value = Number(text); const value = Number(text);
if (Number.isNaN(value)) { if (Number.isNaN(value)) {
toastr.error('Not a valid number'); toastr.error('Not a valid number');
return; return;
} }
$('#extension_floating_interval').val(Math.abs(value)).trigger('input'); $('#extension_floating_interval').val(Math.abs(value)).trigger('input');
toastr.success("Author's Note frequency updated"); toastr.success("Author's Note frequency updated");
} }
function setNotePositionCommand(_, text) { function setNotePositionCommand(_, text) {
const validPositions = { const validPositions = {
'scenario': 0, 'scenario': 0,
'chat': 1, 'chat': 1,
}; };
const position = validPositions[text?.trim()]; const position = validPositions[text?.trim()];
if (Number.isNaN(position)) { if (Number.isNaN(position)) {
toastr.error('Not a valid position'); toastr.error('Not a valid position');
return; return;
} }
$(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input'); $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('input');
toastr.info("Author's Note position updated"); toastr.info("Author's Note position updated");
} }
async function onExtensionFloatingPromptInput() { async function onExtensionFloatingPromptInput() {
chat_metadata[metadata_keys.prompt] = $(this).val(); chat_metadata[metadata_keys.prompt] = $(this).val();
saveMetadataDebounced(); saveMetadataDebounced();
} }
async function onExtensionFloatingIntervalInput() { async function onExtensionFloatingIntervalInput() {
chat_metadata[metadata_keys.interval] = Number($(this).val()); chat_metadata[metadata_keys.interval] = Number($(this).val());
saveMetadataDebounced(); saveMetadataDebounced();
} }
async function onExtensionFloatingDepthInput() { async function onExtensionFloatingDepthInput() {
let value = Number($(this).val()); let value = Number($(this).val());
if (value < 0) { if (value < 0) {
value = Math.abs(value); value = Math.abs(value);
$(this).val(value); $(this).val(value);
} }
chat_metadata[metadata_keys.depth] = value; chat_metadata[metadata_keys.depth] = value;
saveMetadataDebounced(); saveMetadataDebounced();
} }
async function onExtensionFloatingPositionInput(e) { async function onExtensionFloatingPositionInput(e) {
chat_metadata[metadata_keys.position] = e.target.value; chat_metadata[metadata_keys.position] = e.target.value;
saveMetadataDebounced(); saveMetadataDebounced();
} }
function onExtensionFloatingDefaultInput() { function onExtensionFloatingDefaultInput() {
extension_settings.note.default = $(this).val(); extension_settings.note.default = $(this).val();
saveSettingsDebounced(); saveSettingsDebounced();
} }
function loadSettings() { function loadSettings() {
chat_metadata[metadata_keys.prompt] = chat_metadata[metadata_keys.prompt] ?? extension_settings.note.default ?? ''; 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.interval] = chat_metadata[metadata_keys.interval] ?? DEFAULT_INTERVAL;
chat_metadata[metadata_keys.position] = chat_metadata[metadata_keys.position] ?? DEFAULT_POSITION; chat_metadata[metadata_keys.position] = chat_metadata[metadata_keys.position] ?? DEFAULT_POSITION;
chat_metadata[metadata_keys.depth] = chat_metadata[metadata_keys.depth] ?? DEFAULT_DEPTH; chat_metadata[metadata_keys.depth] = chat_metadata[metadata_keys.depth] ?? DEFAULT_DEPTH;
$('#extension_floating_prompt').val(chat_metadata[metadata_keys.prompt]); $('#extension_floating_prompt').val(chat_metadata[metadata_keys.prompt]);
$('#extension_floating_interval').val(chat_metadata[metadata_keys.interval]); $('#extension_floating_interval').val(chat_metadata[metadata_keys.interval]);
$('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]); $('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]);
$(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true); $(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true);
$('#extension_floating_default').val(extension_settings.note.default); $('#extension_floating_default').val(extension_settings.note.default);
} }
let isWorkerBusy = false; async function moduleWorker() {
const context = getContext();
async function moduleWorkerWrapper() {
// Don't touch me I'm busy... if (!context.groupId && context.characterId === undefined) {
if (isWorkerBusy) { return;
return; }
}
loadSettings();
// I'm free. Let's update!
try { // take the count of messages
isWorkerBusy = true; let lastMessageNumber = Array.isArray(context.chat) && context.chat.length ? context.chat.filter(m => m.is_user).length : 0;
await moduleWorker();
} // special case for new chat
finally { if (Array.isArray(context.chat) && context.chat.length === 1) {
isWorkerBusy = false; lastMessageNumber = 1;
} }
}
if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
async function moduleWorker() { context.setExtensionPrompt(MODULE_NAME, '');
const context = getContext(); $('#extension_floating_counter').text('(disabled)');
return;
if (!context.groupId && context.characterId === undefined) { }
return;
} const messagesTillInsertion = lastMessageNumber >= chat_metadata[metadata_keys.interval]
? (lastMessageNumber % chat_metadata[metadata_keys.interval])
loadSettings(); : (chat_metadata[metadata_keys.interval] - lastMessageNumber);
const shouldAddPrompt = messagesTillInsertion == 0;
// take the count of messages const prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : '';
let lastMessageNumber = Array.isArray(context.chat) && context.chat.length ? context.chat.filter(m => m.is_user).length : 0; context.setExtensionPrompt(MODULE_NAME, prompt, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
$('#extension_floating_counter').text(shouldAddPrompt ? '0' : messagesTillInsertion);
// special case for new chat }
if (Array.isArray(context.chat) && context.chat.length === 1) {
lastMessageNumber = 1; (function () {
} function addExtensionsSettings() {
const settingsHtml = `
if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) { <div id="floatingPrompt" class="drawer-content flexGap5">
context.setExtensionPrompt(MODULE_NAME, ''); <div id="floatingPromptheader" class="fa-solid fa-grip drag-grabber"></div>
$('#extension_floating_counter').text('(disabled)'); <div name="floatingPromptHolder">
return; <div class="inline-drawer">
} <div id="ANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<b>Author's Note</b>
const messagesTillInsertion = lastMessageNumber >= chat_metadata[metadata_keys.interval] <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
? (lastMessageNumber % chat_metadata[metadata_keys.interval]) </div>
: (chat_metadata[metadata_keys.interval] - lastMessageNumber); <div class="inline-drawer-content">
const shouldAddPrompt = messagesTillInsertion == 0; <small>
const prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : ''; <b>Unique to this chat</b>.<br>
context.setExtensionPrompt(MODULE_NAME, prompt, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]); Bookmarks inherit the Note from their parent, and can be changed individually after that.<br>
$('#extension_floating_counter').text(shouldAddPrompt ? '0' : messagesTillInsertion); </small>
}
<textarea id="extension_floating_prompt" class="text_pole" rows="8" maxlength="10000"></textarea>
(function () {
function addExtensionsSettings() { <div class="floating_prompt_radio_group">
const settingsHtml = ` <label>
<div id="floatingPrompt" class="drawer-content flexGap5"> <input type="radio" name="extension_floating_position" value="0" />
<div id="floatingPromptheader" class="fa-solid fa-grip drag-grabber"></div> After scenario
<div name="floatingPromptHolder"> </label>
<div class="inline-drawer"> <label>
<div id="ANBlockToggle" class="inline-drawer-toggle inline-drawer-header"> <input type="radio" name="extension_floating_position" value="1" />
<b>Author's Note</b> In-chat @ Depth <input id="extension_floating_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> </label>
</div> </div>
<div class="inline-drawer-content"> <!--<label for="extension_floating_interval">In-Chat Insertion Depth</label>-->
<small>
<b>Unique to this chat</b>.<br> <label for="extension_floating_interval">Insertion Frequency</label>
Bookmarks inherit the Note from their parent, and can be changed individually after that.<br>
</small> <input id="extension_floating_interval" class="text_pole widthUnset" type="number" min="0" max="999" /><small> (0 = Disable)</small>
<br>
<textarea id="extension_floating_prompt" class="text_pole" rows="8" maxlength="10000"></textarea>
<span>User inputs until next insertion: <span id="extension_floating_counter">(disabled)</span></span>
<div class="floating_prompt_radio_group">
<label> </div>
<input type="radio" name="extension_floating_position" value="0" /> </div>
After scenario <hr class="sysHR">
</label> <div class="inline-drawer">
<label> <div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
<input type="radio" name="extension_floating_position" value="1" /> <b>Default Author's Note</b>
In-chat @ Depth <input id="extension_floating_depth" class="text_pole widthUnset" type="number" min="0" max="99" /> <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</label> </div>
</div> <div class="inline-drawer-content">
<!--<label for="extension_floating_interval">In-Chat Insertion Depth</label>--> <small>Will be automatically added as the Author's Note for all new chats.</small>
<label for="extension_floating_interval">Insertion Frequency</label> <textarea id="extension_floating_default" class="text_pole" rows="8" maxlength="10000"
placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
<input id="extension_floating_interval" class="text_pole widthUnset" type="number" min="0" max="999" /><small> (0 = Disable)</small> </div>
<br> </div>
</div>
<span>User inputs until next insertion: <span id="extension_floating_counter">(disabled)</span></span> </div>
`;
</div>
</div> $('#movingDivs').append(settingsHtml);
<hr class="sysHR"> $('#extension_floating_prompt').on('input', onExtensionFloatingPromptInput);
<div class="inline-drawer"> $('#extension_floating_interval').on('input', onExtensionFloatingIntervalInput);
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header"> $('#extension_floating_depth').on('input', onExtensionFloatingDepthInput);
<b>Default Author's Note</b> $('#extension_floating_default').on('input', onExtensionFloatingDefaultInput);
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> $('input[name="extension_floating_position"]').on('change', onExtensionFloatingPositionInput);
</div> }
<div class="inline-drawer-content">
<small>Will be automatically added as the Author's Note for all new chats.</small> addExtensionsSettings();
const wrapper = new ModuleWorkerWrapper(moduleWorker);
<textarea id="extension_floating_default" class="text_pole" rows="8" maxlength="10000" setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea> registerSlashCommand('note', setNoteTextCommand, [], "<span class='monospace'>(text)</span> sets an author's note for the currently selected chat", true, true);
</div> registerSlashCommand('depth', setNoteDepthCommand, [], "<span class='monospace'>(number)</span> sets an author's note depth for in-chat positioning", true, true);
</div> registerSlashCommand('freq', setNoteIntervalCommand, ['interval'], "<span class='monospace'>(number)</span> sets an author's note insertion frequency", true, true);
</div> registerSlashCommand('pos', setNotePositionCommand, ['position'], "(<span class='monospace'>chat</span> or <span class='monospace'>scenario</span>) sets an author's note position", true, true);
</div> })();
`;
$('#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, [], "<span class='monospace'>(text)</span> sets an author's note for the currently selected chat", true, true);
registerSlashCommand('depth', setNoteDepthCommand, [], "<span class='monospace'>(number)</span> sets an author's note depth for in-chat positioning", true, true);
registerSlashCommand('freq', setNoteIntervalCommand, ['interval'], "<span class='monospace'>(number)</span> sets an author's note insertion frequency", true, true);
registerSlashCommand('pos', setNotePositionCommand, ['position'], "(<span class='monospace'>chat</span> or <span class='monospace'>scenario</span>) sets an author's note position", true, true);
})();

View File

@ -1,388 +1,371 @@
import { getStringHash, debounce } from "../../utils.js"; import { getStringHash, debounce } from "../../utils.js";
import { getContext, getApiUrl, extension_settings } from "../../extensions.js"; import { getContext, getApiUrl, extension_settings, ModuleWorkerWrapper } from "../../extensions.js";
import { extension_prompt_types, is_send_press, saveSettingsDebounced } from "../../../script.js"; import { extension_prompt_types, is_send_press, saveSettingsDebounced } from "../../../script.js";
export { MODULE_NAME }; export { MODULE_NAME };
const MODULE_NAME = '1_memory'; const MODULE_NAME = '1_memory';
const UPDATE_INTERVAL = 5000; const UPDATE_INTERVAL = 5000;
let lastCharacterId = null; let lastCharacterId = null;
let lastGroupId = null; let lastGroupId = null;
let lastChatId = null; let lastChatId = null;
let lastMessageHash = null; let lastMessageHash = null;
let lastMessageId = null; let lastMessageId = null;
let inApiCall = false; let inApiCall = false;
const formatMemoryValue = (value) => value ? `Context: ${value.trim()}` : ''; const formatMemoryValue = (value) => value ? `Context: ${value.trim()}` : '';
const saveChatDebounced = debounce(() => getContext().saveChat(), 2000); const saveChatDebounced = debounce(() => getContext().saveChat(), 2000);
const defaultSettings = { const defaultSettings = {
minLongMemory: 16, minLongMemory: 16,
maxLongMemory: 1024, maxLongMemory: 1024,
longMemoryLength: 128, longMemoryLength: 128,
shortMemoryLength: 512, shortMemoryLength: 512,
minShortMemory: 128, minShortMemory: 128,
maxShortMemory: 1024, maxShortMemory: 1024,
shortMemoryStep: 16, shortMemoryStep: 16,
longMemoryStep: 8, longMemoryStep: 8,
repetitionPenaltyStep: 0.05, repetitionPenaltyStep: 0.05,
repetitionPenalty: 1.2, repetitionPenalty: 1.2,
maxRepetitionPenalty: 2.0, maxRepetitionPenalty: 2.0,
minRepetitionPenalty: 1.0, minRepetitionPenalty: 1.0,
temperature: 1.0, temperature: 1.0,
minTemperature: 0.1, minTemperature: 0.1,
maxTemperature: 2.0, maxTemperature: 2.0,
temperatureStep: 0.05, temperatureStep: 0.05,
lengthPenalty: 1, lengthPenalty: 1,
minLengthPenalty: -4, minLengthPenalty: -4,
maxLengthPenalty: 4, maxLengthPenalty: 4,
lengthPenaltyStep: 0.1, lengthPenaltyStep: 0.1,
memoryFrozen: false, memoryFrozen: false,
}; };
function loadSettings() { function loadSettings() {
if (Object.keys(extension_settings.memory).length === 0) { if (Object.keys(extension_settings.memory).length === 0) {
Object.assign(extension_settings.memory, defaultSettings); Object.assign(extension_settings.memory, defaultSettings);
} }
$('#memory_long_length').val(extension_settings.memory.longMemoryLength).trigger('input'); $('#memory_long_length').val(extension_settings.memory.longMemoryLength).trigger('input');
$('#memory_short_length').val(extension_settings.memory.shortMemoryLength).trigger('input'); $('#memory_short_length').val(extension_settings.memory.shortMemoryLength).trigger('input');
$('#memory_repetition_penalty').val(extension_settings.memory.repetitionPenalty).trigger('input'); $('#memory_repetition_penalty').val(extension_settings.memory.repetitionPenalty).trigger('input');
$('#memory_temperature').val(extension_settings.memory.temperature).trigger('input'); $('#memory_temperature').val(extension_settings.memory.temperature).trigger('input');
$('#memory_length_penalty').val(extension_settings.memory.lengthPenalty).trigger('input'); $('#memory_length_penalty').val(extension_settings.memory.lengthPenalty).trigger('input');
$('#memory_frozen').prop('checked', extension_settings.memory.memoryFrozen).trigger('input'); $('#memory_frozen').prop('checked', extension_settings.memory.memoryFrozen).trigger('input');
} }
function onMemoryShortInput() { function onMemoryShortInput() {
const value = $(this).val(); const value = $(this).val();
extension_settings.memory.shortMemoryLength = Number(value); extension_settings.memory.shortMemoryLength = Number(value);
$('#memory_short_length_tokens').text(value); $('#memory_short_length_tokens').text(value);
saveSettingsDebounced(); saveSettingsDebounced();
// Don't let long buffer be bigger than short // Don't let long buffer be bigger than short
if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) { if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) {
$('#memory_long_length').val(extension_settings.memory.shortMemoryLength).trigger('input'); $('#memory_long_length').val(extension_settings.memory.shortMemoryLength).trigger('input');
} }
} }
function onMemoryLongInput() { function onMemoryLongInput() {
const value = $(this).val(); const value = $(this).val();
extension_settings.memory.longMemoryLength = Number(value); extension_settings.memory.longMemoryLength = Number(value);
$('#memory_long_length_tokens').text(value); $('#memory_long_length_tokens').text(value);
saveSettingsDebounced(); saveSettingsDebounced();
// Don't let long buffer be bigger than short // Don't let long buffer be bigger than short
if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) { if (extension_settings.memory.longMemoryLength > extension_settings.memory.shortMemoryLength) {
$('#memory_short_length').val(extension_settings.memory.longMemoryLength).trigger('input'); $('#memory_short_length').val(extension_settings.memory.longMemoryLength).trigger('input');
} }
} }
function onMemoryRepetitionPenaltyInput() { function onMemoryRepetitionPenaltyInput() {
const value = $(this).val(); const value = $(this).val();
extension_settings.memory.repetitionPenalty = Number(value); extension_settings.memory.repetitionPenalty = Number(value);
$('#memory_repetition_penalty_value').text(extension_settings.memory.repetitionPenalty.toFixed(2)); $('#memory_repetition_penalty_value').text(extension_settings.memory.repetitionPenalty.toFixed(2));
saveSettingsDebounced(); saveSettingsDebounced();
} }
function onMemoryTemperatureInput() { function onMemoryTemperatureInput() {
const value = $(this).val(); const value = $(this).val();
extension_settings.memory.temperature = Number(value); extension_settings.memory.temperature = Number(value);
$('#memory_temperature_value').text(extension_settings.memory.temperature.toFixed(2)); $('#memory_temperature_value').text(extension_settings.memory.temperature.toFixed(2));
saveSettingsDebounced(); saveSettingsDebounced();
} }
function onMemoryLengthPenaltyInput() { function onMemoryLengthPenaltyInput() {
const value = $(this).val(); const value = $(this).val();
extension_settings.memory.lengthPenalty = Number(value); extension_settings.memory.lengthPenalty = Number(value);
$('#memory_length_penalty_value').text(extension_settings.memory.lengthPenalty.toFixed(2)); $('#memory_length_penalty_value').text(extension_settings.memory.lengthPenalty.toFixed(2));
saveSettingsDebounced(); saveSettingsDebounced();
} }
function onMemoryFrozenInput() { function onMemoryFrozenInput() {
const value = Boolean($(this).prop('checked')); const value = Boolean($(this).prop('checked'));
extension_settings.memory.memoryFrozen = value; extension_settings.memory.memoryFrozen = value;
saveSettingsDebounced(); saveSettingsDebounced();
} }
function saveLastValues() { function saveLastValues() {
const context = getContext(); const context = getContext();
lastGroupId = context.groupId; lastGroupId = context.groupId;
lastCharacterId = context.characterId; lastCharacterId = context.characterId;
lastChatId = context.chatId; lastChatId = context.chatId;
lastMessageId = context.chat?.length ?? null; lastMessageId = context.chat?.length ?? null;
lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['mes']) ?? ''); lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['mes']) ?? '');
} }
function getLatestMemoryFromChat(chat) { function getLatestMemoryFromChat(chat) {
if (!Array.isArray(chat) || !chat.length) { if (!Array.isArray(chat) || !chat.length) {
return ''; return '';
} }
const reversedChat = chat.slice().reverse(); const reversedChat = chat.slice().reverse();
reversedChat.shift(); reversedChat.shift();
for (let mes of reversedChat) { for (let mes of reversedChat) {
if (mes.extra && mes.extra.memory) { if (mes.extra && mes.extra.memory) {
return mes.extra.memory; return mes.extra.memory;
} }
} }
return ''; return '';
} }
let isWorkerBusy = false; async function moduleWorker() {
const context = getContext();
async function moduleWorkerWrapper() { const chat = context.chat;
// Don't touch me I'm busy...
if (isWorkerBusy) { // no characters or group selected
return; if (!context.groupId && context.characterId === undefined) {
} return;
}
// I'm free. Let's update!
try { // Generation is in progress, summary prevented
isWorkerBusy = true; if (is_send_press) {
await moduleWorker(); return;
} }
finally {
isWorkerBusy = false; // Chat/character/group changed
} if ((context.groupId && lastGroupId !== context.groupId) || (context.characterId !== lastCharacterId) || (context.chatId !== lastChatId)) {
} const latestMemory = getLatestMemoryFromChat(chat);
setMemoryContext(latestMemory, false);
async function moduleWorker() { saveLastValues();
const context = getContext(); return;
const chat = context.chat; }
// no characters or group selected // Currently summarizing or frozen state - skip
if (!context.groupId && context.characterId === undefined) { if (inApiCall || extension_settings.memory.memoryFrozen) {
return; return;
} }
// Generation is in progress, summary prevented // No new messages - do nothing
if (is_send_press) { if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) {
return; return;
} }
// Chat/character/group changed // Messages has been deleted - rewrite the context with the latest available memory
if ((context.groupId && lastGroupId !== context.groupId) || (context.characterId !== lastCharacterId) || (context.chatId !== lastChatId)) { if (chat.length < lastMessageId) {
const latestMemory = getLatestMemoryFromChat(chat); const latestMemory = getLatestMemoryFromChat(chat);
setMemoryContext(latestMemory, false); setMemoryContext(latestMemory, false);
saveLastValues(); }
return;
} // Message has been edited / regenerated - delete the saved memory
if (chat.length
// Currently summarizing or frozen state - skip && chat[chat.length - 1].extra
if (inApiCall || extension_settings.memory.memoryFrozen) { && chat[chat.length - 1].extra.memory
return; && lastMessageId === chat.length
} && getStringHash(chat[chat.length - 1].mes) !== lastMessageHash) {
delete chat[chat.length - 1].extra.memory;
// No new messages - do nothing }
if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) {
return; try {
} await summarizeChat(context);
}
// Messages has been deleted - rewrite the context with the latest available memory catch (error) {
if (chat.length < lastMessageId) { console.log(error);
const latestMemory = getLatestMemoryFromChat(chat); }
setMemoryContext(latestMemory, false); finally {
} saveLastValues();
}
// Message has been edited / regenerated - delete the saved memory }
if (chat.length
&& chat[chat.length - 1].extra async function summarizeChat(context) {
&& chat[chat.length - 1].extra.memory function getMemoryString() {
&& lastMessageId === chat.length return (longMemory + '\n\n' + memoryBuffer.slice().reverse().join('\n\n')).trim();
&& getStringHash(chat[chat.length - 1].mes) !== lastMessageHash) { }
delete chat[chat.length - 1].extra.memory;
} const chat = context.chat;
const longMemory = getLatestMemoryFromChat(chat);
try { const reversedChat = chat.slice().reverse();
await summarizeChat(context); reversedChat.shift();
} let memoryBuffer = [];
catch (error) {
console.log(error); for (let mes of reversedChat) {
} // we reached the point of latest memory
finally { if (longMemory && mes.extra && mes.extra.memory == longMemory) {
saveLastValues(); break;
} }
}
// don't care about system
async function summarizeChat(context) { if (mes.is_system) {
function getMemoryString() { continue;
return (longMemory + '\n\n' + memoryBuffer.slice().reverse().join('\n\n')).trim(); }
}
// determine the sender's name
const chat = context.chat; const name = mes.is_user ? (context.name1 ?? 'You') : (mes.force_avatar ? mes.name : context.name2);
const longMemory = getLatestMemoryFromChat(chat); const entry = `${name}:\n${mes['mes']}`;
const reversedChat = chat.slice().reverse(); memoryBuffer.push(entry);
reversedChat.shift();
let memoryBuffer = []; // check if token limit was reached
if (context.getTokenCount(getMemoryString()) >= extension_settings.memory.shortMemoryLength) {
for (let mes of reversedChat) { break;
// we reached the point of latest memory }
if (longMemory && mes.extra && mes.extra.memory == longMemory) { }
break;
} const resultingString = getMemoryString();
// don't care about system if (context.getTokenCount(resultingString) < extension_settings.memory.shortMemoryLength) {
if (mes.is_system) { return;
continue; }
}
// perform the summarization API call
// determine the sender's name try {
const name = mes.is_user ? (context.name1 ?? 'You') : (mes.force_avatar ? mes.name : context.name2); inApiCall = true;
const entry = `${name}:\n${mes['mes']}`; const url = new URL(getApiUrl());
memoryBuffer.push(entry); url.pathname = '/api/summarize';
// check if token limit was reached const apiResult = await fetch(url, {
if (context.getTokenCount(getMemoryString()) >= extension_settings.memory.shortMemoryLength) { method: 'POST',
break; headers: {
} 'Content-Type': 'application/json',
} 'Bypass-Tunnel-Reminder': 'bypass',
},
const resultingString = getMemoryString(); body: JSON.stringify({
text: resultingString,
if (context.getTokenCount(resultingString) < extension_settings.memory.shortMemoryLength) { params: {
return; 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,
// perform the summarization API call temperature: extension_settings.memory.temperature,
try { length_penalty: extension_settings.memory.lengthPenalty,
inApiCall = true; }
const url = new URL(getApiUrl()); })
url.pathname = '/api/summarize'; });
const apiResult = await fetch(url, { if (apiResult.ok) {
method: 'POST', const data = await apiResult.json();
headers: { const summary = data.summary;
'Content-Type': 'application/json',
'Bypass-Tunnel-Reminder': 'bypass', const newContext = getContext();
},
body: JSON.stringify({ // something changed during summarization request
text: resultingString, if (newContext.groupId !== context.groupId
params: { || newContext.chatId !== context.chatId
min_length: extension_settings.memory.longMemoryLength * 0, // testing how it behaves 0 min length || (!newContext.groupId && (newContext.characterId !== context.characterId))) {
max_length: extension_settings.memory.longMemoryLength, console.log('Context changed, summary discarded');
repetition_penalty: extension_settings.memory.repetitionPenalty, return;
temperature: extension_settings.memory.temperature, }
length_penalty: extension_settings.memory.lengthPenalty,
} setMemoryContext(summary, true);
}) }
}); }
catch (error) {
if (apiResult.ok) { console.log(error);
const data = await apiResult.json(); }
const summary = data.summary; finally {
inApiCall = false;
const newContext = getContext(); }
}
// something changed during summarization request
if (newContext.groupId !== context.groupId function onMemoryRestoreClick() {
|| newContext.chatId !== context.chatId const context = getContext();
|| (!newContext.groupId && (newContext.characterId !== context.characterId))) { const content = $('#memory_contents').val();
console.log('Context changed, summary discarded'); const reversedChat = context.chat.slice().reverse();
return; reversedChat.shift();
}
for (let mes of reversedChat) {
setMemoryContext(summary, true); if (mes.extra && mes.extra.memory == content) {
} delete mes.extra.memory;
} break;
catch (error) { }
console.log(error); }
}
finally { const newContent = getLatestMemoryFromChat(context.chat);
inApiCall = false; setMemoryContext(newContent, false);
} }
}
function onMemoryContentInput() {
function onMemoryRestoreClick() { const value = $(this).val();
const context = getContext(); setMemoryContext(value, true);
const content = $('#memory_contents').val(); }
const reversedChat = context.chat.slice().reverse();
reversedChat.shift(); function setMemoryContext(value, saveToMessage) {
const context = getContext();
for (let mes of reversedChat) { context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_prompt_types.AFTER_SCENARIO);
if (mes.extra && mes.extra.memory == content) { $('#memory_contents').val(value);
delete mes.extra.memory;
break; if (saveToMessage && context.chat.length) {
} const idx = context.chat.length - 2;
} const mes = context.chat[idx < 0 ? 0 : idx];
const newContent = getLatestMemoryFromChat(context.chat); if (!mes.extra) {
setMemoryContext(newContent, false); mes.extra = {};
} }
function onMemoryContentInput() { mes.extra.memory = value;
const value = $(this).val(); saveChatDebounced();
setMemoryContext(value, true); }
} }
function setMemoryContext(value, saveToMessage) { $(document).ready(function () {
const context = getContext(); function addExtensionControls() {
context.setExtensionPrompt(MODULE_NAME, formatMemoryValue(value), extension_prompt_types.AFTER_SCENARIO); const settingsHtml = `
$('#memory_contents').val(value); <div id="memory_settings">
<div class="inline-drawer">
if (saveToMessage && context.chat.length) { <div class="inline-drawer-toggle inline-drawer-header">
const idx = context.chat.length - 2; <b>Chat memory</b>
const mes = context.chat[idx < 0 ? 0 : idx]; <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
</div>
if (!mes.extra) { <div class="inline-drawer-content">
mes.extra = {}; <label for="memory_contents">Memory contents</label>
} <textarea id="memory_contents" class="text_pole" rows="8" placeholder="Context will be generated here..."></textarea>
<div class="memory_contents_controls">
mes.extra.memory = value; <input id="memory_restore" class="menu_button" type="submit" value="Restore previous state" />
saveChatDebounced(); <label for="memory_frozen"><input id="memory_frozen" type="checkbox" /> Freeze context</label>
} </div>
} </div>
</div>
$(document).ready(function () { <div class="inline-drawer">
function addExtensionControls() { <div class="inline-drawer-toggle inline-drawer-header">
const settingsHtml = ` <b>Summarization parameters</b>
<div id="memory_settings"> <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
<div class="inline-drawer"> </div>
<div class="inline-drawer-toggle inline-drawer-header"> <div class="inline-drawer-content">
<b>Chat memory</b> <label for="memory_short_length">Buffer <small>[short-term]</small> length (<span id="memory_short_length_tokens"></span> tokens)</label>
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> <input id="memory_short_length" type="range" value="${defaultSettings.shortMemoryLength}" min="${defaultSettings.minShortMemory}" max="${defaultSettings.maxShortMemory}" step="${defaultSettings.shortMemoryStep}" />
</div> <label for="memory_long_length">Summary <small>[long-term]</small> length (<span id="memory_long_length_tokens"></span> tokens)</label>
<div class="inline-drawer-content"> <input id="memory_long_length" type="range" value="${defaultSettings.longMemoryLength}" min="${defaultSettings.minLongMemory}" max="${defaultSettings.maxLongMemory}" step="${defaultSettings.longMemoryStep}" />
<label for="memory_contents">Memory contents</label> <label for="memory_temperature">Temperature (<span id="memory_temperature_value"></span>)</label>
<textarea id="memory_contents" class="text_pole" rows="8" placeholder="Context will be generated here..."></textarea> <input id="memory_temperature" type="range" value="${defaultSettings.temperature}" min="${defaultSettings.minTemperature}" max="${defaultSettings.maxTemperature}" step="${defaultSettings.temperatureStep}" />
<div class="memory_contents_controls"> <label for="memory_repetition_penalty">Repetition penalty (<span id="memory_repetition_penalty_value"></span>)</label>
<input id="memory_restore" class="menu_button" type="submit" value="Restore previous state" /> <input id="memory_repetition_penalty" type="range" value="${defaultSettings.repetitionPenalty}" min="${defaultSettings.minRepetitionPenalty}" max="${defaultSettings.maxRepetitionPenalty}" step="${defaultSettings.repetitionPenaltyStep}" />
<label for="memory_frozen"><input id="memory_frozen" type="checkbox" /> Freeze context</label> <label for="memory_length_penalty">Length penalty <small>[higher = longer summaries]</small> (<span id="memory_length_penalty_value"></span>)</label>
</div> <input id="memory_length_penalty" type="range" value="${defaultSettings.lengthPenalty}" min="${defaultSettings.minLengthPenalty}" max="${defaultSettings.maxLengthPenalty}" step="${defaultSettings.lengthPenaltyStep}" />
</div> </div>
</div> </div>
<div class="inline-drawer"> </div>
<div class="inline-drawer-toggle inline-drawer-header"> `;
<b>Summarization parameters</b> $('#extensions_settings').append(settingsHtml);
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div> $('#memory_restore').on('click', onMemoryRestoreClick);
</div> $('#memory_contents').on('input', onMemoryContentInput);
<div class="inline-drawer-content"> $('#memory_long_length').on('input', onMemoryLongInput);
<label for="memory_short_length">Buffer <small>[short-term]</small> length (<span id="memory_short_length_tokens"></span> tokens)</label> $('#memory_short_length').on('input', onMemoryShortInput);
<input id="memory_short_length" type="range" value="${defaultSettings.shortMemoryLength}" min="${defaultSettings.minShortMemory}" max="${defaultSettings.maxShortMemory}" step="${defaultSettings.shortMemoryStep}" /> $('#memory_repetition_penalty').on('input', onMemoryRepetitionPenaltyInput);
<label for="memory_long_length">Summary <small>[long-term]</small> length (<span id="memory_long_length_tokens"></span> tokens)</label> $('#memory_temperature').on('input', onMemoryTemperatureInput);
<input id="memory_long_length" type="range" value="${defaultSettings.longMemoryLength}" min="${defaultSettings.minLongMemory}" max="${defaultSettings.maxLongMemory}" step="${defaultSettings.longMemoryStep}" /> $('#memory_length_penalty').on('input', onMemoryLengthPenaltyInput);
<label for="memory_temperature">Temperature (<span id="memory_temperature_value"></span>)</label> $('#memory_frozen').on('input', onMemoryFrozenInput);
<input id="memory_temperature" type="range" value="${defaultSettings.temperature}" min="${defaultSettings.minTemperature}" max="${defaultSettings.maxTemperature}" step="${defaultSettings.temperatureStep}" /> }
<label for="memory_repetition_penalty">Repetition penalty (<span id="memory_repetition_penalty_value"></span>)</label>
<input id="memory_repetition_penalty" type="range" value="${defaultSettings.repetitionPenalty}" min="${defaultSettings.minRepetitionPenalty}" max="${defaultSettings.maxRepetitionPenalty}" step="${defaultSettings.repetitionPenaltyStep}" /> addExtensionControls();
<label for="memory_length_penalty">Length penalty <small>[higher = longer summaries]</small> (<span id="memory_length_penalty_value"></span>)</label> loadSettings();
<input id="memory_length_penalty" type="range" value="${defaultSettings.lengthPenalty}" min="${defaultSettings.minLengthPenalty}" max="${defaultSettings.maxLengthPenalty}" step="${defaultSettings.lengthPenaltyStep}" /> const wrapper = new ModuleWorkerWrapper(moduleWorker);
</div> setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL);
</div> });
</div>
`;
$('#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);
});

View File

@ -1,5 +1,5 @@
import { callPopup, cancelTtsPlay, eventSource, event_types, isMultigenEnabled, is_send_press, saveSettingsDebounced } from '../../../script.js' 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 { getStringHash } from '../../utils.js'
import { ElevenLabsTtsProvider } from './elevenlabs.js' import { ElevenLabsTtsProvider } from './elevenlabs.js'
import { SileroTtsProvider } from './silerotts.js' import { SileroTtsProvider } from './silerotts.js'
@ -38,24 +38,6 @@ async function onNarrateOneMessage() {
moduleWorker(); 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() { async function moduleWorker() {
// Primarily determining when to add new chat to the TTS queue // Primarily determining when to add new chat to the TTS queue
const enabled = $('#tts_enabled').is(':checked') const enabled = $('#tts_enabled').is(':checked')
@ -661,6 +643,7 @@ $(document).ready(function () {
loadSettings() // Depends on Extension Controls and loadTtsProvider loadSettings() // Depends on Extension Controls and loadTtsProvider
loadTtsProvider(extension_settings.tts.currentProvider) // No dependencies loadTtsProvider(extension_settings.tts.currentProvider) // No dependencies
addAudioControl() // Depends on Extension Controls 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); eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);
}) })