From fad4e4e75e074dd99bca3d5adc63b946b3aa7023 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:30:35 +0200 Subject: [PATCH 01/11] Add command and profile for custom stop strings --- .../extensions/connection-manager/index.js | 4 +++ public/scripts/power-user.js | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js index e9d0dd2fe..f31e4b495 100644 --- a/public/scripts/extensions/connection-manager/index.js +++ b/public/scripts/extensions/connection-manager/index.js @@ -30,6 +30,7 @@ const CC_COMMANDS = [ 'api-url', 'model', 'proxy', + 'stop-strings', ]; const TC_COMMANDS = [ @@ -43,6 +44,7 @@ const TC_COMMANDS = [ 'context', 'instruct-state', 'tokenizer', + 'stop-strings', ]; const FANCY_NAMES = { @@ -57,6 +59,7 @@ const FANCY_NAMES = { 'instruct': 'Instruct Template', 'context': 'Context Template', 'tokenizer': 'Tokenizer', + 'stop-strings': 'Custom Stopping Strings', }; /** @@ -138,6 +141,7 @@ const profilesProvider = () => [ * @property {string} [context] Context Template * @property {string} [instruct-state] Instruct Mode * @property {string} [tokenizer] Tokenizer + * @property {string} [stop-strings] Custom Stopping Strings * @property {string[]} [exclude] Commands to exclude */ diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 2a74f8722..2c1d5cd4c 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -4072,4 +4072,34 @@ $(document).ready(() => { ], helpString: 'activates a movingUI preset by name', })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'stop-strings', + aliases: ['stopping-strings'], + helpString: 'Sets a list of custom stopping strings. Gets the list if no value is provided.', + returns: ARGUMENT_TYPE.LIST, + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'list of strings', + typeList: [ARGUMENT_TYPE.LIST], + acceptsMultiple: false, + isRequired: false, + }), + ], + callback: (_, value) => { + if (String(value ?? '').trim()) { + const parsedValue = ((x) => { try { return JSON.parse(x.toString()); } catch { return null; } })(value); + if (!parsedValue || !Array.isArray(parsedValue)) { + throw new Error('Invalid list format. The value must be a JSON-serialized array of strings.'); + } + parsedValue.forEach((item, index) => { + parsedValue[index] = String(item); + }); + power_user.custom_stopping_strings = JSON.stringify(parsedValue); + $('#custom_stopping_strings').val(power_user.custom_stopping_strings); + saveSettingsDebounced(); + } + + return power_user.custom_stopping_strings; + }, + })); }); From 283ceb6bbfff486fdbbc8358c02099a4ff92a24d Mon Sep 17 00:00:00 2001 From: Dzmitry Kazlouski Date: Tue, 28 Jan 2025 02:51:05 +0300 Subject: [PATCH 02/11] Decrease TTS generation delay by splitting a message on a new line --- public/scripts/extensions/tts/index.js | 33 ++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 4b8978422..636103f23 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -120,7 +120,7 @@ async function onNarrateOneMessage() { } resetTtsPlayback(); - ttsJobQueue.push(message); + splitMessageAndAddToTtsJobQueue(message); moduleWorker(); } @@ -147,7 +147,7 @@ async function onNarrateText(args, text) { } resetTtsPlayback(); - ttsJobQueue.push({ mes: text, name: name }); + splitMessageAndAddToTtsJobQueue({ mes: text, name: name }); await moduleWorker(); // Return back to the chat voices @@ -220,6 +220,31 @@ function isTtsProcessing() { return processing; } +/** + * Splits a message into lines and adds each non-empty line to the TTS job queue. + * @param {Object} message - The message object to be processed. + * @param {string} message.mes - The text of the message to be split into lines. + * @param {string} message.name - The name associated with the message. + * @returns {void} + */ +function splitMessageAndAddToTtsJobQueue(message) { + const lines = message.mes.split("\n"); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + if (line.length === 0) { + continue; + } + + ttsJobQueue.push( + Object.assign({}, message, { + mes: line, + }) + ); + } +} + function debugTtsPlayback() { console.log(JSON.stringify( { @@ -350,7 +375,7 @@ function onAudioControlClicked() { talkingAnimation(false); } else { // Default play behavior if not processing or playing is to play the last message. - ttsJobQueue.push(context.chat[context.chat.length - 1]); + splitMessageAndAddToTtsJobQueue(context.chat[context.chat.length - 1]); } updateUiAudioPlayState(); } @@ -816,7 +841,7 @@ async function onMessageEvent(messageId, lastCharIndex) { lastChatId = context.chatId; console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`); - ttsJobQueue.push(message); + splitMessageAndAddToTtsJobQueue(message); } async function onMessageDeleted() { From 145136059ea5c293f640cc1c4b2d6cc3017e6283 Mon Sep 17 00:00:00 2001 From: Small Eggs <144642298+small-eggs@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:43:24 -0800 Subject: [PATCH 03/11] Fix tts.skip_tags's regex to match newlines The extension_settings.tts.skip_tags setting is meant to skip sending tags and their content to the TTS API provider. The original regular expression matched content inside tags with ".*?". Unfortunately, Javascript's engine does *not* match newlines on the "." without the /s flag. The /s flag was added in ES2018. To be more compatible, the regex has been changed to "[\s\S]+?". This gives similar performance (instead of using capture groups) and matches all content within a tag, as the original regex intended. --- public/scripts/extensions/tts/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 4b8978422..279737f7a 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -466,7 +466,7 @@ async function processTtsQueue() { } if (extension_settings.tts.skip_tags) { - text = text.replace(/<.*?>.*?<\/.*?>/g, '').trim(); + text = text.replace(/<.*?>[\s\S]*?<\/.*?>/g, '').trim(); } if (!extension_settings.tts.pass_asterisks) { From 63e7acb87bfdd1b10de1a2e669d371ae2ecd90f0 Mon Sep 17 00:00:00 2001 From: Dzmitry Kazlouski Date: Tue, 28 Jan 2025 05:50:25 +0300 Subject: [PATCH 04/11] Make this feature togglable in extensions > "Narrate by paragraphs (when not streaming)" --- public/scripts/extensions/tts/index.js | 27 +++++++++++++++++---- public/scripts/extensions/tts/settings.html | 4 +++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 636103f23..1d23807cb 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -120,7 +120,7 @@ async function onNarrateOneMessage() { } resetTtsPlayback(); - splitMessageAndAddToTtsJobQueue(message); + processAndQueueTtsMessage(message); moduleWorker(); } @@ -147,7 +147,7 @@ async function onNarrateText(args, text) { } resetTtsPlayback(); - splitMessageAndAddToTtsJobQueue({ mes: text, name: name }); + processAndQueueTtsMessage({ mes: text, name: name }); await moduleWorker(); // Return back to the chat voices @@ -227,7 +227,12 @@ function isTtsProcessing() { * @param {string} message.name - The name associated with the message. * @returns {void} */ -function splitMessageAndAddToTtsJobQueue(message) { +function processAndQueueTtsMessage(message) { + if (!extension_settings.tts.narrate_by_paragraphs) { + ttsJobQueue.push(message); + return; + } + const lines = message.mes.split("\n"); for (let i = 0; i < lines.length; i++) { @@ -375,7 +380,7 @@ function onAudioControlClicked() { talkingAnimation(false); } else { // Default play behavior if not processing or playing is to play the last message. - splitMessageAndAddToTtsJobQueue(context.chat[context.chat.length - 1]); + processAndQueueTtsMessage(context.chat[context.chat.length - 1]); } updateUiAudioPlayState(); } @@ -594,6 +599,7 @@ function loadSettings() { $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only); $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation); $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation); + $('#tts_narrate_by_paragraphs').prop('checked', extension_settings.tts.narrate_by_paragraphs); $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only); $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user); $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks); @@ -663,6 +669,11 @@ function onPeriodicAutoGenerationClick() { saveSettingsDebounced(); } +function onNarrateByParagraphsClick() { + extension_settings.tts.narrate_by_paragraphs = !!$('#tts_narrate_by_paragraphs').prop('checked'); + saveSettingsDebounced(); +} + function onNarrateDialoguesClick() { extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked'); @@ -841,7 +852,12 @@ async function onMessageEvent(messageId, lastCharIndex) { lastChatId = context.chatId; console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`); - splitMessageAndAddToTtsJobQueue(message); + + if (extension_settings.tts.periodic_auto_generation) { + ttsJobQueue.push(message); + } else { + processAndQueueTtsMessage(message); + } } async function onMessageDeleted() { @@ -1181,6 +1197,7 @@ jQuery(async function () { $('#tts_pass_asterisks').on('click', onPassAsterisksClick); $('#tts_auto_generation').on('click', onAutoGenerationClick); $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick); + $('#tts_narrate_by_paragraphs').on('click', onNarrateByParagraphsClick); $('#tts_narrate_user').on('click', onNarrateUserClick); $('#playback_rate').on('input', function () { diff --git a/public/scripts/extensions/tts/settings.html b/public/scripts/extensions/tts/settings.html index dc005a406..5fb5b6895 100644 --- a/public/scripts/extensions/tts/settings.html +++ b/public/scripts/extensions/tts/settings.html @@ -30,6 +30,10 @@ Narrate by paragraphs (when streaming) +