Merge pull request #3382 from SillyTavern/staging Staging into reasoning-regex
Signed| @@ -6093,6 +6093,19 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes, | |||
| 6093 | return { type, getMessage }; | 6093 | return { type, getMessage }; |
| 6094 | } | 6094 | } |
| 6095 | 6095 | ||
| 6096 | export 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 | |||
| 6096 | function saveImageToMessage(img, mes) { | 6109 | function 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 | } |
| 8526 | 8539 | ||
| 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 | } |
| 8661 | 8677 | ||
| 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); |
| @@ -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: ` |
| @@ -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 | ]; |
| 34 | 35 | ||
| 35 | const TC_COMMANDS = [ | 36 | const 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 | ]; |
| 47 | 49 | ||
| 48 | const FANCY_NAMES = { | 50 | const 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 | }; |
| 61 | 64 | ||
| 62 | /** | 65 | /** |
| @@ -138,6 +141,7 @@ const profilesProvider = () => [ | |||
| 138 | * @property {string} [context] Context Template | 141 | * @property {string} [context] Context Template |
| 139 | * @property {string} [instruct-state] Instruct Mode | 142 | * @property {string} [instruct-state] Instruct Mode |
| 140 | * @property {string} [tokenizer] Tokenizer | 143 | * @property {string} [tokenizer] Tokenizer |
| 144 | * @property {string} [stop-strings] Custom Stopping Strings | ||
| 141 | * @property {string[]} [exclude] Commands to exclude | 145 | * @property {string[]} [exclude] Commands to exclude |
| 142 | */ | 146 | */ |
| 143 | 147 | ||
| @@ -30,6 +30,7 @@ import { GoogleTranslateTtsProvider } from './google-translate.js'; | |||
| 30 | export { talkingAnimation }; | 30 | export { talkingAnimation }; |
| 31 | 31 | ||
| 32 | const UPDATE_INTERVAL = 1000; | 32 | const UPDATE_INTERVAL = 1000; |
| 33 | const wrapper = new ModuleWorkerWrapper(moduleWorker); | ||
| 33 | 34 | ||
| 34 | let voiceMapEntries = []; | 35 | let voiceMapEntries = []; |
| 35 | let voiceMap = {}; // {charName:voiceid, charName2:voiceid2} | 36 | let voiceMap = {}; // {charName:voiceid, charName2:voiceid2} |
| @@ -120,7 +121,7 @@ async function onNarrateOneMessage() { | |||
| 120 | } | 121 | } |
| 121 | 122 | ||
| 122 | resetTtsPlayback(); | 123 | resetTtsPlayback(); |
| 123 | ttsJobQueue.push(message); | 124 | processAndQueueTtsMessage(message); |
| 124 | moduleWorker(); | 125 | moduleWorker(); |
| 125 | } | 126 | } |
| 126 | 127 | ||
| @@ -147,7 +148,7 @@ async function onNarrateText(args, text) { | |||
| 147 | } | 148 | } |
| 148 | 149 | ||
| 149 | resetTtsPlayback(); | 150 | resetTtsPlayback(); |
| 150 | ttsJobQueue.push({ mes: text, name: name }); | 151 | processAndQueueTtsMessage({ mes: text, name: name }); |
| 151 | await moduleWorker(); | 152 | await moduleWorker(); |
| 152 | 153 | ||
| 153 | // Return back to the chat voices | 154 | // Return back to the chat voices |
| @@ -220,6 +221,36 @@ function isTtsProcessing() { | |||
| 220 | return processing; | 221 | return processing; |
| 221 | } | 222 | } |
| 222 | 223 | ||
| 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 | */ | ||
| 231 | function 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 | |||
| 223 | function debugTtsPlayback() { | 254 | function 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 animation | 408 | talkingAnimation(false); //stop lip animation |
| 378 | // updateUiPlayState(); | 409 | // updateUiPlayState(); |
| 410 | wrapper.update(); | ||
| 379 | } | 411 | } |
| 380 | 412 | ||
| 381 | /** | 413 | /** |
| @@ -466,7 +498,7 @@ async function processTtsQueue() { | |||
| 466 | } | 498 | } |
| 467 | 499 | ||
| 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 | } |
| 471 | 503 | ||
| 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 | } |
| 640 | 673 | ||
| 674 | function onNarrateByParagraphsClick() { | ||
| 675 | extension_settings.tts.narrate_by_paragraphs = !!$('#tts_narrate_by_paragraphs').prop('checked'); | ||
| 676 | saveSettingsDebounced(); | ||
| 677 | } | ||
| 678 | |||
| 641 | 679 | ||
| 642 | function onNarrateDialoguesClick() { | 680 | function 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; |
| 817 | 855 | ||
| 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}"`); |
| 819 | ttsJobQueue.push(message); | 857 | |
| 858 | if (extension_settings.tts.periodic_auto_generation) { | ||
| 859 | ttsJobQueue.push(message); | ||
| 860 | } else { | ||
| 861 | processAndQueueTtsMessage(message); | ||
| 862 | } | ||
| 820 | } | 863 | } |
| 821 | 864 | ||
| 822 | async function onMessageDeleted() { | 865 | async 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); |
| 1160 | 1204 | ||
| 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 loadTtsProvider | 1221 | loadSettings(); // Depends on Extension Controls and loadTtsProvider |
| 1178 | loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies | 1222 | loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies |
| 1179 | addAudioControl(); // Depends on Extension Controls | 1223 | addAudioControl(); // Depends on Extension Controls |
| 1180 | const wrapper = new ModuleWorkerWrapper(moduleWorker); | ||
| 1181 | setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things | 1224 | 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); |
| @@ -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> |
| @@ -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 | }); |
| @@ -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 ''; |
| @@ -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; |
| 2815 | 2816 | ||
| 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 | } |
| 2820 | 2824 | ||
| 2821 | await saveChatConditional(); | 2825 | await saveChatConditional(); |