Merge pull request #3382 from SillyTavern/staging Staging into reasoning-regex

9e8fd3f5a060c432692431a10a2610b983b4f24d

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
8 files changed, +122 -7Showing whitespace changes
public/script.js+19 -0
@@ -6093,6 +6093,19 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
6093 return { type, getMessage };6093 return { type, getMessage };
6094}6094}
60956095
6096export function syncCurrentSwipeInfoExtras() {
6097 if (!chat.length) {
6098 return;
6099 }
6100 const currentMessage = chat[chat.length - 1];
6101 if (currentMessage && Array.isArray(currentMessage.swipe_info) && typeof currentMessage.swipe_id === 'number') {
6102 const swipeInfo = currentMessage.swipe_info[currentMessage.swipe_id];
6103 if (swipeInfo && typeof swipeInfo === 'object') {
6104 swipeInfo.extra = structuredClone(currentMessage.extra);
6105 }
6106 }
6107}
6108
6096function saveImageToMessage(img, mes) {6109function saveImageToMessage(img, mes) {
6097 if (mes && img.image) {6110 if (mes && img.image) {
6098 if (!mes.extra || typeof mes.extra !== 'object') {6111 if (!mes.extra || typeof mes.extra !== 'object') {
@@ -8524,6 +8537,9 @@ function swipe_left() { // when we swipe left..but no generation.
8524 streamingProcessor.onStopStreaming();8537 streamingProcessor.onStopStreaming();
8525 }8538 }
85268539
8540 // Make sure ad-hoc changes to extras are saved before swiping away
8541 syncCurrentSwipeInfoExtras();
8542
8527 const swipe_duration = 120;8543 const swipe_duration = 120;
8528 const swipe_range = '700px';8544 const swipe_range = '700px';
8529 chat[chat.length - 1]['swipe_id']--;8545 chat[chat.length - 1]['swipe_id']--;
@@ -8659,6 +8675,9 @@ const swipe_right = () => {
8659 return unblockGeneration();8675 return unblockGeneration();
8660 }8676 }
86618677
8678 // Make sure ad-hoc changes to extras are saved before swiping away
8679 syncCurrentSwipeInfoExtras();
8680
8662 const swipe_duration = 200;8681 const swipe_duration = 200;
8663 const swipe_range = 700;8682 const swipe_range = 700;
8664 //console.log(swipe_range);8683 //console.log(swipe_range);
public/scripts/authors-note.js+1 -1
@@ -566,7 +566,7 @@ export function initAuthorsNote() {
566 namedArgumentList: [],566 namedArgumentList: [],
567 unnamedArgumentList: [567 unnamedArgumentList: [
568 new SlashCommandArgument(568 new SlashCommandArgument(
569 'position', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'],569 'role', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'],
570 ),570 ),
571 ],571 ],
572 helpString: `572 helpString: `
public/scripts/extensions/connection-manager/index.js+4 -0
@@ -30,6 +30,7 @@ const CC_COMMANDS = [
30 'api-url',30 'api-url',
31 'model',31 'model',
32 'proxy',32 'proxy',
33 'stop-strings',
33];34];
3435
35const TC_COMMANDS = [36const TC_COMMANDS = [
@@ -43,6 +44,7 @@ const TC_COMMANDS = [
43 'context',44 'context',
44 'instruct-state',45 'instruct-state',
45 'tokenizer',46 'tokenizer',
47 'stop-strings',
46];48];
4749
48const FANCY_NAMES = {50const FANCY_NAMES = {
@@ -57,6 +59,7 @@ const FANCY_NAMES = {
57 'instruct': 'Instruct Template',59 'instruct': 'Instruct Template',
58 'context': 'Context Template',60 'context': 'Context Template',
59 'tokenizer': 'Tokenizer',61 'tokenizer': 'Tokenizer',
62 'stop-strings': 'Custom Stopping Strings',
60};63};
6164
62/**65/**
@@ -138,6 +141,7 @@ const profilesProvider = () => [
138 * @property {string} [context] Context Template141 * @property {string} [context] Context Template
139 * @property {string} [instruct-state] Instruct Mode142 * @property {string} [instruct-state] Instruct Mode
140 * @property {string} [tokenizer] Tokenizer143 * @property {string} [tokenizer] Tokenizer
144 * @property {string} [stop-strings] Custom Stopping Strings
141 * @property {string[]} [exclude] Commands to exclude145 * @property {string[]} [exclude] Commands to exclude
142 */146 */
143147
public/scripts/extensions/tts/index.js+48 -5
@@ -30,6 +30,7 @@ import { GoogleTranslateTtsProvider } from './google-translate.js';
30export { talkingAnimation };30export { talkingAnimation };
3131
32const UPDATE_INTERVAL = 1000;32const UPDATE_INTERVAL = 1000;
33const wrapper = new ModuleWorkerWrapper(moduleWorker);
3334
34let voiceMapEntries = [];35let voiceMapEntries = [];
35let voiceMap = {}; // {charName:voiceid, charName2:voiceid2}36let voiceMap = {}; // {charName:voiceid, charName2:voiceid2}
@@ -120,7 +121,7 @@ async function onNarrateOneMessage() {
120 }121 }
121122
122 resetTtsPlayback();123 resetTtsPlayback();
123 ttsJobQueue.push(message);124 processAndQueueTtsMessage(message);
124 moduleWorker();125 moduleWorker();
125}126}
126127
@@ -147,7 +148,7 @@ async function onNarrateText(args, text) {
147 }148 }
148149
149 resetTtsPlayback();150 resetTtsPlayback();
150 ttsJobQueue.push({ mes: text, name: name });151 processAndQueueTtsMessage({ mes: text, name: name });
151 await moduleWorker();152 await moduleWorker();
152153
153 // Return back to the chat voices154 // Return back to the chat voices
@@ -220,6 +221,36 @@ function isTtsProcessing() {
220 return processing;221 return processing;
221}222}
222223
224/**
225 * Splits a message into lines and adds each non-empty line to the TTS job queue.
226 * @param {Object} message - The message object to be processed.
227 * @param {string} message.mes - The text of the message to be split into lines.
228 * @param {string} message.name - The name associated with the message.
229 * @returns {void}
230 */
231function processAndQueueTtsMessage(message) {
232 if (!extension_settings.tts.narrate_by_paragraphs) {
233 ttsJobQueue.push(message);
234 return;
235 }
236
237 const lines = message.mes.split('\n');
238
239 for (let i = 0; i < lines.length; i++) {
240 const line = lines[i];
241
242 if (line.length === 0) {
243 continue;
244 }
245
246 ttsJobQueue.push(
247 Object.assign({}, message, {
248 mes: line,
249 }),
250 );
251 }
252}
253
223function debugTtsPlayback() {254function debugTtsPlayback() {
224 console.log(JSON.stringify(255 console.log(JSON.stringify(
225 {256 {
@@ -350,7 +381,7 @@ function onAudioControlClicked() {
350 talkingAnimation(false);381 talkingAnimation(false);
351 } else {382 } else {
352 // Default play behavior if not processing or playing is to play the last message.383 // Default play behavior if not processing or playing is to play the last message.
353 ttsJobQueue.push(context.chat[context.chat.length - 1]);384 processAndQueueTtsMessage(context.chat[context.chat.length - 1]);
354 }385 }
355 updateUiAudioPlayState();386 updateUiAudioPlayState();
356}387}
@@ -376,6 +407,7 @@ function completeCurrentAudioJob() {
376 currentAudioJob = null;407 currentAudioJob = null;
377 talkingAnimation(false); //stop lip animation408 talkingAnimation(false); //stop lip animation
378 // updateUiPlayState();409 // updateUiPlayState();
410 wrapper.update();
379}411}
380412
381/**413/**
@@ -466,7 +498,7 @@ async function processTtsQueue() {
466 }498 }
467499
468 if (extension_settings.tts.skip_tags) {500 if (extension_settings.tts.skip_tags) {
469 text = text.replace(/<.*?>.*?<\/.*?>/g, '').trim();501 text = text.replace(/<.*?>[\s\S]*?<\/.*?>/g, '').trim();
470 }502 }
471503
472 if (!extension_settings.tts.pass_asterisks) {504 if (!extension_settings.tts.pass_asterisks) {
@@ -569,6 +601,7 @@ function loadSettings() {
569 $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only);601 $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only);
570 $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation);602 $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation);
571 $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation);603 $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation);
604 $('#tts_narrate_by_paragraphs').prop('checked', extension_settings.tts.narrate_by_paragraphs);
572 $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only);605 $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only);
573 $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user);606 $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user);
574 $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks);607 $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks);
@@ -638,6 +671,11 @@ function onPeriodicAutoGenerationClick() {
638 saveSettingsDebounced();671 saveSettingsDebounced();
639}672}
640673
674function onNarrateByParagraphsClick() {
675 extension_settings.tts.narrate_by_paragraphs = !!$('#tts_narrate_by_paragraphs').prop('checked');
676 saveSettingsDebounced();
677}
678
641679
642function onNarrateDialoguesClick() {680function onNarrateDialoguesClick() {
643 extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked');681 extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked');
@@ -816,7 +854,12 @@ async function onMessageEvent(messageId, lastCharIndex) {
816 lastChatId = context.chatId;854 lastChatId = context.chatId;
817855
818 console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`);856 console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`);
857
858 if (extension_settings.tts.periodic_auto_generation) {
819 ttsJobQueue.push(message);859 ttsJobQueue.push(message);
860 } else {
861 processAndQueueTtsMessage(message);
862 }
820}863}
821864
822async function onMessageDeleted() {865async function onMessageDeleted() {
@@ -1156,6 +1199,7 @@ jQuery(async function () {
1156 $('#tts_pass_asterisks').on('click', onPassAsterisksClick);1199 $('#tts_pass_asterisks').on('click', onPassAsterisksClick);
1157 $('#tts_auto_generation').on('click', onAutoGenerationClick);1200 $('#tts_auto_generation').on('click', onAutoGenerationClick);
1158 $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick);1201 $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick);
1202 $('#tts_narrate_by_paragraphs').on('click', onNarrateByParagraphsClick);
1159 $('#tts_narrate_user').on('click', onNarrateUserClick);1203 $('#tts_narrate_user').on('click', onNarrateUserClick);
11601204
1161 $('#playback_rate').on('input', function () {1205 $('#playback_rate').on('input', function () {
@@ -1177,7 +1221,6 @@ jQuery(async function () {
1177 loadSettings(); // Depends on Extension Controls and loadTtsProvider1221 loadSettings(); // Depends on Extension Controls and loadTtsProvider
1178 loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies1222 loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies
1179 addAudioControl(); // Depends on Extension Controls1223 addAudioControl(); // Depends on Extension Controls
1180 const wrapper = new ModuleWorkerWrapper(moduleWorker);
1181 setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things1224 setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things
1182 eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);1225 eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);
1183 eventSource.on(event_types.CHAT_CHANGED, onChatChanged);1226 eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
public/scripts/extensions/tts/settings.html+4 -0
@@ -30,6 +30,10 @@
30 <input type="checkbox" id="tts_periodic_auto_generation">30 <input type="checkbox" id="tts_periodic_auto_generation">
31 <small data-i18n="Narrate by paragraphs (when streaming)">Narrate by paragraphs (when streaming)</small>31 <small data-i18n="Narrate by paragraphs (when streaming)">Narrate by paragraphs (when streaming)</small>
32 </label>32 </label>
33 <label class="checkbox_label" for="tts_narrate_by_paragraphs">
34 <input type="checkbox" id="tts_narrate_by_paragraphs">
35 <small data-i18n="Narrate by paragraphs (when not streaming)">Narrate by paragraphs (when not streaming)</small>
36 </label>
33 <label class="checkbox_label" for="tts_narrate_quoted">37 <label class="checkbox_label" for="tts_narrate_quoted">
34 <input type="checkbox" id="tts_narrate_quoted">38 <input type="checkbox" id="tts_narrate_quoted">
35 <small data-i18n="Only narrate quotes">Only narrate "quotes"</small>39 <small data-i18n="Only narrate quotes">Only narrate "quotes"</small>
public/scripts/power-user.js+41 -0
@@ -4072,4 +4072,45 @@ $(document).ready(() => {
4072 ],4072 ],
4073 helpString: 'activates a movingUI preset by name',4073 helpString: 'activates a movingUI preset by name',
4074 }));4074 }));
4075 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
4076 name: 'stop-strings',
4077 aliases: ['stopping-strings', 'custom-stopping-strings', 'custom-stop-strings'],
4078 helpString: `
4079 <div>
4080 Sets a list of custom stopping strings. Gets the list if no value is provided.
4081 </div>
4082 <div>
4083 <strong>Examples:</strong>
4084 </div>
4085 <ul>
4086 <li>Value must be a JSON-serialized array: <pre><code class="language-stscript">/stop-strings ["goodbye", "farewell"]</code></pre></li>
4087 <li>Pipe characters must be escaped with a backslash: <pre><code class="language-stscript">/stop-strings ["left\\|right"]</code></pre></li>
4088 </ul>
4089 `,
4090 returns: ARGUMENT_TYPE.LIST,
4091 unnamedArgumentList: [
4092 SlashCommandArgument.fromProps({
4093 description: 'list of strings',
4094 typeList: [ARGUMENT_TYPE.LIST],
4095 acceptsMultiple: false,
4096 isRequired: false,
4097 }),
4098 ],
4099 callback: (_, value) => {
4100 if (String(value ?? '').trim()) {
4101 const parsedValue = ((x) => { try { return JSON.parse(x.toString()); } catch { return null; } })(value);
4102 if (!parsedValue || !Array.isArray(parsedValue)) {
4103 throw new Error('Invalid list format. The value must be a JSON-serialized array of strings.');
4104 }
4105 parsedValue.forEach((item, index) => {
4106 parsedValue[index] = String(item);
4107 });
4108 power_user.custom_stopping_strings = JSON.stringify(parsedValue);
4109 $('#custom_stopping_strings').val(power_user.custom_stopping_strings);
4110 saveSettingsDebounced();
4111 }
4112
4113 return power_user.custom_stopping_strings;
4114 },
4115 }));
4075});4116});
public/scripts/reasoning.js+1 -1
@@ -147,7 +147,7 @@ function registerReasoningSlashCommands() {
147 }),147 }),
148 ],148 ],
149 callback: async (args, value) => {149 callback: async (args, value) => {
150 const messageId = !isNaN(Number(args[0])) ? Number(args[0]) : chat.length - 1;150 const messageId = !isNaN(Number(args.at)) ? Number(args.at) : chat.length - 1;
151 const message = chat[messageId];151 const message = chat[messageId];
152 if (!message?.extra) {152 if (!message?.extra) {
153 return '';153 return '';
public/scripts/slash-commands.js+4 -0
@@ -42,6 +42,7 @@ import {
42 showMoreMessages,42 showMoreMessages,
43 stopGeneration,43 stopGeneration,
44 substituteParams,44 substituteParams,
45 syncCurrentSwipeInfoExtras,
45 system_avatar,46 system_avatar,
46 system_message_types,47 system_message_types,
47 this_chid,48 this_chid,
@@ -2814,8 +2815,11 @@ async function addSwipeCallback(args, value) {
2814 const newSwipeId = lastMessage.swipes.length - 1;2815 const newSwipeId = lastMessage.swipes.length - 1;
28152816
2816 if (isTrueBoolean(args.switch)) {2817 if (isTrueBoolean(args.switch)) {
2818 // Make sure ad-hoc changes to extras are saved before swiping away
2819 syncCurrentSwipeInfoExtras();
2817 lastMessage.swipe_id = newSwipeId;2820 lastMessage.swipe_id = newSwipeId;
2818 lastMessage.mes = lastMessage.swipes[newSwipeId];2821 lastMessage.mes = lastMessage.swipes[newSwipeId];
2822 lastMessage.extra = structuredClone(lastMessage.swipe_info?.[newSwipeId]?.extra ?? lastMessage.extra ?? {});
2819 }2823 }
28202824
2821 await saveChatConditional();2825 await saveChatConditional();