Merge branch 'staging' into translation-improvements
| @@ -15,6 +15,11 @@ | ||
| 15 | 15 | "**/node_modules/*", |
| 16 | 16 | "public/lib", |
| 17 | 17 | "backups/*", |
| 18 | 18 | "data/*", |
| 19 | + "**/dist/*", | |
| 20 | + "dist/*", | |
| 21 | + "cache/*", | |
| 22 | + "src/tokenizers/*", | |
| 23 | + "docker/*", | |
| 19 | 24 | ] |
| 20 | 25 | } |
| @@ -159,7 +159,7 @@ import { | ||
| 159 | 159 | import { debounce_timeout } from './scripts/constants.js'; |
| 160 | 160 | |
| 161 | 161 | import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, renderExtensionTemplateAsync, runGenerationInterceptors, saveMetadataDebounced, writeExtensionField } from './scripts/extensions.js'; |
| 162 | 162 | import { COMMENT_NAME_DEFAULT, executeSlashCommands, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, registerSlashCommand, stopScriptExecution } from './scripts/slash-commands.js'; |
| 163 | 163 | import { |
| 164 | 164 | tag_map, |
| 165 | 165 | tags, |
| @@ -911,6 +911,7 @@ async function firstLoadInit() { | ||
| 911 | 911 | initKeyboard(); |
| 912 | 912 | initDynamicStyles(); |
| 913 | 913 | initTags(); |
| 914 | + initDefaultSlashCommands(); | |
| 914 | 915 | await getUserAvatars(true, user_avatar); |
| 915 | 916 | await getCharacters(); |
| 916 | 917 | await getBackgrounds(); |
| @@ -356,6 +356,7 @@ jQuery(async function () { | ||
| 356 | 356 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'llamacpp' && textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]) || |
| 357 | 357 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ooba' && textgenerationwebui_settings.server_urls[textgen_types.OOBA]) || |
| 358 | 358 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'koboldcpp' && textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP]) || |
| 359 | + (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'vllm' && textgenerationwebui_settings.server_urls[textgen_types.VLLM]) || | |
| 359 | 360 | (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'custom') || |
| 360 | 361 | extension_settings.caption.source === 'local' || |
| 361 | 362 | extension_settings.caption.source === 'horde'; |
| @@ -26,6 +26,7 @@ | ||
| 26 | 26 | <option value="openai">OpenAI</option> |
| 27 | 27 | <option value="openrouter">OpenRouter</option> |
| 28 | 28 | <option value="ooba" data-i18n="Text Generation WebUI (oobabooga)">Text Generation WebUI (oobabooga)</option> |
| 29 | + <option value="vllm">vLLM</option> | |
| 29 | 30 | </select> |
| 30 | 31 | </div> |
| 31 | 32 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> |
| @@ -66,6 +67,7 @@ | ||
| 66 | 67 | <option data-type="llamacpp" value="llamacpp_current" data-i18n="currently_loaded">[Currently loaded]</option> |
| 67 | 68 | <option data-type="ooba" value="ooba_current" data-i18n="currently_loaded">[Currently loaded]</option> |
| 68 | 69 | <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option> |
| 70 | + <option data-type="vllm" value="vllm_current" data-i18n="currently_selected">[Currently selected]</option> | |
| 69 | 71 | <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option> |
| 70 | 72 | </select> |
| 71 | 73 | </div> |
| @@ -34,6 +34,7 @@ export async function getMultimodalCaption(base64Img, prompt) { | ||
| 34 | 34 | const isCustom = extension_settings.caption.multimodal_api === 'custom'; |
| 35 | 35 | const isOoba = extension_settings.caption.multimodal_api === 'ooba'; |
| 36 | 36 | const isKoboldCpp = extension_settings.caption.multimodal_api === 'koboldcpp'; |
| 37 | + const isVllm = extension_settings.caption.multimodal_api === 'vllm'; | |
| 37 | 38 | const base64Bytes = base64Img.length * 0.75; |
| 38 | 39 | const compressionLimit = 2 * 1024 * 1024; |
| 39 | 40 | if ((['google', 'openrouter'].includes(extension_settings.caption.multimodal_api) && base64Bytes > compressionLimit) || isOoba || isKoboldCpp) { |
| @@ -65,6 +66,14 @@ export async function getMultimodalCaption(base64Img, prompt) { | ||
| 65 | 66 | requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]; |
| 66 | 67 | } |
| 67 | 68 | |
| 69 | + if (isVllm) { | |
| 70 | + if (extension_settings.caption.multimodal_model === 'vllm_current') { | |
| 71 | + requestBody.model = textgenerationwebui_settings.vllm_model; | |
| 72 | + } | |
| 73 | + | |
| 74 | + requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.VLLM]; | |
| 75 | + } | |
| 76 | + | |
| 68 | 77 | if (isLlamaCpp) { |
| 69 | 78 | requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]; |
| 70 | 79 | } |
| @@ -151,6 +160,14 @@ function throwIfInvalidModel(useReverseProxy) { | ||
| 151 | 160 | throw new Error('KoboldCpp server URL is not set.'); |
| 152 | 161 | } |
| 153 | 162 | |
| 163 | + if (extension_settings.caption.multimodal_api === 'vllm' && !textgenerationwebui_settings.server_urls[textgen_types.VLLM]) { | |
| 164 | + throw new Error('vLLM server URL is not set.'); | |
| 165 | + } | |
| 166 | + | |
| 167 | + if (extension_settings.caption.multimodal_api === 'vllm' && extension_settings.caption.multimodal_model === 'vllm_current' && !textgenerationwebui_settings.vllm_model) { | |
| 168 | + throw new Error('vLLM model is not set.'); | |
| 169 | + } | |
| 170 | + | |
| 154 | 171 | if (extension_settings.caption.multimodal_api === 'custom' && !oai_settings.custom_url) { |
| 155 | 172 | throw new Error('Custom API URL is not set.'); |
| 156 | 173 | } |
| @@ -10,6 +10,7 @@ import { NovelTtsProvider } from './novel.js'; | ||
| 10 | 10 | import { power_user } from '../../power-user.js'; |
| 11 | 11 | import { OpenAITtsProvider } from './openai.js'; |
| 12 | 12 | import { XTTSTtsProvider } from './xtts.js'; |
| 13 | +import { VITSTtsProvider } from './vits.js'; | |
| 13 | 14 | import { GSVITtsProvider } from './gsvi.js'; |
| 14 | 15 | import { SBVits2TtsProvider } from './sbvits2.js'; |
| 15 | 16 | import { AllTalkTtsProvider } from './alltalk.js'; |
| @@ -83,6 +84,7 @@ const ttsProviders = { | ||
| 83 | 84 | ElevenLabs: ElevenLabsTtsProvider, |
| 84 | 85 | Silero: SileroTtsProvider, |
| 85 | 86 | XTTSv2: XTTSTtsProvider, |
| 87 | + VITS: VITSTtsProvider, | |
| 86 | 88 | GSVI: GSVITtsProvider, |
| 87 | 89 | SBVits2: SBVits2TtsProvider, |
| 88 | 90 | System: SystemTtsProvider, |
| @@ -0,0 +1,404 @@ | ||
| 1 | +import { getPreviewString, saveTtsProviderSettings } from './index.js'; | |
| 2 | + | |
| 3 | +export { VITSTtsProvider }; | |
| 4 | + | |
| 5 | +class VITSTtsProvider { | |
| 6 | + //########// | |
| 7 | + // Config // | |
| 8 | + //########// | |
| 9 | + | |
| 10 | + settings; | |
| 11 | + ready = false; | |
| 12 | + voices = []; | |
| 13 | + separator = '. '; | |
| 14 | + audioElement = document.createElement('audio'); | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * Perform any text processing before passing to TTS engine. | |
| 18 | + * @param {string} text Input text | |
| 19 | + * @returns {string} Processed text | |
| 20 | + */ | |
| 21 | + processText(text) { | |
| 22 | + return text; | |
| 23 | + } | |
| 24 | + | |
| 25 | + audioFormats = ['wav', 'ogg', 'silk', 'mp3', 'flac']; | |
| 26 | + | |
| 27 | + languageLabels = { | |
| 28 | + 'Auto': 'auto', | |
| 29 | + 'Chinese': 'zh', | |
| 30 | + 'English': 'en', | |
| 31 | + 'Japanese': 'ja', | |
| 32 | + 'Korean': 'ko', | |
| 33 | + }; | |
| 34 | + | |
| 35 | + langKey2LangCode = { | |
| 36 | + 'zh': 'zh-CN', | |
| 37 | + 'en': 'en-US', | |
| 38 | + 'ja': 'ja-JP', | |
| 39 | + 'ko': 'ko-KR', | |
| 40 | + }; | |
| 41 | + | |
| 42 | + modelTypes = { | |
| 43 | + VITS: 'VITS', | |
| 44 | + W2V2_VITS: 'W2V2-VITS', | |
| 45 | + BERT_VITS2: 'BERT-VITS2', | |
| 46 | + }; | |
| 47 | + | |
| 48 | + defaultSettings = { | |
| 49 | + provider_endpoint: 'http://localhost:23456', | |
| 50 | + format: 'wav', | |
| 51 | + lang: 'auto', | |
| 52 | + length: 1.0, | |
| 53 | + noise: 0.33, | |
| 54 | + noisew: 0.4, | |
| 55 | + segment_size: 50, | |
| 56 | + streaming: false, | |
| 57 | + dim_emotion: 0, | |
| 58 | + sdp_ratio: 0.2, | |
| 59 | + emotion: 0, | |
| 60 | + text_prompt: '', | |
| 61 | + style_text: '', | |
| 62 | + style_weight: 1, | |
| 63 | + }; | |
| 64 | + | |
| 65 | + get settingsHtml() { | |
| 66 | + let html = ` | |
| 67 | + <label for="vits_lang">Text Language</label> | |
| 68 | + <select id="vits_lang">`; | |
| 69 | + | |
| 70 | + for (let language in this.languageLabels) { | |
| 71 | + if (this.languageLabels[language] == this.settings?.lang) { | |
| 72 | + html += `<option value="${this.languageLabels[language]}" selected="selected">${language}</option>`; | |
| 73 | + continue; | |
| 74 | + } | |
| 75 | + html += `<option value="${this.languageLabels[language]}">${language}</option>`; | |
| 76 | + } | |
| 77 | + | |
| 78 | + html += ` | |
| 79 | + </select> | |
| 80 | + <label>VITS / W2V2-VITS / Bert-VITS2 Settings:</label><br/> | |
| 81 | + <label for="vits_endpoint">Provider Endpoint:</label> | |
| 82 | + <input id="vits_endpoint" type="text" class="text_pole" maxlength="250" value="${this.defaultSettings.provider_endpoint}"/> | |
| 83 | + <span>Use <a target="_blank" href="https://github.com/Artrajz/vits-simple-api">vits-simple-api</a>.</span><br/> | |
| 84 | + | |
| 85 | + <label for="vits_format">Audio format:</label> | |
| 86 | + <select id="vits_format">`; | |
| 87 | + | |
| 88 | + for (let format of this.audioFormats) { | |
| 89 | + if (format == this.settings?.format) { | |
| 90 | + html += `<option value="${format}" selected="selected">${format}</option>`; | |
| 91 | + continue; | |
| 92 | + } | |
| 93 | + html += `<option value="${format}">${format}</option>`; | |
| 94 | + } | |
| 95 | + | |
| 96 | + html += ` | |
| 97 | + </select> | |
| 98 | + <label for="vits_length">Audio length: <span id="vits_length_output">${this.defaultSettings.length}</span></label> | |
| 99 | + <input id="vits_length" type="range" value="${this.defaultSettings.length}" min="0.0" max="5" step="0.01" /> | |
| 100 | + | |
| 101 | + <label for="vits_noise">Noise: <span id="vits_noise_output">${this.defaultSettings.noise}</span></label> | |
| 102 | + <input id="vits_noise" type="range" value="${this.defaultSettings.noise}" min="0.1" max="2" step="0.01" /> | |
| 103 | + | |
| 104 | + <label for="vits_noisew">SDP noise: <span id="vits_noisew_output">${this.defaultSettings.noisew}</span></label> | |
| 105 | + <input id="vits_noisew" type="range" value="${this.defaultSettings.noisew}" min="0.1" max="2" step="0.01" /> | |
| 106 | + | |
| 107 | + <label for="vits_segment_size">Segment Size: <span id="vits_segment_size_output">${this.defaultSettings.segment_size}</span></label> | |
| 108 | + <input id="vits_segment_size" type="range" value="${this.defaultSettings.segment_size}" min="0" max="1000" step="1" /> | |
| 109 | + | |
| 110 | + <label for="vits_streaming" class="checkbox_label"> | |
| 111 | + <input id="vits_streaming" type="checkbox" /> | |
| 112 | + <span>Streaming</span> | |
| 113 | + </label> | |
| 114 | + | |
| 115 | + <label>W2V2-VITS Settings:</label><br/> | |
| 116 | + <label for="vits_dim_emotion">Dimensional emotion:</label> | |
| 117 | + <input id="vits_dim_emotion" type="number" class="text_pole" min="0" max="5457" step="1" value="${this.defaultSettings.dim_emotion}"/> | |
| 118 | + | |
| 119 | + <label>BERT-VITS2 Settings:</label><br/> | |
| 120 | + <label for="vits_sdp_ratio">sdp_ratio: <span id="vits_sdp_ratio_output">${this.defaultSettings.sdp_ratio}</span></label> | |
| 121 | + <input id="vits_sdp_ratio" type="range" value="${this.defaultSettings.sdp_ratio}" min="0.0" max="1" step="0.01" /> | |
| 122 | + | |
| 123 | + <label for="vits_emotion">emotion: <span id="vits_emotion_output">${this.defaultSettings.emotion}</span></label> | |
| 124 | + <input id="vits_emotion" type="range" value="${this.defaultSettings.emotion}" min="0" max="9" step="1" /> | |
| 125 | + | |
| 126 | + <label for="vits_text_prompt">Text Prompt:</label> | |
| 127 | + <input id="vits_text_prompt" type="text" class="text_pole" maxlength="512" value="${this.defaultSettings.text_prompt}"/> | |
| 128 | + | |
| 129 | + <label for="vits_style_text">Style text:</label> | |
| 130 | + <input id="vits_style_text" type="text" class="text_pole" maxlength="512" value="${this.defaultSettings.style_text}"/> | |
| 131 | + | |
| 132 | + <label for="vits_style_weight">Style weight <span id="vits_style_weight_output">${this.defaultSettings.style_weight}</span></label> | |
| 133 | + <input id="vits_style_weight" type="range" value="${this.defaultSettings.style_weight}" min="0" max="1" step="0.01" /> | |
| 134 | + `; | |
| 135 | + | |
| 136 | + return html; | |
| 137 | + } | |
| 138 | + | |
| 139 | + onSettingsChange() { | |
| 140 | + // Used when provider settings are updated from UI | |
| 141 | + this.settings.provider_endpoint = $('#vits_endpoint').val(); | |
| 142 | + this.settings.lang = $('#vits_lang').val(); | |
| 143 | + this.settings.format = $('#vits_format').val(); | |
| 144 | + this.settings.dim_emotion = $('#vits_dim_emotion').val(); | |
| 145 | + this.settings.text_prompt = $('#vits_text_prompt').val(); | |
| 146 | + this.settings.style_text = $('#vits_style_text').val(); | |
| 147 | + | |
| 148 | + // Update the default TTS settings based on input fields | |
| 149 | + this.settings.length = $('#vits_length').val(); | |
| 150 | + this.settings.noise = $('#vits_noise').val(); | |
| 151 | + this.settings.noisew = $('#vits_noisew').val(); | |
| 152 | + this.settings.segment_size = $('#vits_segment_size').val(); | |
| 153 | + this.settings.streaming = $('#vits_streaming').is(':checked'); | |
| 154 | + this.settings.sdp_ratio = $('#vits_sdp_ratio').val(); | |
| 155 | + this.settings.emotion = $('#vits_emotion').val(); | |
| 156 | + this.settings.style_weight = $('#vits_style_weight').val(); | |
| 157 | + | |
| 158 | + // Update the UI to reflect changes | |
| 159 | + $('#vits_length_output').text(this.settings.length); | |
| 160 | + $('#vits_noise_output').text(this.settings.noise); | |
| 161 | + $('#vits_noisew_output').text(this.settings.noisew); | |
| 162 | + $('#vits_segment_size_output').text(this.settings.segment_size); | |
| 163 | + $('#vits_sdp_ratio_output').text(this.settings.sdp_ratio); | |
| 164 | + $('#vits_emotion_output').text(this.settings.emotion); | |
| 165 | + $('#vits_style_weight_output').text(this.settings.style_weight); | |
| 166 | + | |
| 167 | + saveTtsProviderSettings(); | |
| 168 | + this.changeTTSSettings(); | |
| 169 | + } | |
| 170 | + | |
| 171 | + async loadSettings(settings) { | |
| 172 | + // Pupulate Provider UI given input settings | |
| 173 | + if (Object.keys(settings).length == 0) { | |
| 174 | + console.info('Using default TTS Provider settings'); | |
| 175 | + } | |
| 176 | + | |
| 177 | + // Only accept keys defined in defaultSettings | |
| 178 | + this.settings = this.defaultSettings; | |
| 179 | + | |
| 180 | + for (const key in settings) { | |
| 181 | + if (key in this.settings) { | |
| 182 | + this.settings[key] = settings[key]; | |
| 183 | + } else { | |
| 184 | + console.debug(`Ignoring non-user-configurable setting: ${key}`); | |
| 185 | + } | |
| 186 | + } | |
| 187 | + | |
| 188 | + // Set initial values from the settings | |
| 189 | + $('#vits_endpoint').val(this.settings.provider_endpoint); | |
| 190 | + $('#vits_lang').val(this.settings.lang); | |
| 191 | + $('#vits_format').val(this.settings.format); | |
| 192 | + $('#vits_length').val(this.settings.length); | |
| 193 | + $('#vits_noise').val(this.settings.noise); | |
| 194 | + $('#vits_noisew').val(this.settings.noisew); | |
| 195 | + $('#vits_segment_size').val(this.settings.segment_size); | |
| 196 | + $('#vits_streaming').prop('checked', this.settings.streaming); | |
| 197 | + $('#vits_dim_emotion').val(this.settings.dim_emotion); | |
| 198 | + $('#vits_sdp_ratio').val(this.settings.sdp_ratio); | |
| 199 | + $('#vits_emotion').val(this.settings.emotion); | |
| 200 | + $('#vits_text_prompt').val(this.settings.text_prompt); | |
| 201 | + $('#vits_style_text').val(this.settings.style_text); | |
| 202 | + $('#vits_style_weight').val(this.settings.style_weight); | |
| 203 | + | |
| 204 | + // Update the UI to reflect changes | |
| 205 | + $('#vits_length_output').text(this.settings.length); | |
| 206 | + $('#vits_noise_output').text(this.settings.noise); | |
| 207 | + $('#vits_noisew_output').text(this.settings.noisew); | |
| 208 | + $('#vits_segment_size_output').text(this.settings.segment_size); | |
| 209 | + $('#vits_sdp_ratio_output').text(this.settings.sdp_ratio); | |
| 210 | + $('#vits_emotion_output').text(this.settings.emotion); | |
| 211 | + $('#vits_style_weight_output').text(this.settings.style_weight); | |
| 212 | + | |
| 213 | + // Register input/change event listeners to update settings on user interaction | |
| 214 | + $('#vits_endpoint').on('input', () => { this.onSettingsChange(); }); | |
| 215 | + $('#vits_lang').on('change', () => { this.onSettingsChange(); }); | |
| 216 | + $('#vits_format').on('change', () => { this.onSettingsChange(); }); | |
| 217 | + $('#vits_length').on('change', () => { this.onSettingsChange(); }); | |
| 218 | + $('#vits_noise').on('change', () => { this.onSettingsChange(); }); | |
| 219 | + $('#vits_noisew').on('change', () => { this.onSettingsChange(); }); | |
| 220 | + $('#vits_segment_size').on('change', () => { this.onSettingsChange(); }); | |
| 221 | + $('#vits_streaming').on('change', () => { this.onSettingsChange(); }); | |
| 222 | + $('#vits_dim_emotion').on('change', () => { this.onSettingsChange(); }); | |
| 223 | + $('#vits_sdp_ratio').on('change', () => { this.onSettingsChange(); }); | |
| 224 | + $('#vits_emotion').on('change', () => { this.onSettingsChange(); }); | |
| 225 | + $('#vits_text_prompt').on('change', () => { this.onSettingsChange(); }); | |
| 226 | + $('#vits_style_text').on('change', () => { this.onSettingsChange(); }); | |
| 227 | + $('#vits_style_weight').on('change', () => { this.onSettingsChange(); }); | |
| 228 | + | |
| 229 | + await this.checkReady(); | |
| 230 | + | |
| 231 | + console.info('VITS: Settings loaded'); | |
| 232 | + } | |
| 233 | + | |
| 234 | + // Perform a simple readiness check by trying to fetch voiceIds | |
| 235 | + async checkReady() { | |
| 236 | + await Promise.allSettled([this.fetchTtsVoiceObjects(), this.changeTTSSettings()]); | |
| 237 | + } | |
| 238 | + | |
| 239 | + async onRefreshClick() { | |
| 240 | + return; | |
| 241 | + } | |
| 242 | + | |
| 243 | + //#################// | |
| 244 | + // TTS Interfaces // | |
| 245 | + //#################// | |
| 246 | + | |
| 247 | + async getVoice(voiceName) { | |
| 248 | + if (this.voices.length == 0) { | |
| 249 | + this.voices = await this.fetchTtsVoiceObjects(); | |
| 250 | + } | |
| 251 | + const match = this.voices.filter( | |
| 252 | + v => v.name == voiceName, | |
| 253 | + )[0]; | |
| 254 | + if (!match) { | |
| 255 | + throw `TTS Voice name ${voiceName} not found`; | |
| 256 | + } | |
| 257 | + return match; | |
| 258 | + } | |
| 259 | + | |
| 260 | + async getVoiceById(voiceId) { | |
| 261 | + if (this.voices.length == 0) { | |
| 262 | + this.voices = await this.fetchTtsVoiceObjects(); | |
| 263 | + } | |
| 264 | + const match = this.voices.filter( | |
| 265 | + v => v.voice_id == voiceId, | |
| 266 | + )[0]; | |
| 267 | + if (!match) { | |
| 268 | + throw `TTS Voice id ${voiceId} not found`; | |
| 269 | + } | |
| 270 | + return match; | |
| 271 | + } | |
| 272 | + | |
| 273 | + async generateTts(text, voiceId) { | |
| 274 | + const response = await this.fetchTtsGeneration(text, voiceId); | |
| 275 | + return response; | |
| 276 | + } | |
| 277 | + | |
| 278 | + //###########// | |
| 279 | + // API CALLS // | |
| 280 | + //###########// | |
| 281 | + async fetchTtsVoiceObjects() { | |
| 282 | + const response = await fetch(`${this.settings.provider_endpoint}/voice/speakers`); | |
| 283 | + if (!response.ok) { | |
| 284 | + throw new Error(`HTTP ${response.status}: ${await response.json()}`); | |
| 285 | + } | |
| 286 | + const jsonData = await response.json(); | |
| 287 | + const voices = []; | |
| 288 | + | |
| 289 | + const addVoices = (modelType) => { | |
| 290 | + jsonData[modelType].forEach(voice => { | |
| 291 | + voices.push({ | |
| 292 | + name: `[${modelType}] ${voice.name} (${voice.lang})`, | |
| 293 | + voice_id: `${modelType}&${voice.id}`, | |
| 294 | + preview_url: false, | |
| 295 | + lang: voice.lang, | |
| 296 | + }); | |
| 297 | + }); | |
| 298 | + }; | |
| 299 | + for (const key in this.modelTypes) { | |
| 300 | + addVoices(this.modelTypes[key]); | |
| 301 | + } | |
| 302 | + | |
| 303 | + this.voices = voices; // Assign to the class property | |
| 304 | + return voices; // Also return this list | |
| 305 | + } | |
| 306 | + | |
| 307 | + // Each time a parameter is changed, we change the configuration | |
| 308 | + async changeTTSSettings() { | |
| 309 | + } | |
| 310 | + | |
| 311 | + /** | |
| 312 | + * Fetch TTS generation from the API. | |
| 313 | + * @param {string} inputText Text to generate TTS for | |
| 314 | + * @param {string} voiceId Voice ID to use (model_type&speaker_id)) | |
| 315 | + * @returns {Promise<Response|string>} Fetch response | |
| 316 | + */ | |
| 317 | + async fetchTtsGeneration(inputText, voiceId, lang = null, forceNoStreaming = false) { | |
| 318 | + console.info(`Generating new TTS for voice_id ${voiceId}`); | |
| 319 | + | |
| 320 | + const streaming = !forceNoStreaming && this.settings.streaming; | |
| 321 | + const [model_type, speaker_id] = voiceId.split('&'); | |
| 322 | + const params = new URLSearchParams(); | |
| 323 | + params.append('text', inputText); | |
| 324 | + params.append('id', speaker_id); | |
| 325 | + if (streaming) { | |
| 326 | + params.append('streaming', streaming); | |
| 327 | + // Streaming response only supports MP3 | |
| 328 | + } | |
| 329 | + else { | |
| 330 | + params.append('format', this.settings.format); | |
| 331 | + } | |
| 332 | + params.append('lang', lang ?? this.settings.lang); | |
| 333 | + params.append('length', this.settings.length); | |
| 334 | + params.append('noise', this.settings.noise); | |
| 335 | + params.append('noisew', this.settings.noisew); | |
| 336 | + params.append('segment_size', this.settings.segment_size); | |
| 337 | + | |
| 338 | + if (model_type == this.modelTypes.W2V2_VITS) { | |
| 339 | + params.append('emotion', this.settings.dim_emotion); | |
| 340 | + } | |
| 341 | + else if (model_type == this.modelTypes.BERT_VITS2) { | |
| 342 | + params.append('sdp_ratio', this.settings.sdp_ratio); | |
| 343 | + params.append('emotion', this.settings.emotion); | |
| 344 | + if (this.settings.text_prompt) { | |
| 345 | + params.append('text_prompt', this.settings.text_prompt); | |
| 346 | + } | |
| 347 | + if (this.settings.style_text) { | |
| 348 | + params.append('style_text', this.settings.style_text); | |
| 349 | + params.append('style_weight', this.settings.style_weight); | |
| 350 | + } | |
| 351 | + } | |
| 352 | + | |
| 353 | + const url = `${this.settings.provider_endpoint}/voice/${model_type.toLowerCase()}`; | |
| 354 | + | |
| 355 | + if (streaming) { | |
| 356 | + return url + `?${params.toString()}`; | |
| 357 | + } | |
| 358 | + | |
| 359 | + const response = await fetch( | |
| 360 | + url, | |
| 361 | + { | |
| 362 | + method: 'POST', | |
| 363 | + headers: { | |
| 364 | + 'Content-Type': 'application/x-www-form-urlencoded', | |
| 365 | + }, | |
| 366 | + body: params, | |
| 367 | + }, | |
| 368 | + ); | |
| 369 | + if (!response.ok) { | |
| 370 | + toastr.error(response.statusText, 'TTS Generation Failed'); | |
| 371 | + throw new Error(`HTTP ${response.status}: ${await response.text()}`); | |
| 372 | + } | |
| 373 | + return response; | |
| 374 | + } | |
| 375 | + | |
| 376 | + /** | |
| 377 | + * Preview TTS for a given voice ID. | |
| 378 | + * @param {string} id Voice ID | |
| 379 | + */ | |
| 380 | + async previewTtsVoice(id) { | |
| 381 | + this.audioElement.pause(); | |
| 382 | + this.audioElement.currentTime = 0; | |
| 383 | + const voice = await this.getVoiceById(id); | |
| 384 | + const lang = voice.lang.includes(this.settings.lang) ? this.settings.lang : voice.lang[0]; | |
| 385 | + | |
| 386 | + let lang_code = this.langKey2LangCode[lang]; | |
| 387 | + const text = getPreviewString(lang_code); | |
| 388 | + const response = await this.fetchTtsGeneration(text, id, lang, true); | |
| 389 | + if (typeof response != 'string') { | |
| 390 | + if (!response.ok) { | |
| 391 | + throw new Error(`HTTP ${response.status}: ${await response.text()}`); | |
| 392 | + } | |
| 393 | + const audio = await response.blob(); | |
| 394 | + const url = URL.createObjectURL(audio); | |
| 395 | + this.audioElement.src = url; | |
| 396 | + this.audioElement.play(); | |
| 397 | + } | |
| 398 | + } | |
| 399 | + | |
| 400 | + // Interface not used | |
| 401 | + async fetchTtsFromHistory(history_item_id) { | |
| 402 | + return Promise.resolve(history_item_id); | |
| 403 | + } | |
| 404 | +} | |
| @@ -38,13 +38,13 @@ import { | ||
| 38 | 38 | system_message_types, |
| 39 | 39 | this_chid, |
| 40 | 40 | } from '../script.js'; |
| 41 | 41 | import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 42 | 42 | import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js'; |
| 43 | 43 | import { getMessageTimeStamp } from './RossAscends-mods.js'; |
| 44 | 44 | import { hideChatMessageRange } from './chats.js'; |
| 45 | 45 | import { extension_settings, getContext, saveMetadataDebounced } from './extensions.js'; |
| 46 | 46 | import { getRegexedString, regex_placement } from './extensions/regex/engine.js'; |
| 47 | 47 | import { findGroupMemberId, getGroupMembers, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js'; |
| 48 | 48 | import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js'; |
| 49 | 49 | import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockState, togglePersonaLock, user_avatar } from './personas.js'; |
| 50 | 50 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| @@ -53,7 +53,6 @@ import { decodeTextTokens, getFriendlyTokenizerName, getTextTokens, getTokenCoun | ||
| 53 | 53 | import { debounce, delay, isFalseBoolean, isTrueBoolean, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| 54 | 54 | import { registerVariableCommands, resolveVariable } from './variables.js'; |
| 55 | 55 | import { background_settings } from './backgrounds.js'; |
| 56 | -import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; | |
| 57 | 56 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| 58 | 57 | import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureResult.js'; |
| 59 | 58 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; |
| @@ -75,78 +74,79 @@ export const parser = new SlashCommandParser(); | ||
| 75 | 74 | const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser); |
| 76 | 75 | const getSlashCommandsHelp = parser.getHelpString.bind(parser); |
| 77 | 76 | |
| 78 | -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 77 | +export function initDefaultSlashCommands() { | |
| 79 | - name: '?', | |
| 78 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 80 | - callback: helpCommandCallback, | |
| 79 | + name: '?', | |
| 81 | - aliases: ['help'], | |
| 80 | + callback: helpCommandCallback, | |
| 82 | - unnamedArgumentList: [SlashCommandArgument.fromProps({ | |
| 81 | + aliases: ['help'], | |
| 83 | - description: 'help topic', | |
| 82 | + unnamedArgumentList: [SlashCommandArgument.fromProps({ | |
| 84 | - typeList: [ARGUMENT_TYPE.STRING], | |
| 83 | + description: 'help topic', | |
| 85 | - enumList: [ | |
| 86 | - new SlashCommandEnumValue('slash', 'slash commands (STscript)', enumTypes.command, '/'), | |
| 87 | - new SlashCommandEnumValue('macros', '{{macros}} (text replacement)', enumTypes.macro, enumIcons.macro), | |
| 88 | - new SlashCommandEnumValue('format', 'chat/text formatting', enumTypes.name, '★'), | |
| 89 | - new SlashCommandEnumValue('hotkeys', 'keyboard shortcuts', enumTypes.enum, '⏎'), | |
| 90 | - ], | |
| 91 | - })], | |
| 92 | - helpString: 'Get help on macros, chat formatting and commands.', | |
| 93 | -})); | |
| 94 | -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 95 | - name: 'persona', | |
| 96 | - callback: setNameCallback, | |
| 97 | - namedArgumentList: [ | |
| 98 | - new SlashCommandNamedArgument( | |
| 99 | - 'mode', 'The mode for persona selection. ("lookup" = search for existing persona, "temp" = create a temporary name, set a temporary name, "all" = allow both in the same command)', | |
| 100 | - [ARGUMENT_TYPE.STRING], false, false, 'all', ['lookup', 'temp', 'all'], | |
| 101 | - ), | |
| 102 | - ], | |
| 103 | - unnamedArgumentList: [ | |
| 104 | - SlashCommandArgument.fromProps({ | |
| 105 | - description: 'persona name', | |
| 106 | 84 | typeList: [ARGUMENT_TYPE.STRING], |
| 107 | 85 | isRequiredenumList: true,[ |
| 108 | - enumProvider: commonEnumProviders.personas, | |
| 86 | + new SlashCommandEnumValue('slash', 'slash commands (STscript)', enumTypes.command, '/'), | |
| 109 | - }), | |
| 87 | + new SlashCommandEnumValue('macros', '{{macros}} (text replacement)', enumTypes.macro, enumIcons.macro), | |
| 110 | - ], | |
| 88 | + new SlashCommandEnumValue('format', 'chat/text formatting', enumTypes.name, '★'), | |
| 111 | - helpString: 'Selects the given persona with its name and avatar (by name or avatar url). If no matching persona exists, applies a temporary name.', | |
| 89 | + new SlashCommandEnumValue('hotkeys', 'keyboard shortcuts', enumTypes.enum, '⏎'), | |
| 112 | - aliases: ['name'], | |
| 90 | + ], | |
| 113 | -})); | |
| 91 | + })], | |
| 114 | -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 92 | + helpString: 'Get help on macros, chat formatting and commands.', | |
| 115 | - name: 'sync', | |
| 93 | + })); | |
| 116 | - callback: syncCallback, | |
| 94 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 117 | - helpString: 'Syncs the user persona in user-attributed messages in the current chat.', | |
| 95 | + name: 'persona', | |
| 118 | -})); | |
| 96 | + callback: setNameCallback, | |
| 119 | -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 97 | + namedArgumentList: [ | |
| 120 | - name: 'lock', | |
| 98 | + new SlashCommandNamedArgument( | |
| 121 | - callback: lockPersonaCallback, | |
| 99 | + 'mode', 'The mode for persona selection. ("lookup" = search for existing persona, "temp" = create a temporary name, set a temporary name, "all" = allow both in the same command)', | |
| 122 | - aliases: ['bind'], | |
| 100 | + [ARGUMENT_TYPE.STRING], false, false, 'all', ['lookup', 'temp', 'all'], | |
| 123 | - helpString: 'Locks/unlocks a persona (name and avatar) to the current chat', | |
| 101 | + ), | |
| 124 | - unnamedArgumentList: [ | |
| 102 | + ], | |
| 125 | - SlashCommandArgument.fromProps({ | |
| 103 | + unnamedArgumentList: [ | |
| 126 | - description: 'state', | |
| 104 | + SlashCommandArgument.fromProps({ | |
| 127 | - typeList: [ARGUMENT_TYPE.STRING], | |
| 105 | + description: 'persona name', | |
| 128 | - isRequired: true, | |
| 106 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 129 | - defaultValue: 'toggle', | |
| 107 | + isRequired: true, | |
| 130 | 108 | enumProvider: commonEnumProviders.boolean('onOffToggle')personas, |
| 131 | 109 | }), |
| 132 | 110 | ], |
| 133 | -})); | |
| 111 | + helpString: 'Selects the given persona with its name and avatar (by name or avatar url). If no matching persona exists, applies a temporary name.', | |
| 134 | -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 112 | + aliases: ['name'], | |
| 135 | - name: 'bg', | |
| 113 | + })); | |
| 136 | - callback: setBackgroundCallback, | |
| 114 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 137 | 115 | aliases name: ['backgroundsync'], |
| 138 | - returns: 'the current background', | |
| 116 | + callback: syncCallback, | |
| 139 | - unnamedArgumentList: [ | |
| 117 | + helpString: 'Syncs the user persona in user-attributed messages in the current chat.', | |
| 140 | - SlashCommandArgument.fromProps({ | |
| 118 | + })); | |
| 141 | - description: 'filename', | |
| 119 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 142 | - typeList: [ARGUMENT_TYPE.STRING], | |
| 120 | + name: 'lock', | |
| 143 | 121 | isRequired callback: truelockPersonaCallback, |
| 144 | - enumProvider: () => [...document.querySelectorAll('.bg_example')] | |
| 122 | + aliases: ['bind'], | |
| 145 | - .map(it => new SlashCommandEnumValue(it.getAttribute('bgfile'))) | |
| 123 | + helpString: 'Locks/unlocks a persona (name and avatar) to the current chat', | |
| 146 | - .filter(it => it.value?.length), | |
| 124 | + unnamedArgumentList: [ | |
| 147 | - }), | |
| 125 | + SlashCommandArgument.fromProps({ | |
| 148 | - ], | |
| 126 | + description: 'state', | |
| 149 | - helpString: ` | |
| 127 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 128 | + isRequired: true, | |
| 129 | + defaultValue: 'toggle', | |
| 130 | + enumProvider: commonEnumProviders.boolean('onOffToggle'), | |
| 131 | + }), | |
| 132 | + ], | |
| 133 | + })); | |
| 134 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 135 | + name: 'bg', | |
| 136 | + callback: setBackgroundCallback, | |
| 137 | + aliases: ['background'], | |
| 138 | + returns: 'the current background', | |
| 139 | + unnamedArgumentList: [ | |
| 140 | + SlashCommandArgument.fromProps({ | |
| 141 | + description: 'filename', | |
| 142 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 143 | + isRequired: true, | |
| 144 | + enumProvider: () => [...document.querySelectorAll('.bg_example')] | |
| 145 | + .map(it => new SlashCommandEnumValue(it.getAttribute('bgfile'))) | |
| 146 | + .filter(it => it.value?.length), | |
| 147 | + }), | |
| 148 | + ], | |
| 149 | + helpString: ` | |
| 150 | 150 | <div> |
| 151 | 151 | Sets a background according to the provided filename. Partial names allowed. |
| 152 | 152 | </div> |
| @@ -159,35 +159,35 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 159 | 159 | </ul> |
| 160 | 160 | </div> |
| 161 | 161 | `, |
| 162 | 162 | })); |
| 163 | 163 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 164 | 164 | name: 'sendas', |
| 165 | 165 | callback: sendMessageAs, |
| 166 | 166 | namedArgumentList: [ |
| 167 | 167 | SlashCommandNamedArgument.fromProps({ |
| 168 | 168 | name: 'name', |
| 169 | 169 | description: 'Character name', |
| 170 | 170 | typeList: [ARGUMENT_TYPE.STRING], |
| 171 | 171 | isRequired: true, |
| 172 | 172 | enumProvider: commonEnumProviders.characters('character'), |
| 173 | 173 | forceEnum: false, |
| 174 | 174 | }), |
| 175 | 175 | new SlashCommandNamedArgument( |
| 176 | 176 | 'compact', 'Use compact layout', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', |
| 177 | 177 | ), |
| 178 | 178 | SlashCommandNamedArgument.fromProps({ |
| 179 | 179 | name: 'at', |
| 180 | - description: 'position to insert the message', | |
| 180 | + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', | |
| 181 | 181 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 182 | 182 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 183 | 183 | }), |
| 184 | 184 | ], |
| 185 | 185 | unnamedArgumentList: [ |
| 186 | 186 | new SlashCommandArgument( |
| 187 | 187 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 188 | 188 | ), |
| 189 | 189 | ], |
| 190 | 190 | helpString: ` |
| 191 | 191 | <div> |
| 192 | 192 | Sends a message as a specific character. Uses the character avatar if it exists in the characters list. |
| 193 | 193 | </div> |
| @@ -204,33 +204,33 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 204 | 204 | If "compact" is set to true, the message is sent using a compact layout. |
| 205 | 205 | </div> |
| 206 | 206 | `, |
| 207 | 207 | })); |
| 208 | 208 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 209 | 209 | name: 'sys', |
| 210 | 210 | callback: sendNarratorMessage, |
| 211 | 211 | aliases: ['nar'], |
| 212 | 212 | namedArgumentList: [ |
| 213 | 213 | new SlashCommandNamedArgument( |
| 214 | 214 | 'compact', |
| 215 | 215 | 'compact layout', |
| 216 | 216 | [ARGUMENT_TYPE.BOOLEAN], |
| 217 | 217 | false, |
| 218 | 218 | false, |
| 219 | 219 | 'false', |
| 220 | 220 | ), |
| 221 | 221 | SlashCommandNamedArgument.fromProps({ |
| 222 | 222 | name: 'at', |
| 223 | - description: 'position to insert the message', | |
| 223 | + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', | |
| 224 | 224 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 225 | 225 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 226 | 226 | }), |
| 227 | 227 | ], |
| 228 | 228 | unnamedArgumentList: [ |
| 229 | 229 | new SlashCommandArgument( |
| 230 | 230 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 231 | 231 | ), |
| 232 | 232 | ], |
| 233 | 233 | helpString: ` |
| 234 | 234 | <div> |
| 235 | 235 | Sends a message as a system narrator. |
| 236 | 236 | </div> |
| @@ -249,44 +249,44 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 249 | 249 | </ul> |
| 250 | 250 | </div> |
| 251 | 251 | `, |
| 252 | 252 | })); |
| 253 | 253 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 254 | 254 | name: 'sysname', |
| 255 | 255 | callback: setNarratorName, |
| 256 | 256 | unnamedArgumentList: [ |
| 257 | 257 | new SlashCommandArgument( |
| 258 | 258 | 'name', [ARGUMENT_TYPE.STRING], false, |
| 259 | 259 | ), |
| 260 | 260 | ], |
| 261 | 261 | helpString: 'Sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.', |
| 262 | 262 | })); |
| 263 | 263 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 264 | 264 | name: 'comment', |
| 265 | 265 | callback: sendCommentMessage, |
| 266 | 266 | namedArgumentList: [ |
| 267 | 267 | new SlashCommandNamedArgument( |
| 268 | 268 | 'compact', |
| 269 | 269 | 'Whether to use a compact layout', |
| 270 | 270 | [ARGUMENT_TYPE.BOOLEAN], |
| 271 | 271 | false, |
| 272 | 272 | false, |
| 273 | 273 | 'false', |
| 274 | 274 | ), |
| 275 | 275 | SlashCommandNamedArgument.fromProps({ |
| 276 | 276 | name: 'at', |
| 277 | - description: 'position to insert the message', | |
| 277 | + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', | |
| 278 | 278 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 279 | 279 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 280 | 280 | }), |
| 281 | 281 | ], |
| 282 | 282 | unnamedArgumentList: [ |
| 283 | 283 | new SlashCommandArgument( |
| 284 | 284 | 'text', |
| 285 | 285 | [ARGUMENT_TYPE.STRING], |
| 286 | 286 | true, |
| 287 | 287 | ), |
| 288 | 288 | ], |
| 289 | 289 | helpString: ` |
| 290 | 290 | <div> |
| 291 | 291 | Adds a note/comment message not part of the chat. |
| 292 | 292 | </div> |
| @@ -305,35 +305,35 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 305 | 305 | </ul> |
| 306 | 306 | </div> |
| 307 | 307 | `, |
| 308 | 308 | })); |
| 309 | 309 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 310 | 310 | name: 'single', |
| 311 | 311 | callback: setStoryModeCallback, |
| 312 | 312 | aliases: ['story'], |
| 313 | 313 | helpString: 'Sets the message style to single document mode without names or avatars visible.', |
| 314 | 314 | })); |
| 315 | 315 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 316 | 316 | name: 'bubble', |
| 317 | 317 | callback: setBubbleModeCallback, |
| 318 | 318 | aliases: ['bubbles'], |
| 319 | 319 | helpString: 'Sets the message style to bubble chat mode.', |
| 320 | 320 | })); |
| 321 | 321 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 322 | 322 | name: 'flat', |
| 323 | 323 | callback: setFlatModeCallback, |
| 324 | 324 | aliases: ['default'], |
| 325 | 325 | helpString: 'Sets the message style to flat chat mode.', |
| 326 | 326 | })); |
| 327 | 327 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 328 | 328 | name: 'continue', |
| 329 | 329 | callback: continueChatCallback, |
| 330 | 330 | aliases: ['cont'], |
| 331 | 331 | unnamedArgumentList: [ |
| 332 | 332 | new SlashCommandArgument( |
| 333 | 333 | 'prompt', [ARGUMENT_TYPE.STRING], false, |
| 334 | 334 | ), |
| 335 | 335 | ], |
| 336 | 336 | helpString: ` |
| 337 | 337 | <div> |
| 338 | 338 | Continues the last message in the chat, with an optional additional prompt. |
| 339 | 339 | </div> |
| @@ -351,87 +351,87 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 351 | 351 | </ul> |
| 352 | 352 | </div> |
| 353 | 353 | `, |
| 354 | 354 | })); |
| 355 | 355 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 356 | 356 | name: 'go', |
| 357 | 357 | callback: goToCharacterCallback, |
| 358 | 358 | unnamedArgumentList: [ |
| 359 | 359 | SlashCommandArgument.fromProps({ |
| 360 | 360 | description: 'name', |
| 361 | 361 | typeList: [ARGUMENT_TYPE.STRING], |
| 362 | 362 | isRequired: true, |
| 363 | 363 | enumProvider: commonEnumProviders.characters('all'), |
| 364 | 364 | }), |
| 365 | 365 | ], |
| 366 | 366 | helpString: 'Opens up a chat with the character or group by its name', |
| 367 | 367 | aliases: ['char'], |
| 368 | 368 | })); |
| 369 | 369 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 370 | 370 | name: 'rename-char', |
| 371 | 371 | /** @param {{silent: string, chats: string}} options @param {string} name */ |
| 372 | 372 | callback: async ({ silent = 'true', chats = null }, name) => { |
| 373 | 373 | const renamed = await renameCharacter(name, { silent: isTrueBoolean(silent), renameChats: chats !== null ? isTrueBoolean(chats) : null }); |
| 374 | 374 | return String(renamed); |
| 375 | 375 | }, |
| 376 | 376 | returns: 'true/false - Whether the rename was successful', |
| 377 | 377 | namedArgumentList: [ |
| 378 | 378 | new SlashCommandNamedArgument( |
| 379 | 379 | 'silent', 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true', |
| 380 | 380 | ), |
| 381 | 381 | new SlashCommandNamedArgument( |
| 382 | 382 | 'chats', 'Rename char in all previous chats', [ARGUMENT_TYPE.BOOLEAN], false, false, '<null>', |
| 383 | 383 | ), |
| 384 | 384 | ], |
| 385 | 385 | unnamedArgumentList: [ |
| 386 | 386 | new SlashCommandArgument( |
| 387 | 387 | 'new char name', [ARGUMENT_TYPE.STRING], true, |
| 388 | 388 | ), |
| 389 | 389 | ], |
| 390 | 390 | helpString: 'Renames the current character.', |
| 391 | 391 | })); |
| 392 | 392 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 393 | 393 | name: 'sysgen', |
| 394 | 394 | callback: generateSystemMessage, |
| 395 | 395 | unnamedArgumentList: [ |
| 396 | 396 | new SlashCommandArgument( |
| 397 | 397 | 'prompt', [ARGUMENT_TYPE.STRING], true, |
| 398 | 398 | ), |
| 399 | 399 | ], |
| 400 | 400 | helpString: 'Generates a system message using a specified prompt.', |
| 401 | 401 | })); |
| 402 | 402 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 403 | 403 | name: 'ask', |
| 404 | 404 | callback: askCharacter, |
| 405 | 405 | namedArgumentList: [ |
| 406 | 406 | SlashCommandNamedArgument.fromProps({ |
| 407 | 407 | name: 'name', |
| 408 | 408 | description: 'character name', |
| 409 | 409 | typeList: [ARGUMENT_TYPE.STRING], |
| 410 | 410 | isRequired: true, |
| 411 | 411 | enumProvider: commonEnumProviders.characters('character'), |
| 412 | 412 | }), |
| 413 | 413 | ], |
| 414 | 414 | unnamedArgumentList: [ |
| 415 | 415 | new SlashCommandArgument( |
| 416 | 416 | 'prompt', [ARGUMENT_TYPE.STRING], true, false, |
| 417 | 417 | ), |
| 418 | 418 | ], |
| 419 | 419 | helpString: 'Asks a specified character card a prompt. Character name must be provided in a named argument.', |
| 420 | 420 | })); |
| 421 | 421 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 422 | 422 | name: 'delname', |
| 423 | 423 | callback: deleteMessagesByNameCallback, |
| 424 | 424 | namedArgumentList: [], |
| 425 | 425 | unnamedArgumentList: [ |
| 426 | 426 | SlashCommandArgument.fromProps({ |
| 427 | 427 | description: 'name', |
| 428 | 428 | typeList: [ARGUMENT_TYPE.STRING], |
| 429 | 429 | isRequired: true, |
| 430 | 430 | enumProvider: commonEnumProviders.characters('character'), |
| 431 | 431 | }), |
| 432 | 432 | ], |
| 433 | 433 | aliases: ['cancel'], |
| 434 | 434 | helpString: ` |
| 435 | 435 | <div> |
| 436 | 436 | Deletes all messages attributed to a specified name. |
| 437 | 437 | </div> |
| @@ -444,41 +444,41 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 444 | 444 | </ul> |
| 445 | 445 | </div> |
| 446 | 446 | `, |
| 447 | 447 | })); |
| 448 | 448 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 449 | 449 | name: 'send', |
| 450 | 450 | callback: sendUserMessageCallback, |
| 451 | 451 | namedArgumentList: [ |
| 452 | 452 | new SlashCommandNamedArgument( |
| 453 | 453 | 'compact', |
| 454 | 454 | 'whether to use a compact layout', |
| 455 | 455 | [ARGUMENT_TYPE.BOOLEAN], |
| 456 | 456 | false, |
| 457 | 457 | false, |
| 458 | 458 | 'false', |
| 459 | 459 | ), |
| 460 | 460 | SlashCommandNamedArgument.fromProps({ |
| 461 | 461 | name: 'at', |
| 462 | - description: 'position to insert the message', | |
| 462 | + description: 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.', | |
| 463 | 463 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 464 | 464 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 465 | 465 | }), |
| 466 | 466 | SlashCommandNamedArgument.fromProps({ |
| 467 | 467 | name: 'name', |
| 468 | 468 | description: 'display name', |
| 469 | 469 | typeList: [ARGUMENT_TYPE.STRING], |
| 470 | 470 | defaultValue: '{{user}}', |
| 471 | 471 | enumProvider: commonEnumProviders.characters('character'), |
| 472 | 472 | }), |
| 473 | 473 | ], |
| 474 | 474 | unnamedArgumentList: [ |
| 475 | 475 | new SlashCommandArgument( |
| 476 | 476 | 'text', |
| 477 | 477 | [ARGUMENT_TYPE.STRING], |
| 478 | 478 | true, |
| 479 | 479 | ), |
| 480 | 480 | ], |
| 481 | 481 | helpString: ` |
| 482 | 482 | <div> |
| 483 | 483 | Adds a user message to the chat log without triggering a generation. |
| 484 | 484 | </div> |
| @@ -500,29 +500,29 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 500 | 500 | </ul> |
| 501 | 501 | </div> |
| 502 | 502 | `, |
| 503 | 503 | })); |
| 504 | 504 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 505 | 505 | name: 'trigger', |
| 506 | 506 | callback: triggerGenerationCallback, |
| 507 | 507 | namedArgumentList: [ |
| 508 | 508 | new SlashCommandNamedArgument( |
| 509 | 509 | 'await', |
| 510 | 510 | 'Whether to await for the triggered generation before continuing', |
| 511 | 511 | [ARGUMENT_TYPE.BOOLEAN], |
| 512 | 512 | false, |
| 513 | 513 | false, |
| 514 | 514 | 'false', |
| 515 | 515 | ), |
| 516 | 516 | ], |
| 517 | 517 | unnamedArgumentList: [ |
| 518 | 518 | SlashCommandArgument.fromProps({ |
| 519 | 519 | description: 'group member index (starts with 0) or name', |
| 520 | 520 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], |
| 521 | 521 | isRequired: false, |
| 522 | 522 | enumProvider: commonEnumProviders.groupMembers(), |
| 523 | 523 | }), |
| 524 | 524 | ], |
| 525 | 525 | helpString: ` |
| 526 | 526 | <div> |
| 527 | 527 | Triggers a message generation. If in group, can trigger a message for the specified group member index or name. |
| 528 | 528 | </div> |
| @@ -530,74 +530,74 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 530 | 530 | If <code>await=true</code> named argument is passed, the command will await for the triggered generation before continuing. |
| 531 | 531 | </div> |
| 532 | 532 | `, |
| 533 | 533 | })); |
| 534 | 534 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 535 | 535 | name: 'hide', |
| 536 | 536 | callback: hideMessageCallback, |
| 537 | 537 | unnamedArgumentList: [ |
| 538 | 538 | SlashCommandArgument.fromProps({ |
| 539 | 539 | description: 'message index (starts with 0) or range', |
| 540 | 540 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], |
| 541 | 541 | isRequired: true, |
| 542 | 542 | enumProvider: commonEnumProviders.messages(), |
| 543 | 543 | }), |
| 544 | 544 | ], |
| 545 | 545 | helpString: 'Hides a chat message from the prompt.', |
| 546 | 546 | })); |
| 547 | 547 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 548 | 548 | name: 'unhide', |
| 549 | 549 | callback: unhideMessageCallback, |
| 550 | 550 | unnamedArgumentList: [ |
| 551 | 551 | SlashCommandArgument.fromProps({ |
| 552 | 552 | description: 'message index (starts with 0) or range', |
| 553 | 553 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], |
| 554 | 554 | isRequired: true, |
| 555 | 555 | enumProvider: commonEnumProviders.messages(), |
| 556 | 556 | }), |
| 557 | 557 | ], |
| 558 | 558 | helpString: 'Unhides a message from the prompt.', |
| 559 | 559 | })); |
| 560 | 560 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 561 | 561 | name: 'member-disable', |
| 562 | 562 | callback: disableGroupMemberCallback, |
| 563 | 563 | aliases: ['disable', 'disablemember', 'memberdisable'], |
| 564 | 564 | unnamedArgumentList: [ |
| 565 | 565 | SlashCommandArgument.fromProps({ |
| 566 | 566 | description: 'member index (starts with 0) or name', |
| 567 | 567 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], |
| 568 | 568 | isRequired: true, |
| 569 | 569 | enumProvider: commonEnumProviders.groupMembers(), |
| 570 | 570 | }), |
| 571 | 571 | ], |
| 572 | 572 | helpString: 'Disables a group member from being drafted for replies.', |
| 573 | 573 | })); |
| 574 | 574 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 575 | 575 | name: 'member-enable', |
| 576 | 576 | aliases: ['enable', 'enablemember', 'memberenable'], |
| 577 | 577 | callback: enableGroupMemberCallback, |
| 578 | 578 | unnamedArgumentList: [ |
| 579 | 579 | SlashCommandArgument.fromProps({ |
| 580 | 580 | description: 'member index (starts with 0) or name', |
| 581 | 581 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], |
| 582 | 582 | isRequired: true, |
| 583 | 583 | enumProvider: commonEnumProviders.groupMembers(), |
| 584 | 584 | }), |
| 585 | 585 | ], |
| 586 | 586 | helpString: 'Enables a group member to be drafted for replies.', |
| 587 | 587 | })); |
| 588 | 588 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 589 | 589 | name: 'member-add', |
| 590 | 590 | callback: addGroupMemberCallback, |
| 591 | 591 | aliases: ['addmember', 'memberadd'], |
| 592 | 592 | unnamedArgumentList: [ |
| 593 | 593 | SlashCommandArgument.fromProps({ |
| 594 | 594 | description: 'character name', |
| 595 | 595 | typeList: [ARGUMENT_TYPE.STRING], |
| 596 | 596 | isRequired: true, |
| 597 | 597 | enumProvider: () => selected_group ? commonEnumProviders.characters('character')() : [], |
| 598 | 598 | }), |
| 599 | 599 | ], |
| 600 | 600 | helpString: ` |
| 601 | 601 | <div> |
| 602 | 602 | Adds a new group member to the group chat. |
| 603 | 603 | </div> |
| @@ -610,20 +610,20 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 610 | 610 | </ul> |
| 611 | 611 | </div> |
| 612 | 612 | `, |
| 613 | 613 | })); |
| 614 | 614 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 615 | 615 | name: 'member-remove', |
| 616 | 616 | callback: removeGroupMemberCallback, |
| 617 | 617 | aliases: ['removemember', 'memberremove'], |
| 618 | 618 | unnamedArgumentList: [ |
| 619 | 619 | SlashCommandArgument.fromProps({ |
| 620 | 620 | description: 'member index (starts with 0) or name', |
| 621 | 621 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], |
| 622 | 622 | isRequired: true, |
| 623 | 623 | enumProvider: commonEnumProviders.groupMembers(), |
| 624 | 624 | }), |
| 625 | 625 | ], |
| 626 | 626 | helpString: ` |
| 627 | 627 | <div> |
| 628 | 628 | Removes a group member from the group chat. |
| 629 | 629 | </div> |
| @@ -637,47 +637,47 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 637 | 637 | </ul> |
| 638 | 638 | </div> |
| 639 | 639 | `, |
| 640 | 640 | })); |
| 641 | 641 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 642 | 642 | name: 'member-up', |
| 643 | 643 | callback: moveGroupMemberUpCallback, |
| 644 | 644 | aliases: ['upmember', 'memberup'], |
| 645 | 645 | unnamedArgumentList: [ |
| 646 | 646 | SlashCommandArgument.fromProps({ |
| 647 | 647 | description: 'member index (starts with 0) or name', |
| 648 | 648 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], |
| 649 | 649 | isRequired: true, |
| 650 | 650 | enumProvider: commonEnumProviders.groupMembers(), |
| 651 | 651 | }), |
| 652 | 652 | ], |
| 653 | 653 | helpString: 'Moves a group member up in the group chat list.', |
| 654 | 654 | })); |
| 655 | 655 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 656 | 656 | name: 'member-down', |
| 657 | 657 | callback: moveGroupMemberDownCallback, |
| 658 | 658 | aliases: ['downmember', 'memberdown'], |
| 659 | 659 | unnamedArgumentList: [ |
| 660 | 660 | SlashCommandArgument.fromProps({ |
| 661 | 661 | description: 'member index (starts with 0) or name', |
| 662 | 662 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], |
| 663 | 663 | isRequired: true, |
| 664 | 664 | enumProvider: commonEnumProviders.groupMembers(), |
| 665 | 665 | }), |
| 666 | 666 | ], |
| 667 | 667 | helpString: 'Moves a group member down in the group chat list.', |
| 668 | 668 | })); |
| 669 | 669 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 670 | 670 | name: 'peek', |
| 671 | 671 | callback: peekCallback, |
| 672 | 672 | unnamedArgumentList: [ |
| 673 | 673 | SlashCommandArgument.fromProps({ |
| 674 | 674 | description: 'member index (starts with 0) or name', |
| 675 | 675 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], |
| 676 | 676 | isRequired: true, |
| 677 | 677 | enumProvider: commonEnumProviders.groupMembers(), |
| 678 | 678 | }), |
| 679 | 679 | ], |
| 680 | 680 | helpString: ` |
| 681 | 681 | <div> |
| 682 | 682 | Shows a group member character card without switching chats. |
| 683 | 683 | </div> |
| @@ -691,22 +691,22 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 691 | 691 | </ul> |
| 692 | 692 | </div> |
| 693 | 693 | `, |
| 694 | 694 | })); |
| 695 | 695 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 696 | 696 | name: 'delswipe', |
| 697 | 697 | callback: deleteSwipeCallback, |
| 698 | 698 | aliases: ['swipedel'], |
| 699 | 699 | unnamedArgumentList: [ |
| 700 | 700 | SlashCommandArgument.fromProps({ |
| 701 | 701 | description: '1-based swipe id', |
| 702 | 702 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 703 | 703 | isRequired: true, |
| 704 | 704 | enumProvider: () => Array.isArray(chat[chat.length - 1]?.swipes) ? |
| 705 | 705 | chat[chat.length - 1].swipes.map((/** @type {string} */ swipe, /** @type {number} */ i) => new SlashCommandEnumValue(String(i + 1), swipe, enumTypes.enum, enumIcons.message)) |
| 706 | 706 | : [], |
| 707 | 707 | }), |
| 708 | 708 | ], |
| 709 | 709 | helpString: ` |
| 710 | 710 | <div> |
| 711 | 711 | Deletes a swipe from the last chat message. If swipe id is not provided, it deletes the current swipe. |
| 712 | 712 | </div> |
| @@ -724,34 +724,60 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 724 | 724 | </ul> |
| 725 | 725 | </div> |
| 726 | 726 | `, |
| 727 | 727 | })); |
| 728 | 728 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 729 | 729 | name: 'echo', |
| 730 | 730 | callback: echoCallback, |
| 731 | 731 | returns: 'the text', |
| 732 | 732 | namedArgumentList: [ |
| 733 | 733 | new SlashCommandNamedArgument( |
| 734 | 734 | 'title', 'title of the toast message', [ARGUMENT_TYPE.STRING], false, |
| 735 | 735 | ), |
| 736 | 736 | SlashCommandNamedArgument.fromProps({ |
| 737 | 737 | name: 'severity', |
| 738 | 738 | description: 'severity level of the toast message', |
| 739 | 739 | typeList: [ARGUMENT_TYPE.STRING], |
| 740 | 740 | defaultValue: 'info', |
| 741 | 741 | enumProvider: () => [ |
| 742 | 742 | new SlashCommandEnumValue('info', 'info', enumTypes.macro, 'ℹ️'), |
| 743 | 743 | new SlashCommandEnumValue('warning', 'warning', enumTypes.enum, '⚠️'), |
| 744 | 744 | new SlashCommandEnumValue('error', 'error', enumTypes.enum, '❗'), |
| 745 | 745 | new SlashCommandEnumValue('success', 'success', enumTypes.enum, '✅'), |
| 746 | 746 | ], |
| 747 | 747 | }), |
| 748 | - ], | |
| 748 | + SlashCommandNamedArgument.fromProps({ | |
| 749 | - unnamedArgumentList: [ | |
| 749 | + name: 'timeout', | |
| 750 | - new SlashCommandArgument( | |
| 750 | + description: 'time in milliseconds to display the toast message. Set this and \'extendedTimeout\' to 0 to show indefinitely until dismissed.', | |
| 751 | - 'text', [ARGUMENT_TYPE.STRING], true, | |
| 751 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 752 | - ), | |
| 752 | + defaultValue: `${toastr.options.timeOut}`, | |
| 753 | - ], | |
| 753 | + }), | |
| 754 | - helpString: ` | |
| 754 | + SlashCommandNamedArgument.fromProps({ | |
| 755 | + name: 'extendedTimeout', | |
| 756 | + description: 'time in milliseconds to display the toast message. Set this and \'timeout\' to 0 to show indefinitely until dismissed.', | |
| 757 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 758 | + defaultValue: `${toastr.options.extendedTimeOut}`, | |
| 759 | + }), | |
| 760 | + SlashCommandNamedArgument.fromProps({ | |
| 761 | + name: 'preventDuplicates', | |
| 762 | + description: 'prevent duplicate toasts with the same message from being displayed.', | |
| 763 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 764 | + defaultValue: 'false', | |
| 765 | + enumList: commonEnumProviders.boolean('trueFalse')(), | |
| 766 | + }), | |
| 767 | + SlashCommandNamedArgument.fromProps({ | |
| 768 | + name: 'awaitDismissal', | |
| 769 | + description: 'wait for the toast to be dismissed before continuing.', | |
| 770 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 771 | + defaultValue: 'false', | |
| 772 | + enumList: commonEnumProviders.boolean('trueFalse')(), | |
| 773 | + }), | |
| 774 | + ], | |
| 775 | + unnamedArgumentList: [ | |
| 776 | + new SlashCommandArgument( | |
| 777 | + 'text', [ARGUMENT_TYPE.STRING], true, | |
| 778 | + ), | |
| 779 | + ], | |
| 780 | + helpString: ` | |
| 755 | 781 | <div> |
| 756 | 782 | Echoes the provided text to a toast message. Useful for pipes debugging. |
| 757 | 783 | </div> |
| @@ -764,42 +790,42 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 764 | 790 | </ul> |
| 765 | 791 | </div> |
| 766 | 792 | `, |
| 767 | 793 | })); |
| 768 | 794 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 769 | 795 | name: 'gen', |
| 770 | 796 | callback: generateCallback, |
| 771 | 797 | returns: 'generated text', |
| 772 | 798 | namedArgumentList: [ |
| 773 | 799 | new SlashCommandNamedArgument( |
| 774 | 800 | 'lock', 'lock user input during generation', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(), |
| 775 | 801 | ), |
| 776 | 802 | SlashCommandNamedArgument.fromProps({ |
| 777 | 803 | name: 'name', |
| 778 | 804 | description: 'in-prompt name for instruct mode', |
| 779 | 805 | typeList: [ARGUMENT_TYPE.STRING], |
| 780 | 806 | defaultValue: 'System', |
| 781 | 807 | enumProvider: () => [...commonEnumProviders.characters('character')(), new SlashCommandEnumValue('System', null, enumTypes.enum, enumIcons.assistant)], |
| 782 | 808 | forceEnum: false, |
| 783 | 809 | }), |
| 784 | 810 | new SlashCommandNamedArgument( |
| 785 | 811 | 'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER], false, |
| 786 | 812 | ), |
| 787 | 813 | SlashCommandNamedArgument.fromProps({ |
| 788 | 814 | name: 'as', |
| 789 | 815 | description: 'role of the output prompt', |
| 790 | 816 | typeList: [ARGUMENT_TYPE.STRING], |
| 791 | 817 | enumList: [ |
| 792 | 818 | new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.assistant), |
| 793 | 819 | new SlashCommandEnumValue('char', null, enumTypes.enum, enumIcons.character), |
| 794 | 820 | ], |
| 795 | 821 | }), |
| 796 | 822 | ], |
| 797 | 823 | unnamedArgumentList: [ |
| 798 | 824 | new SlashCommandArgument( |
| 799 | 825 | 'prompt', [ARGUMENT_TYPE.STRING], true, |
| 800 | 826 | ), |
| 801 | 827 | ], |
| 802 | 828 | helpString: ` |
| 803 | 829 | <div> |
| 804 | 830 | Generates text using the provided prompt and passes it to the next command through the pipe, optionally locking user input while generating and allowing to configure the in-prompt name for instruct mode (default = "System"). |
| 805 | 831 | </div> |
| @@ -807,43 +833,43 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 807 | 833 | "as" argument controls the role of the output prompt: system (default) or char. If "length" argument is provided as a number in tokens, allows to temporarily override an API response length. |
| 808 | 834 | </div> |
| 809 | 835 | `, |
| 810 | 836 | })); |
| 811 | 837 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 812 | 838 | name: 'genraw', |
| 813 | 839 | callback: generateRawCallback, |
| 814 | 840 | returns: 'generated text', |
| 815 | 841 | namedArgumentList: [ |
| 816 | 842 | new SlashCommandNamedArgument( |
| 817 | 843 | 'lock', 'lock user input during generation', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(), |
| 818 | 844 | ), |
| 819 | 845 | new SlashCommandNamedArgument( |
| 820 | 846 | 'instruct', 'use instruct mode', [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(), |
| 821 | 847 | ), |
| 822 | 848 | new SlashCommandNamedArgument( |
| 823 | 849 | 'stop', 'one-time custom stop strings', [ARGUMENT_TYPE.LIST], false, |
| 824 | 850 | ), |
| 825 | 851 | SlashCommandNamedArgument.fromProps({ |
| 826 | 852 | name: 'as', |
| 827 | 853 | description: 'role of the output prompt', |
| 828 | 854 | typeList: [ARGUMENT_TYPE.STRING], |
| 829 | 855 | enumList: [ |
| 830 | 856 | new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.assistant), |
| 831 | 857 | new SlashCommandEnumValue('char', null, enumTypes.enum, enumIcons.character), |
| 832 | 858 | ], |
| 833 | 859 | }), |
| 834 | 860 | new SlashCommandNamedArgument( |
| 835 | 861 | 'system', 'system prompt at the start', [ARGUMENT_TYPE.STRING], false, |
| 836 | 862 | ), |
| 837 | 863 | new SlashCommandNamedArgument( |
| 838 | 864 | 'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER], false, |
| 839 | 865 | ), |
| 840 | 866 | ], |
| 841 | 867 | unnamedArgumentList: [ |
| 842 | 868 | new SlashCommandArgument( |
| 843 | 869 | 'prompt', [ARGUMENT_TYPE.STRING], true, |
| 844 | 870 | ), |
| 845 | 871 | ], |
| 846 | 872 | helpString: ` |
| 847 | 873 | <div> |
| 848 | 874 | Generates text using the provided prompt and passes it to the next command through the pipe, optionally locking user input while generating. Does not include chat history or character card. |
| 849 | 875 | </div> |
| @@ -860,55 +886,55 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 860 | 886 | If "length" argument is provided as a number in tokens, allows to temporarily override an API response length. |
| 861 | 887 | </div> |
| 862 | 888 | `, |
| 863 | 889 | })); |
| 864 | 890 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 865 | 891 | name: 'addswipe', |
| 866 | 892 | callback: addSwipeCallback, |
| 867 | 893 | aliases: ['swipeadd'], |
| 868 | 894 | unnamedArgumentList: [ |
| 869 | 895 | new SlashCommandArgument( |
| 870 | 896 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 871 | 897 | ), |
| 872 | 898 | ], |
| 873 | 899 | helpString: 'Adds a swipe to the last chat message.', |
| 874 | 900 | })); |
| 875 | 901 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 876 | 902 | name: 'abort', |
| 877 | 903 | callback: abortCallback, |
| 878 | 904 | namedArgumentList: [ |
| 879 | 905 | SlashCommandNamedArgument.fromProps({ |
| 880 | 906 | name: 'quiet', |
| 881 | 907 | description: 'Whether to suppress the toast message notifying about the /abort call.', |
| 882 | 908 | typeList: [ARGUMENT_TYPE.BOOLEAN], |
| 883 | 909 | defaultValue: 'true', |
| 884 | 910 | }), |
| 885 | 911 | ], |
| 886 | 912 | unnamedArgumentList: [ |
| 887 | 913 | SlashCommandArgument.fromProps({ |
| 888 | 914 | description: 'The reason for aborting command execution. Shown when quiet=false', |
| 889 | 915 | typeList: [ARGUMENT_TYPE.STRING], |
| 890 | 916 | }), |
| 891 | 917 | ], |
| 892 | 918 | helpString: 'Aborts the slash command batch execution.', |
| 893 | 919 | })); |
| 894 | 920 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 895 | 921 | name: 'fuzzy', |
| 896 | 922 | callback: fuzzyCallback, |
| 897 | 923 | returns: 'first matching item', |
| 898 | 924 | namedArgumentList: [ |
| 899 | 925 | new SlashCommandNamedArgument( |
| 900 | 926 | 'list', 'list of items to match against', [ARGUMENT_TYPE.LIST], true, |
| 901 | 927 | ), |
| 902 | 928 | new SlashCommandNamedArgument( |
| 903 | 929 | 'threshold', 'fuzzy match threshold (0.0 to 1.0)', [ARGUMENT_TYPE.NUMBER], false, false, '0.4', |
| 904 | 930 | ), |
| 905 | 931 | ], |
| 906 | 932 | unnamedArgumentList: [ |
| 907 | 933 | new SlashCommandArgument( |
| 908 | 934 | 'text to search', [ARGUMENT_TYPE.STRING], true, |
| 909 | 935 | ), |
| 910 | 936 | ], |
| 911 | 937 | helpString: ` |
| 912 | 938 | <div> |
| 913 | 939 | Performs a fuzzy match of each item in the <code>list</code> against the <code>text to search</code>. |
| 914 | 940 | If any item matches, then its name is returned. If no item matches the text, no value is returned. |
| @@ -930,23 +956,23 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 930 | 956 | </ul> |
| 931 | 957 | </div> |
| 932 | 958 | `, |
| 933 | 959 | })); |
| 934 | 960 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 935 | 961 | name: 'pass', |
| 936 | 962 | callback: (_, arg) => { |
| 937 | 963 | // We do not support arrays of closures. Arrays of strings will be send as JSON |
| 938 | 964 | if (Array.isArray(arg) && arg.some(x => x instanceof SlashCommandClosure)) throw new Error('Command /pass does not support multiple closures'); |
| 939 | 965 | if (Array.isArray(arg)) return JSON.stringify(arg); |
| 940 | 966 | return arg; |
| 941 | 967 | }, |
| 942 | 968 | returns: 'the provided value', |
| 943 | 969 | unnamedArgumentList: [ |
| 944 | 970 | new SlashCommandArgument( |
| 945 | 971 | 'text', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.BOOLEAN, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY, ARGUMENT_TYPE.CLOSURE], true, |
| 946 | 972 | ), |
| 947 | 973 | ], |
| 948 | 974 | aliases: ['return'], |
| 949 | 975 | helpString: ` |
| 950 | 976 | <div> |
| 951 | 977 | <pre><span class="monospace">/pass (text)</span> – passes the text to the next command through the pipe.</pre> |
| 952 | 978 | </div> |
| @@ -957,17 +983,17 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 957 | 983 | </ul> |
| 958 | 984 | </div> |
| 959 | 985 | `, |
| 960 | 986 | })); |
| 961 | 987 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 962 | 988 | name: 'delay', |
| 963 | 989 | callback: delayCallback, |
| 964 | 990 | aliases: ['wait', 'sleep'], |
| 965 | 991 | unnamedArgumentList: [ |
| 966 | 992 | new SlashCommandArgument( |
| 967 | 993 | 'milliseconds', [ARGUMENT_TYPE.NUMBER], true, |
| 968 | 994 | ), |
| 969 | 995 | ], |
| 970 | 996 | helpString: ` |
| 971 | 997 | <div> |
| 972 | 998 | Delays the next command in the pipe by the specified number of milliseconds. |
| 973 | 999 | </div> |
| @@ -980,101 +1006,101 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 980 | 1006 | </ul> |
| 981 | 1007 | </div> |
| 982 | 1008 | `, |
| 983 | 1009 | })); |
| 984 | 1010 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 985 | 1011 | name: 'input', |
| 986 | 1012 | aliases: ['prompt'], |
| 987 | 1013 | callback: inputCallback, |
| 988 | 1014 | returns: 'user input', |
| 989 | 1015 | namedArgumentList: [ |
| 990 | 1016 | new SlashCommandNamedArgument( |
| 991 | 1017 | 'default', 'default value of the input field', [ARGUMENT_TYPE.STRING], false, false, '"string"', |
| 992 | 1018 | ), |
| 993 | 1019 | new SlashCommandNamedArgument( |
| 994 | 1020 | 'large', 'show large input field', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(), |
| 995 | 1021 | ), |
| 996 | 1022 | new SlashCommandNamedArgument( |
| 997 | 1023 | 'wide', 'show wide input field', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(), |
| 998 | 1024 | ), |
| 999 | 1025 | new SlashCommandNamedArgument( |
| 1000 | 1026 | 'okButton', 'text for the ok button', [ARGUMENT_TYPE.STRING], false, |
| 1001 | 1027 | ), |
| 1002 | 1028 | new SlashCommandNamedArgument( |
| 1003 | 1029 | 'rows', 'number of rows for the input field', [ARGUMENT_TYPE.NUMBER], false, |
| 1004 | 1030 | ), |
| 1005 | 1031 | ], |
| 1006 | 1032 | unnamedArgumentList: [ |
| 1007 | 1033 | new SlashCommandArgument( |
| 1008 | 1034 | 'text to display', [ARGUMENT_TYPE.STRING], false, |
| 1009 | 1035 | ), |
| 1010 | 1036 | ], |
| 1011 | 1037 | helpString: ` |
| 1012 | 1038 | <div> |
| 1013 | 1039 | Shows a popup with the provided text and an input field. |
| 1014 | 1040 | The <code>default</code> argument is the default value of the input field, and the text argument is the text to display. |
| 1015 | 1041 | </div> |
| 1016 | 1042 | `, |
| 1017 | 1043 | })); |
| 1018 | 1044 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1019 | 1045 | name: 'run', |
| 1020 | 1046 | aliases: ['call', 'exec'], |
| 1021 | 1047 | callback: runCallback, |
| 1022 | 1048 | returns: 'result of the executed closure of QR', |
| 1023 | 1049 | namedArgumentList: [ |
| 1024 | 1050 | new SlashCommandNamedArgument( |
| 1025 | 1051 | 'args', 'named arguments', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.BOOLEAN, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY], false, true, |
| 1026 | 1052 | ), |
| 1027 | 1053 | ], |
| 1028 | 1054 | unnamedArgumentList: [ |
| 1029 | 1055 | SlashCommandArgument.fromProps({ |
| 1030 | 1056 | description: 'scoped variable or qr label', |
| 1031 | 1057 | typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING], |
| 1032 | 1058 | isRequired: true, |
| 1033 | 1059 | enumProvider: () => [ |
| 1034 | 1060 | ...commonEnumProviders.variables('scope')(), |
| 1035 | 1061 | ...(typeof window['qrEnumProviderExecutables'] === 'function') ? window['qrEnumProviderExecutables']() : [], |
| 1036 | 1062 | ], |
| 1037 | 1063 | }), |
| 1038 | 1064 | ], |
| 1039 | 1065 | helpString: ` |
| 1040 | 1066 | <div> |
| 1041 | 1067 | Runs a closure from a scoped variable, or a Quick Reply with the specified name from a currently active preset or from another preset. |
| 1042 | 1068 | Named arguments can be referenced in a QR with <code>{{arg::key}}</code>. |
| 1043 | 1069 | </div> |
| 1044 | 1070 | `, |
| 1045 | 1071 | })); |
| 1046 | 1072 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1047 | 1073 | name: 'messages', |
| 1048 | 1074 | callback: getMessagesCallback, |
| 1049 | 1075 | aliases: ['message'], |
| 1050 | 1076 | namedArgumentList: [ |
| 1051 | 1077 | new SlashCommandNamedArgument( |
| 1052 | 1078 | 'names', 'show message author names', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', commonEnumProviders.boolean('onOff')(), |
| 1053 | 1079 | ), |
| 1054 | 1080 | new SlashCommandNamedArgument( |
| 1055 | 1081 | 'hidden', 'include hidden messages', [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', commonEnumProviders.boolean('onOff')(), |
| 1056 | 1082 | ), |
| 1057 | 1083 | SlashCommandNamedArgument.fromProps({ |
| 1058 | 1084 | name: 'role', |
| 1059 | 1085 | description: 'filter messages by role', |
| 1060 | 1086 | typeList: [ARGUMENT_TYPE.STRING], |
| 1061 | 1087 | enumList: [ |
| 1062 | 1088 | new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.system), |
| 1063 | 1089 | new SlashCommandEnumValue('assistant', null, enumTypes.enum, enumIcons.assistant), |
| 1064 | 1090 | new SlashCommandEnumValue('user', null, enumTypes.enum, enumIcons.user), |
| 1065 | 1091 | ], |
| 1066 | 1092 | }), |
| 1067 | 1093 | ], |
| 1068 | 1094 | unnamedArgumentList: [ |
| 1069 | 1095 | SlashCommandArgument.fromProps({ |
| 1070 | 1096 | description: 'message index (starts with 0) or range', |
| 1071 | 1097 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], |
| 1072 | 1098 | isRequired: true, |
| 1073 | 1099 | enumProvider: commonEnumProviders.messages(), |
| 1074 | 1100 | }), |
| 1075 | 1101 | ], |
| 1076 | 1102 | returns: 'the specified message or range of messages as a string', |
| 1077 | 1103 | helpString: ` |
| 1078 | 1104 | <div> |
| 1079 | 1105 | Returns the specified message or range of messages as a string. |
| 1080 | 1106 | </div> |
| @@ -1098,16 +1124,16 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 1098 | 1124 | </ul> |
| 1099 | 1125 | </div> |
| 1100 | 1126 | `, |
| 1101 | 1127 | })); |
| 1102 | 1128 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1103 | 1129 | name: 'setinput', |
| 1104 | 1130 | callback: setInputCallback, |
| 1105 | 1131 | unnamedArgumentList: [ |
| 1106 | 1132 | new SlashCommandArgument( |
| 1107 | 1133 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 1108 | 1134 | ), |
| 1109 | 1135 | ], |
| 1110 | 1136 | helpString: ` |
| 1111 | 1137 | <div> |
| 1112 | 1138 | Sets the user input to the specified text and passes it to the next command through the pipe. |
| 1113 | 1139 | </div> |
| @@ -1120,28 +1146,28 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 1120 | 1146 | </ul> |
| 1121 | 1147 | </div> |
| 1122 | 1148 | `, |
| 1123 | 1149 | })); |
| 1124 | 1150 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1125 | 1151 | name: 'popup', |
| 1126 | 1152 | callback: popupCallback, |
| 1127 | 1153 | returns: 'popup text', |
| 1128 | 1154 | namedArgumentList: [ |
| 1129 | 1155 | new SlashCommandNamedArgument( |
| 1130 | 1156 | 'large', 'show large popup', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(), |
| 1131 | 1157 | ), |
| 1132 | 1158 | new SlashCommandNamedArgument( |
| 1133 | 1159 | 'wide', 'show wide popup', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(), |
| 1134 | 1160 | ), |
| 1135 | 1161 | new SlashCommandNamedArgument( |
| 1136 | 1162 | 'okButton', 'text for the OK button', [ARGUMENT_TYPE.STRING], false, |
| 1137 | 1163 | ), |
| 1138 | 1164 | ], |
| 1139 | 1165 | unnamedArgumentList: [ |
| 1140 | 1166 | new SlashCommandArgument( |
| 1141 | 1167 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 1142 | 1168 | ), |
| 1143 | 1169 | ], |
| 1144 | 1170 | helpString: ` |
| 1145 | 1171 | <div> |
| 1146 | 1172 | Shows a blocking popup with the specified text and buttons. |
| 1147 | 1173 | Returns the popup text. |
| @@ -1155,22 +1181,22 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 1155 | 1181 | </ul> |
| 1156 | 1182 | </div> |
| 1157 | 1183 | `, |
| 1158 | 1184 | })); |
| 1159 | 1185 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1160 | 1186 | name: 'buttons', |
| 1161 | 1187 | callback: buttonsCallback, |
| 1162 | 1188 | returns: 'clicked button label', |
| 1163 | 1189 | namedArgumentList: [ |
| 1164 | 1190 | new SlashCommandNamedArgument( |
| 1165 | 1191 | 'labels', 'button labels', [ARGUMENT_TYPE.LIST], true, |
| 1166 | 1192 | ), |
| 1167 | 1193 | ], |
| 1168 | 1194 | unnamedArgumentList: [ |
| 1169 | 1195 | new SlashCommandArgument( |
| 1170 | 1196 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 1171 | 1197 | ), |
| 1172 | 1198 | ], |
| 1173 | 1199 | helpString: ` |
| 1174 | 1200 | <div> |
| 1175 | 1201 | Shows a blocking popup with the specified text and buttons. |
| 1176 | 1202 | Returns the clicked button label into the pipe or empty string if canceled. |
| @@ -1184,32 +1210,32 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 1184 | 1210 | </ul> |
| 1185 | 1211 | </div> |
| 1186 | 1212 | `, |
| 1187 | 1213 | })); |
| 1188 | 1214 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1189 | 1215 | name: 'trimtokens', |
| 1190 | 1216 | callback: trimTokensCallback, |
| 1191 | 1217 | returns: 'trimmed text', |
| 1192 | 1218 | namedArgumentList: [ |
| 1193 | 1219 | new SlashCommandNamedArgument( |
| 1194 | 1220 | 'limit', 'number of tokens to keep', [ARGUMENT_TYPE.NUMBER], true, |
| 1195 | 1221 | ), |
| 1196 | 1222 | SlashCommandNamedArgument.fromProps({ |
| 1197 | 1223 | name: 'direction', |
| 1198 | 1224 | description: 'trim direction', |
| 1199 | 1225 | typeList: [ARGUMENT_TYPE.STRING], |
| 1200 | 1226 | isRequired: true, |
| 1201 | 1227 | enumList: [ |
| 1202 | 1228 | new SlashCommandEnumValue('start', null, enumTypes.enum, '⏪'), |
| 1203 | 1229 | new SlashCommandEnumValue('end', null, enumTypes.enum, '⏩'), |
| 1204 | 1230 | ], |
| 1205 | 1231 | }), |
| 1206 | 1232 | ], |
| 1207 | 1233 | unnamedArgumentList: [ |
| 1208 | 1234 | new SlashCommandArgument( |
| 1209 | 1235 | 'text', [ARGUMENT_TYPE.STRING], false, |
| 1210 | 1236 | ), |
| 1211 | 1237 | ], |
| 1212 | 1238 | helpString: ` |
| 1213 | 1239 | <div> |
| 1214 | 1240 | Trims the start or end of text to the specified number of tokens. |
| 1215 | 1241 | </div> |
| @@ -1222,17 +1248,17 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 1222 | 1248 | </ul> |
| 1223 | 1249 | </div> |
| 1224 | 1250 | `, |
| 1225 | 1251 | })); |
| 1226 | 1252 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1227 | 1253 | name: 'trimstart', |
| 1228 | 1254 | callback: trimStartCallback, |
| 1229 | 1255 | returns: 'trimmed text', |
| 1230 | 1256 | unnamedArgumentList: [ |
| 1231 | 1257 | new SlashCommandArgument( |
| 1232 | 1258 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 1233 | 1259 | ), |
| 1234 | 1260 | ], |
| 1235 | 1261 | helpString: ` |
| 1236 | 1262 | <div> |
| 1237 | 1263 | Trims the text to the start of the first full sentence. |
| 1238 | 1264 | </div> |
| @@ -1245,148 +1271,149 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 1245 | 1271 | </ul> |
| 1246 | 1272 | </div> |
| 1247 | 1273 | `, |
| 1248 | 1274 | })); |
| 1249 | 1275 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1250 | 1276 | name: 'trimend', |
| 1251 | 1277 | callback: trimEndCallback, |
| 1252 | 1278 | returns: 'trimmed text', |
| 1253 | 1279 | unnamedArgumentList: [ |
| 1254 | 1280 | new SlashCommandArgument( |
| 1255 | 1281 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 1256 | 1282 | ), |
| 1257 | 1283 | ], |
| 1258 | 1284 | helpString: 'Trims the text to the end of the last full sentence.', |
| 1259 | 1285 | })); |
| 1260 | 1286 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1261 | 1287 | name: 'inject', |
| 1262 | 1288 | callback: injectCallback, |
| 1263 | 1289 | namedArgumentList: [ |
| 1264 | 1290 | SlashCommandNamedArgument.fromProps({ |
| 1265 | 1291 | name: 'id', |
| 1266 | 1292 | description: 'injection ID or variable name pointing to ID', |
| 1267 | 1293 | typeList: [ARGUMENT_TYPE.STRING], |
| 1268 | 1294 | isRequired: true, |
| 1269 | 1295 | enumProvider: commonEnumProviders.injects, |
| 1270 | 1296 | }), |
| 1271 | 1297 | new SlashCommandNamedArgument( |
| 1272 | 1298 | 'position', 'injection position', [ARGUMENT_TYPE.STRING], false, false, 'after', ['before', 'after', 'chat'], |
| 1273 | 1299 | ), |
| 1274 | 1300 | new SlashCommandNamedArgument( |
| 1275 | 1301 | 'depth', 'injection depth', [ARGUMENT_TYPE.NUMBER], false, false, '4', |
| 1276 | 1302 | ), |
| 1277 | 1303 | new SlashCommandNamedArgument( |
| 1278 | 1304 | 'scan', 'include injection content into World Info scans', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', |
| 1279 | 1305 | ), |
| 1280 | 1306 | SlashCommandNamedArgument.fromProps({ |
| 1281 | 1307 | name: 'role', |
| 1282 | 1308 | description: 'role for in-chat injections', |
| 1283 | 1309 | typeList: [ARGUMENT_TYPE.STRING], |
| 1284 | 1310 | isRequired: false, |
| 1285 | 1311 | enumList: [ |
| 1286 | 1312 | new SlashCommandEnumValue('system', null, enumTypes.enum, enumIcons.system), |
| 1287 | 1313 | new SlashCommandEnumValue('assistant', null, enumTypes.enum, enumIcons.assistant), |
| 1288 | 1314 | new SlashCommandEnumValue('user', null, enumTypes.enum, enumIcons.user), |
| 1289 | 1315 | ], |
| 1290 | 1316 | }), |
| 1291 | 1317 | new SlashCommandNamedArgument( |
| 1292 | 1318 | 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', |
| 1293 | 1319 | ), |
| 1294 | 1320 | ], |
| 1295 | 1321 | unnamedArgumentList: [ |
| 1296 | 1322 | new SlashCommandArgument( |
| 1297 | 1323 | 'text', [ARGUMENT_TYPE.STRING], false, |
| 1298 | 1324 | ), |
| 1299 | 1325 | ], |
| 1300 | 1326 | helpString: 'Injects a text into the LLM prompt for the current chat. Requires a unique injection ID. Positions: "before" main prompt, "after" main prompt, in-"chat" (default: after). Depth: injection depth for the prompt (default: 4). Role: role for in-chat injections (default: system). Scan: include injection content into World Info scans (default: false).', |
| 1301 | 1327 | })); |
| 1302 | 1328 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1303 | 1329 | name: 'listinjects', |
| 1304 | 1330 | callback: listInjectsCallback, |
| 1305 | 1331 | helpString: 'Lists all script injections for the current chat.', |
| 1306 | 1332 | })); |
| 1307 | 1333 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1308 | 1334 | name: 'flushinject', |
| 1309 | 1335 | aliases: ['flushinjects'], |
| 1310 | 1336 | unnamedArgumentList: [ |
| 1311 | 1337 | SlashCommandArgument.fromProps({ |
| 1312 | 1338 | description: 'injection ID or a variable name pointing to ID', |
| 1313 | 1339 | typeList: [ARGUMENT_TYPE.STRING], |
| 1314 | 1340 | defaultValue: '', |
| 1315 | 1341 | enumProvider: commonEnumProviders.injects, |
| 1316 | 1342 | }), |
| 1317 | 1343 | ], |
| 1318 | 1344 | callback: flushInjectsCallback, |
| 1319 | 1345 | helpString: 'Removes a script injection for the current chat. If no ID is provided, removes all script injections.', |
| 1320 | 1346 | })); |
| 1321 | 1347 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1322 | 1348 | name: 'tokens', |
| 1323 | 1349 | callback: (_, text) => { |
| 1324 | 1350 | if (text instanceof SlashCommandClosure || Array.isArray(text)) throw new Error('Unnamed argument cannot be a closure for command /tokens'); |
| 1325 | 1351 | return getTokenCountAsync(text).then(count => String(count)); |
| 1326 | 1352 | }, |
| 1327 | 1353 | returns: 'number of tokens', |
| 1328 | 1354 | unnamedArgumentList: [ |
| 1329 | 1355 | new SlashCommandArgument( |
| 1330 | 1356 | 'text', [ARGUMENT_TYPE.STRING], true, |
| 1331 | 1357 | ), |
| 1332 | 1358 | ], |
| 1333 | 1359 | helpString: 'Counts the number of tokens in the provided text.', |
| 1334 | 1360 | })); |
| 1335 | 1361 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1336 | 1362 | name: 'model', |
| 1337 | 1363 | callback: modelCallback, |
| 1338 | 1364 | returns: 'current model', |
| 1339 | 1365 | unnamedArgumentList: [ |
| 1340 | 1366 | SlashCommandArgument.fromProps({ |
| 1341 | 1367 | description: 'model name', |
| 1342 | 1368 | typeList: [ARGUMENT_TYPE.STRING], |
| 1343 | 1369 | enumProvider: () => getModelOptions()?.options.map(option => new SlashCommandEnumValue(option.value, option.value !== option.text ? option.text : null)), |
| 1344 | 1370 | }), |
| 1345 | 1371 | ], |
| 1346 | 1372 | helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.', |
| 1347 | 1373 | })); |
| 1348 | 1374 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1349 | 1375 | name: 'setpromptentry', |
| 1350 | 1376 | aliases: ['setpromptentries'], |
| 1351 | 1377 | callback: setPromptEntryCallback, |
| 1352 | 1378 | namedArgumentList: [ |
| 1353 | 1379 | SlashCommandNamedArgument.fromProps({ |
| 1354 | 1380 | name: 'identifier', |
| 1355 | 1381 | description: 'Prompt entry identifier(s) to target', |
| 1356 | 1382 | typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], |
| 1357 | 1383 | acceptsMultiple: true, |
| 1358 | 1384 | enumProvider: () => { |
| 1359 | 1385 | const promptManager = setupChatCompletionPromptManager(oai_settings); |
| 1360 | 1386 | const prompts = promptManager.serviceSettings.prompts; |
| 1361 | 1387 | return prompts.map(prompt => new SlashCommandEnumValue(prompt.identifier, prompt.name, enumTypes.enum)); |
| 1362 | 1388 | }, |
| 1363 | 1389 | }), |
| 1364 | 1390 | SlashCommandNamedArgument.fromProps({ |
| 1365 | 1391 | name: 'name', |
| 1366 | 1392 | description: 'Prompt entry name(s) to target', |
| 1367 | 1393 | typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], |
| 1368 | 1394 | acceptsMultiple: true, |
| 1369 | 1395 | enumProvider: () => { |
| 1370 | 1396 | const promptManager = setupChatCompletionPromptManager(oai_settings); |
| 1371 | 1397 | const prompts = promptManager.serviceSettings.prompts; |
| 1372 | 1398 | return prompts.map(prompt => new SlashCommandEnumValue(prompt.name, prompt.identifier, enumTypes.enum)); |
| 1373 | 1399 | }, |
| 1374 | 1400 | }), |
| 1375 | 1401 | ], |
| 1376 | 1402 | unnamedArgumentList: [ |
| 1377 | 1403 | SlashCommandArgument.fromProps({ |
| 1378 | 1404 | description: 'Set entry/entries on or off', |
| 1379 | 1405 | typeList: [ARGUMENT_TYPE.STRING], |
| 1380 | 1406 | isRequired: true, |
| 1381 | 1407 | acceptsMultiple: false, |
| 1382 | 1408 | defaultValue: 'toggle', // unnamed arguments don't support default values yet |
| 1383 | 1409 | enumList: commonEnumProviders.boolean('onOffToggle')(), |
| 1384 | 1410 | }), |
| 1385 | 1411 | ], |
| 1386 | 1412 | helpString: 'Sets the specified prompt manager entry/entries on or off.', |
| 1387 | 1413 | })); |
| 1388 | 1414 | |
| 1389 | 1415 | registerVariableCommands(); |
| 1416 | +} | |
| 1390 | 1417 | |
| 1391 | 1418 | const NARRATOR_NAME_KEY = 'narrator_name'; |
| 1392 | 1419 | const NARRATOR_NAME_DEFAULT = 'System'; |
| @@ -1938,32 +1965,67 @@ async function generateCallback(args, value) { | ||
| 1938 | 1965 | } |
| 1939 | 1966 | } |
| 1940 | 1967 | |
| 1968 | +/** | |
| 1969 | + * | |
| 1970 | + * @param {{title?: string, severity?: string, timeout?: string, extendedTimeout?: string, preventDuplicates?: string, awaitDismissal?: string}} args - named arguments from the slash command | |
| 1971 | + * @param {string} value - The string to echo (unnamed argument from the slash command) | |
| 1972 | + * @returns {Promise<string>} The text that was echoed | |
| 1973 | + */ | |
| 1941 | 1974 | async function echoCallback(args, value) { |
| 1942 | 1975 | // Note: We don't need to sanitize input, as toastr is set up by default to escape HTML via toastr options |
| 1943 | 1976 | if (value === '') { |
| 1944 | 1977 | console.warn('WARN: No argument provided for /echo command'); |
| 1945 | 1978 | return ''; |
| 1946 | 1979 | } |
| 1947 | - const title = args?.title !== undefined && typeof args?.title === 'string' ? args.title : undefined; | |
| 1980 | + | |
| 1948 | - const severity = args?.severity !== undefined && typeof args?.severity === 'string' ? args.severity : 'info'; | |
| 1981 | + if (args.severity && !['error', 'warning', 'success', 'info'].includes(args.severity)) { | |
| 1982 | + toastr.warning(`Invalid severity provided for /echo command: ${args.severity}`); | |
| 1983 | + args.severity = null; | |
| 1984 | + } | |
| 1985 | + | |
| 1986 | + const title = args.title ? args.title : undefined; | |
| 1987 | + const severity = args.severity ? args.severity : 'info'; | |
| 1988 | + | |
| 1989 | + /** @type {ToastrOptions} */ | |
| 1990 | + const options = {}; | |
| 1991 | + if (args.timeout && !isNaN(parseInt(args.timeout))) options.timeOut = parseInt(args.timeout); | |
| 1992 | + if (args.extendedTimeout && !isNaN(parseInt(args.extendedTimeout))) options.extendedTimeOut = parseInt(args.extendedTimeout); | |
| 1993 | + if (isTrueBoolean(args.preventDuplicates)) options.preventDuplicates = true; | |
| 1994 | + | |
| 1995 | + // Prepare possible await handling | |
| 1996 | + let awaitDismissal = isTrueBoolean(args.awaitDismissal); | |
| 1997 | + let resolveToastDismissal; | |
| 1998 | + | |
| 1999 | + if (awaitDismissal) { | |
| 2000 | + options.onHidden = () => resolveToastDismissal(value); | |
| 2001 | + } | |
| 2002 | + | |
| 1949 | 2003 | switch (severity) { |
| 1950 | 2004 | case 'error': |
| 1951 | 2005 | toastr.error(value, title, options); |
| 1952 | 2006 | break; |
| 1953 | 2007 | case 'warning': |
| 1954 | 2008 | toastr.warning(value, title, options); |
| 1955 | 2009 | break; |
| 1956 | 2010 | case 'success': |
| 1957 | 2011 | toastr.success(value, title, options); |
| 1958 | 2012 | break; |
| 1959 | 2013 | case 'info': |
| 1960 | 2014 | default: |
| 1961 | 2015 | toastr.info(value, title, options); |
| 1962 | 2016 | break; |
| 1963 | 2017 | } |
| 1964 | - return value; | |
| 2018 | + | |
| 2019 | + if (awaitDismissal) { | |
| 2020 | + return new Promise((resolve) => { | |
| 2021 | + resolveToastDismissal = resolve; | |
| 2022 | + }); | |
| 2023 | + } else { | |
| 2024 | + return value; | |
| 2025 | + } | |
| 1965 | 2026 | } |
| 1966 | 2027 | |
| 2028 | + | |
| 1967 | 2029 | async function addSwipeCallback(_, arg) { |
| 1968 | 2030 | const lastMessage = chat[chat.length - 1]; |
| 1969 | 2031 | |
| @@ -2428,7 +2490,14 @@ async function sendUserMessageCallback(args, text) { | ||
| 2428 | 2490 | text = text.trim(); |
| 2429 | 2491 | const compact = isTrueBoolean(args?.compact); |
| 2430 | 2492 | const bias = extractMessageBias(text); |
| 2431 | - const insertAt = Number(args?.at); | |
| 2493 | + | |
| 2494 | + let insertAt = Number(args?.at); | |
| 2495 | + | |
| 2496 | + // Convert possible depth parameter to index | |
| 2497 | + if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { | |
| 2498 | + // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) | |
| 2499 | + insertAt = chat.length + insertAt; | |
| 2500 | + } | |
| 2432 | 2501 | |
| 2433 | 2502 | if ('name' in args) { |
| 2434 | 2503 | const name = args.name || ''; |
| @@ -2737,7 +2806,13 @@ export async function sendMessageAs(args, text) { | ||
| 2737 | 2806 | }, |
| 2738 | 2807 | }]; |
| 2739 | 2808 | |
| 2740 | 2809 | constlet insertAt = Number(args.at); |
| 2810 | + | |
| 2811 | + // Convert possible depth parameter to index | |
| 2812 | + if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { | |
| 2813 | + // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) | |
| 2814 | + insertAt = chat.length + insertAt; | |
| 2815 | + } | |
| 2741 | 2816 | |
| 2742 | 2817 | if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { |
| 2743 | 2818 | chat.splice(insertAt, 0, message); |
| @@ -2784,7 +2859,13 @@ export async function sendNarratorMessage(args, text) { | ||
| 2784 | 2859 | }, |
| 2785 | 2860 | }; |
| 2786 | 2861 | |
| 2787 | 2862 | constlet insertAt = Number(args.at); |
| 2863 | + | |
| 2864 | + // Convert possible depth parameter to index | |
| 2865 | + if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { | |
| 2866 | + // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) | |
| 2867 | + insertAt = chat.length + insertAt; | |
| 2868 | + } | |
| 2788 | 2869 | |
| 2789 | 2870 | if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { |
| 2790 | 2871 | chat.splice(insertAt, 0, message); |
| @@ -2866,7 +2947,13 @@ async function sendCommentMessage(args, text) { | ||
| 2866 | 2947 | }, |
| 2867 | 2948 | }; |
| 2868 | 2949 | |
| 2869 | 2950 | constlet insertAt = Number(args.at); |
| 2951 | + | |
| 2952 | + // Convert possible depth parameter to index | |
| 2953 | + if (!isNaN(insertAt) && (insertAt < 0 || insertAt === Number(-0))) { | |
| 2954 | + // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7) | |
| 2955 | + insertAt = chat.length + insertAt; | |
| 2956 | + } | |
| 2870 | 2957 | |
| 2871 | 2958 | if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { |
| 2872 | 2959 | chat.splice(insertAt, 0, message); |
| @@ -59,7 +59,7 @@ export class SlashCommandArgument { | ||
| 59 | 59 | * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} enums |
| 60 | 60 | * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} enumProvider function that returns auto complete options |
| 61 | 61 | */ |
| 62 | 62 | constructor(description, types, isRequired = false, acceptsMultiple = false, defaultValue = null, enums = [], enumProvider = null, forceEnum = truefalse) { |
| 63 | 63 | this.description = description; |
| 64 | 64 | this.typeList = types ? Array.isArray(types) ? types : [types] : []; |
| 65 | 65 | this.isRequired = isRequired ?? false; |
| @@ -90,7 +90,7 @@ export class SlashCommandNamedArgument extends SlashCommandArgument { | ||
| 90 | 90 | * @param {string|SlashCommandClosure} [props.defaultValue=null] default value if no value is provided |
| 91 | 91 | * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} [props.enumList=[]] list of accepted values |
| 92 | 92 | * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} [props.enumProvider=null] function that returns auto complete options |
| 93 | 93 | * @param {boolean} [props.forceEnum=truefalse] default: truefalse - whether the input must match one of the enum values |
| 94 | 94 | */ |
| 95 | 95 | static fromProps(props) { |
| 96 | 96 | return new SlashCommandNamedArgument( |
| @@ -103,7 +103,7 @@ export class SlashCommandNamedArgument extends SlashCommandArgument { | ||
| 103 | 103 | props.enumList ?? [], |
| 104 | 104 | props.aliasList ?? [], |
| 105 | 105 | props.enumProvider ?? null, |
| 106 | 106 | props.forceEnum ?? truefalse, |
| 107 | 107 | ); |
| 108 | 108 | } |
| 109 | 109 | |
| @@ -120,9 +120,9 @@ export class SlashCommandNamedArgument extends SlashCommandArgument { | ||
| 120 | 120 | * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} [enums=[]] |
| 121 | 121 | * @param {string[]} [aliases=[]] |
| 122 | 122 | * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} [enumProvider=null] function that returns auto complete options |
| 123 | 123 | * @param {boolean} [forceEnum=truefalse] |
| 124 | 124 | */ |
| 125 | 125 | constructor(name, description, types, isRequired = false, acceptsMultiple = false, defaultValue = null, enums = [], aliases = [], enumProvider = null, forceEnum = truefalse) { |
| 126 | 126 | super(description, types, isRequired, acceptsMultiple, defaultValue, enums, enumProvider, forceEnum); |
| 127 | 127 | this.name = name; |
| 128 | 128 | this.aliasList = aliases ? Array.isArray(aliases) ? aliases : [aliases] : []; |
| @@ -43,7 +43,11 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 43 | 43 | key = readSecret(request.user.directories, SECRET_KEYS.KOBOLDCPP); |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | - if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp'].includes(request.body.api) === false) { | |
| 46 | + if (request.body.api === 'vllm') { | |
| 47 | + key = readSecret(request.user.directories, SECRET_KEYS.VLLM); | |
| 48 | + } | |
| 49 | + | |
| 50 | + if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) { | |
| 47 | 51 | console.log('No key found for API', request.body.api); |
| 48 | 52 | return response.sendStatus(400); |
| 49 | 53 | } |
| @@ -110,7 +114,7 @@ router.post('/caption-image', jsonParser, async (request, response) => { | ||
| 110 | 114 | }); |
| 111 | 115 | } |
| 112 | 116 | |
| 113 | 117 | if (request.body.api === 'koboldcpp' || request.body.api === 'vllm') { |
| 114 | 118 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; |
| 115 | 119 | } |
| 116 | 120 | |