Merge branch 'staging' into staging-3071-v2
| @@ -472,6 +472,11 @@ label[for="trim_spaces"]:has(input:checked) i.warning { | ||
| 472 | 472 | display: none; |
| 473 | 473 | } |
| 474 | 474 | |
| 475 | +label[for="trim_spaces"]:not(:has(input:checked)) small { | |
| 476 | + color: var(--warning); | |
| 477 | + opacity: 1; | |
| 478 | +} | |
| 479 | + | |
| 475 | 480 | #claude_function_prefill_warning { |
| 476 | 481 | display: none; |
| 477 | 482 | color: red; |
| @@ -1587,6 +1587,10 @@ | ||
| 1587 | 1587 | <input type="checkbox" id="skip_special_tokens_textgenerationwebui" /> |
| 1588 | 1588 | <small data-i18n="Skip Special Tokens">Skip Special Tokens</small> |
| 1589 | 1589 | </label> |
| 1590 | + <label data-tg-type="openrouter" class="checkbox_label flexGrow flexShrink" for="include_reasoning_textgenerationwebui"> | |
| 1591 | + <input type="checkbox" id="include_reasoning_textgenerationwebui" /> | |
| 1592 | + <small data-i18n="Request Model Reasoning">Request Model Reasoning</small> | |
| 1593 | + </label> | |
| 1590 | 1594 | <label data-tg-type="ooba, aphrodite, tabby" class="checkbox_label flexGrow flexShrink" for="temperature_last_textgenerationwebui"> |
| 1591 | 1595 | <input type="checkbox" id="temperature_last_textgenerationwebui" /> |
| 1592 | 1596 | <label> |
| @@ -1981,13 +1985,13 @@ | ||
| 1981 | 1985 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> |
| 1982 | 1986 | <input id="openai_show_thoughts" type="checkbox" /> |
| 1983 | 1987 | <span> |
| 1984 | 1988 | <span data-i18n="ShowRequest model reasoning">ShowRequest model reasoning</span> |
| 1985 | 1989 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i> |
| 1986 | 1990 | </span> |
| 1987 | 1991 | </label> |
| 1988 | 1992 | <div class="toggle-description justifyLeft marginBot5"> |
| 1989 | 1993 | <span data-i18n="DisplayAllows the model's internalto thoughtsreturn inits thethinking responseprocess."> |
| 1990 | 1994 | DisplayAllows the model's internalto thoughtsreturn inits thethinking responseprocess. |
| 1991 | 1995 | </span> |
| 1992 | 1996 | </div> |
| 1993 | 1997 | </div> |
| @@ -3151,34 +3155,22 @@ | ||
| 3151 | 3155 | </div> |
| 3152 | 3156 | <h4 data-i18n="Groq Model">Groq Model</h4> |
| 3153 | 3157 | <select id="model_groq_select"> |
| 3154 | 3158 | <optgroup label="LlamaProduction 3.3Models"> |
| 3159 | + <option value="gemma2-9b-it">gemma2-9b-it</option> | |
| 3155 | 3160 | <option value="llama-3.3-70b-versatile">llama-3.3-70b-versatile</option> |
| 3161 | + <option value="llama-3.1-8b-instant">llama-3.1-8b-instant</option> | |
| 3162 | + <option value="llama3-70b-8192">llama3-70b-8192</option> | |
| 3163 | + <option value="llama3-8b-8192">llama3-8b-8192</option> | |
| 3164 | + <option value="mixtral-8x7b-32768">mixtral-8x7b-32768</option> | |
| 3156 | 3165 | </optgroup> |
| 3157 | 3166 | <optgroup label="LlamaPreview 3.2Models"> |
| 3167 | + <option value="deepseek-r1-distill-llama-70b">deepseek-r1-distill-llama-70b</option> | |
| 3168 | + <option value="llama-3.3-70b-specdec">llama-3.3-70b-specdec</option> | |
| 3158 | 3169 | <option value="llama-3.2-1b-preview">llama-3.2-1b-preview</option> |
| 3159 | 3170 | <option value="llama-3.2-3b-preview">llama-3.2-3b-preview</option> |
| 3160 | 3171 | <option value="llama-3.2-11b-vision-preview">llama-3.2-11b-vision-preview</option> |
| 3161 | 3172 | <option value="llama-3.2-90b-vision-preview">llama-3.2-90b-vision-preview</option> |
| 3162 | 3173 | </optgroup> |
| 3163 | - <optgroup label="Llama 3.1"> | |
| 3164 | - <option value="llama-3.1-8b-instant">llama-3.1-8b-instant</option> | |
| 3165 | - <option value="llama-3.1-70b-versatile">llama-3.1-70b-versatile</option> | |
| 3166 | - <option value="llama-3.1-405b-reasoning">llama-3.1-405b-reasoning</option> | |
| 3167 | - </optgroup> | |
| 3168 | - <optgroup label="Llama 3"> | |
| 3169 | - <option value="llama3-groq-8b-8192-tool-use-preview">llama3-groq-8b-8192-tool-use-preview</option> | |
| 3170 | - <option value="llama3-groq-70b-8192-tool-use-preview">llama3-groq-70b-8192-tool-use-preview</option> | |
| 3171 | - <option value="llama3-8b-8192">llama3-8b-8192</option> | |
| 3172 | - <option value="llama3-70b-8192">llama3-70b-8192</option> | |
| 3173 | - </optgroup> | |
| 3174 | - <optgroup label="Gemma"> | |
| 3175 | - <option value="gemma-7b-it">gemma-7b-it</option> | |
| 3176 | - <option value="gemma2-9b-it">gemma2-9b-it</option> | |
| 3177 | - </optgroup> | |
| 3178 | - <optgroup label="Other"> | |
| 3179 | - <option value="mixtral-8x7b-32768">mixtral-8x7b-32768</option> | |
| 3180 | - <option value="llava-v1.5-7b-4096-preview">llava-v1.5-7b-4096-preview</option> | |
| 3181 | - </optgroup> | |
| 3182 | 3174 | </select> |
| 3183 | 3175 | </div> |
| 3184 | 3176 | <div id="nanogpt_form" data-source="nanogpt"> |
| @@ -3227,32 +3219,19 @@ | ||
| 3227 | 3219 | <h4 data-i18n="Perplexity Model">Perplexity Model</h4> |
| 3228 | 3220 | <select id="model_perplexity_select"> |
| 3229 | 3221 | <optgroup label="Perplexity Sonar Models"> |
| 3222 | + <option value="sonar">sonar</option> | |
| 3223 | + <option value="sonar-pro">sonar-pro</option> | |
| 3224 | + <option value="sonar-reasoning">sonar-reasoning</option> | |
| 3225 | + </optgroup> | |
| 3226 | + <optgroup label="Deprecated Models"> | |
| 3227 | + <!-- These are scheduled for deprecation after 2/22/2025 --> | |
| 3230 | 3228 | <option value="llama-3.1-sonar-small-128k-online">llama-3.1-sonar-small-128k-online</option> |
| 3231 | 3229 | <option value="llama-3.1-sonar-large-128k-online">llama-3.1-sonar-large-128k-online</option> |
| 3232 | 3230 | <option value="llama-3.1-sonar-huge-128k-online">llama-3.1-sonar-huge-128k-online</option> |
| 3233 | - </optgroup> | |
| 3231 | + <!-- These are not listed on the site anymore --> | |
| 3234 | - <optgroup label="Perplexity Chat Models"> | |
| 3235 | 3232 | <option value="llama-3.1-sonar-small-128k-chat">llama-3.1-sonar-small-128k-chat</option> |
| 3236 | 3233 | <option value="llama-3.1-sonar-large-128k-chat">llama-3.1-sonar-large-128k-chat</option> |
| 3237 | 3234 | </optgroup> |
| 3238 | - <optgroup label="Open-Source Models"> | |
| 3239 | - <option value="llama-3.1-8b-instruct">llama-3.1-8b-instruct</option> | |
| 3240 | - <option value="llama-3.1-70b-instruct">llama-3.1-70b-instruct</option> | |
| 3241 | - </optgroup> | |
| 3242 | - <optgroup label="Deprecated Models"> | |
| 3243 | - <option value="llama-3-sonar-small-32k-chat">llama-3-sonar-small-32k-chat</option> | |
| 3244 | - <option value="llama-3-sonar-small-32k-online">llama-3-sonar-small-32k-online</option> | |
| 3245 | - <option value="llama-3-sonar-large-32k-chat">llama-3-sonar-large-32k-chat</option> | |
| 3246 | - <option value="llama-3-sonar-large-32k-online">llama-3-sonar-large-32k-online</option> | |
| 3247 | - <option value="sonar-small-chat">sonar-small-chat</option> | |
| 3248 | - <option value="sonar-small-online">sonar-small-online</option> | |
| 3249 | - <option value="sonar-medium-chat">sonar-medium-chat</option> | |
| 3250 | - <option value="sonar-medium-online">sonar-medium-online</option> | |
| 3251 | - <option value="llama-3-8b-instruct">llama-3-8b-instruct</option> | |
| 3252 | - <option value="llama-3-70b-instruct">llama-3-70b-instruct</option> | |
| 3253 | - <option value="mistral-7b-instruct">mistral-7b-instruct (v0.2)</option> | |
| 3254 | - <option value="mixtral-8x7b-instruct">mixtral-8x7b-instruct</option> | |
| 3255 | - </optgroup> | |
| 3256 | 3235 | </select> |
| 3257 | 3236 | </div> |
| 3258 | 3237 | <form id="cohere_form" data-source="cohere" action="javascript:void(null);" method="post" enctype="multipart/form-data"> |
| @@ -3804,6 +3783,12 @@ | ||
| 3804 | 3783 | <span data-i18n="Reasoning">Reasoning</span> |
| 3805 | 3784 | </h4> |
| 3806 | 3785 | <div> |
| 3786 | + <label class="checkbox_label" for="reasoning_auto_parse" title="Automatically parse reasoning blocks from main content between the reasoning prefix/suffix. Both fields must be defined and non-empty." data-i18n="[title]reasoning_auto_parse"> | |
| 3787 | + <input id="reasoning_auto_parse" type="checkbox" /> | |
| 3788 | + <small data-i18n="Auto-Parse Reasoning"> | |
| 3789 | + Auto-Parse Reasoning | |
| 3790 | + </small> | |
| 3791 | + </label> | |
| 3807 | 3792 | <label class="checkbox_label" for="reasoning_add_to_prompts" title="Add existing reasoning blocks to prompts. To add a new reasoning block, use the message edit menu." data-i18n="[title]reasoning_add_to_prompts"> |
| 3808 | 3793 | <input id="reasoning_add_to_prompts" type="checkbox" /> |
| 3809 | 3794 | <small data-i18n="Add Reasoning to Prompts"> |
| @@ -4845,6 +4830,7 @@ | ||
| 4845 | 4830 | </div> |
| 4846 | 4831 | <div id="extensions_settings" class="flex1 wide50p"> |
| 4847 | 4832 | <div id="assets_container" class="extension_container"></div> |
| 4833 | + <div id="typing_indicator_container" class="extension_container"></div> | |
| 4848 | 4834 | <div id="expressions_container" class="extension_container"></div> |
| 4849 | 4835 | <div id="sd_container" class="extension_container"></div> |
| 4850 | 4836 | <div id="tts_container" class="extension_container"></div> |
| @@ -6488,9 +6474,6 @@ | ||
| 6488 | 6474 | </div> |
| 6489 | 6475 | |
| 6490 | 6476 | <!-- chat and input bar --> |
| 6491 | - <div id="typing_indicator_template" class="template_element"> | |
| 6492 | - <div class="typing_indicator"><span class="typing_indicator_name">CHAR</span> is typing</div> | |
| 6493 | - </div> | |
| 6494 | 6477 | <div id="message_file_template" class="template_element"> |
| 6495 | 6478 | <div class="mes_file_container"> |
| 6496 | 6479 | <div class="fa-lg fa-solid fa-file-alt mes_file_icon"></div> |
| @@ -1385,8 +1385,8 @@ | ||
| 1385 | 1385 | "enable_functions_desc_1": "Autorise l'utilisation", |
| 1386 | 1386 | "enable_functions_desc_2": "outils de fonction", |
| 1387 | 1387 | "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.", |
| 1388 | 1388 | "ShowRequest model reasoning": "AfficherDemander les pensées du modèle", |
| 1389 | 1389 | "DisplayAllows the model's internalto thoughtsreturn inits thethinking responseprocess.": "AfficherPermet lesau penséesmodèle internesde duretourner modèleson dansprocessus lade réponseréflexion.", |
| 1390 | 1390 | "Confirm token parsing with": "Confirmer l'analyse des tokens avec", |
| 1391 | 1391 | "openai_logit_bias_no_items": "Aucun élément", |
| 1392 | 1392 | "api_no_connection": "Pas de connection...", |
| @@ -266,8 +266,8 @@ | ||
| 266 | 266 | "Use system prompt": "使用系统提示词", |
| 267 | 267 | "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过", |
| 268 | 268 | "Merges_all_system_messages_desc_2": "字段发送。", |
| 269 | 269 | "ShowRequest model reasoning": "展示思维链请求思维链", |
| 270 | 270 | "DisplayAllows the model's internalto thoughtsreturn inits thethinking responseprocess.": "展示模型在回复时的内部思维链允许模型返回其思维过程。", |
| 271 | 271 | "Assistant Prefill": "AI预填", |
| 272 | 272 | "Expand the editor": "展开编辑器", |
| 273 | 273 | "Start Claude's answer with...": "以如下内容开始Claude的回答...", |
| @@ -2357,8 +2357,8 @@ | ||
| 2357 | 2357 | "Forbid": "禁止", |
| 2358 | 2358 | "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。", |
| 2359 | 2359 | "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。", |
| 2360 | 2360 | "ShowRequest model reasoning": "顯示模型思維鏈請求模型思維鏈", |
| 2361 | 2361 | "DisplayAllows the model's internalto thoughtsreturn inits thethinking responseprocess.": "在回應中顯示模型的思維鏈(內部思考過程)讓模型回傳其思考過程。", |
| 2362 | 2362 | "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]", |
| 2363 | 2363 | "Model ID (optional)": "模型 ID(可選)", |
| 2364 | 2364 | "DeepSeek API Key": "DeepSeek API 金鑰", |
| @@ -95,6 +95,7 @@ import { | ||
| 95 | 95 | resetMovableStyles, |
| 96 | 96 | forceCharacterEditorTokenize, |
| 97 | 97 | applyPowerUserSettings, |
| 98 | + generatedTextFiltered, | |
| 98 | 99 | } from './scripts/power-user.js'; |
| 99 | 100 | |
| 100 | 101 | import { |
| @@ -169,6 +170,7 @@ import { | ||
| 169 | 170 | toggleDrawer, |
| 170 | 171 | isElementInViewport, |
| 171 | 172 | copyText, |
| 173 | + escapeHtml, | |
| 172 | 174 | } from './scripts/utils.js'; |
| 173 | 175 | import { debounce_timeout } from './scripts/constants.js'; |
| 174 | 176 | |
| @@ -1993,14 +1995,15 @@ export async function sendTextareaMessage() { | ||
| 1993 | 1995 | * @param {boolean} isUser If the message was sent by the user |
| 1994 | 1996 | * @param {number} messageId Message index in chat array |
| 1995 | 1997 | * @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides |
| 1998 | + * @param {boolean} [isReasoning] If the message is reasoning output | |
| 1996 | 1999 | * @returns {string} HTML string |
| 1997 | 2000 | */ |
| 1998 | 2001 | export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}, isReasoning = false) { |
| 1999 | 2002 | if (!mes) { |
| 2000 | 2003 | return ''; |
| 2001 | 2004 | } |
| 2002 | 2005 | |
| 2003 | 2006 | if (Number(messageId) === 0 && !isSystem && !isUser && !isReasoning) { |
| 2004 | 2007 | const mesBeforeReplace = mes; |
| 2005 | 2008 | const chatMessage = chat[messageId]; |
| 2006 | 2009 | mes = substituteParams(mes, undefined, ch_name); |
| @@ -2029,6 +2032,9 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san | ||
| 2029 | 2032 | if (!isSystem) { |
| 2030 | 2033 | function getRegexPlacement() { |
| 2031 | 2034 | try { |
| 2035 | + if (isReasoning) { | |
| 2036 | + return regex_placement.REASONING; | |
| 2037 | + } | |
| 2032 | 2038 | if (isUser) { |
| 2033 | 2039 | return regex_placement.USER_INPUT; |
| 2034 | 2040 | } else if (chat[messageId]?.extra?.type === 'narrator') { |
| @@ -2062,6 +2068,17 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san | ||
| 2062 | 2068 | mes = mes.replaceAll('<', '<').replaceAll('>', '>'); |
| 2063 | 2069 | } |
| 2064 | 2070 | |
| 2071 | + // Make sure reasoning strings are always shown, even if they include "<" or ">" | |
| 2072 | + [power_user.reasoning.prefix, power_user.reasoning.suffix].forEach((reasoningString) => { | |
| 2073 | + if (!reasoningString || !reasoningString.trim().length) { | |
| 2074 | + return; | |
| 2075 | + } | |
| 2076 | + // Only replace the first occurrence of the reasoning string | |
| 2077 | + if (mes.includes(reasoningString)) { | |
| 2078 | + mes = mes.replace(reasoningString, escapeHtml(reasoningString)); | |
| 2079 | + } | |
| 2080 | + }); | |
| 2081 | + | |
| 2065 | 2082 | if (!isSystem) { |
| 2066 | 2083 | // Save double quotes in tags as a special character to prevent them from being encoded |
| 2067 | 2084 | if (!power_user.encode_tags) { |
| @@ -2250,8 +2267,8 @@ function getMessageFromTemplate({ | ||
| 2250 | 2267 | export function updateMessageBlock(messageId, message) { |
| 2251 | 2268 | const messageElement = $(`#chat [mesid="${messageId}"]`); |
| 2252 | 2269 | const text = message?.extra?.display_text ?? message.mes; |
| 2253 | 2270 | messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId, {}, false)); |
| 2254 | 2271 | messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, -1messageId, {}, true)); |
| 2255 | 2272 | addCopyToCodeBlocks(messageElement); |
| 2256 | 2273 | appendMediaToMessage(message, messageElement); |
| 2257 | 2274 | } |
| @@ -2408,9 +2425,10 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2408 | 2425 | mes.is_user, |
| 2409 | 2426 | chat.indexOf(mes), |
| 2410 | 2427 | sanitizerOverrides, |
| 2428 | + false, | |
| 2411 | 2429 | ); |
| 2412 | 2430 | const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1, {}, false); |
| 2413 | 2431 | const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, -1chat.indexOf(mes), {}, true); |
| 2414 | 2432 | let bookmarkLink = mes?.extra?.bookmark_link ?? ''; |
| 2415 | 2433 | |
| 2416 | 2434 | let params = { |
| @@ -3057,7 +3075,7 @@ export function isStreamingEnabled() { | ||
| 3057 | 3075 | (main_api == 'openai' && |
| 3058 | 3076 | oai_settings.stream_openai && |
| 3059 | 3077 | !noStreamSources.includes(oai_settings.chat_completion_source) && |
| 3060 | 3078 | !(oai_settings.chat_completion_source == chat_completion_sources.OPENAI && (oai_settings.openai_model.startsWith('o1-') || oai_settings.openai_model.startsWith('o3'))) && |
| 3061 | 3079 | !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison'))) |
| 3062 | 3080 | || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming) |
| 3063 | 3081 | || (main_api == 'novel' && nai_settings.streaming_novel) |
| @@ -3153,7 +3171,7 @@ class StreamingProcessor { | ||
| 3153 | 3171 | this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true })); |
| 3154 | 3172 | } |
| 3155 | 3173 | else { |
| 3156 | 3174 | await saveReply(this.type, text, true, '', [], ''); |
| 3157 | 3175 | messageId = chat.length - 1; |
| 3158 | 3176 | this.#checkDomElements(messageId); |
| 3159 | 3177 | this.showMessageButtons(messageId); |
| @@ -3203,9 +3221,9 @@ class StreamingProcessor { | ||
| 3203 | 3221 | } |
| 3204 | 3222 | |
| 3205 | 3223 | if (this.reasoning) { |
| 3206 | 3224 | chat[messageId]['extra']['reasoning'] = power_user.trim_spaces ? this.reasoning.trim() : this.reasoning; |
| 3207 | 3225 | if (this.messageReasoningDom instanceof HTMLElement) { |
| 3208 | 3226 | const formattedReasoning = messageFormatting(this.reasoning, '', false, false, -1messageId, {}, true); |
| 3209 | 3227 | this.messageReasoningDom.innerHTML = formattedReasoning; |
| 3210 | 3228 | } |
| 3211 | 3229 | } |
| @@ -3232,6 +3250,8 @@ class StreamingProcessor { | ||
| 3232 | 3250 | chat[messageId].is_system, |
| 3233 | 3251 | chat[messageId].is_user, |
| 3234 | 3252 | messageId, |
| 3253 | + {}, | |
| 3254 | + false, | |
| 3235 | 3255 | ); |
| 3236 | 3256 | if (this.messageTextDom instanceof HTMLElement) { |
| 3237 | 3257 | this.messageTextDom.innerHTML = formattedText; |
| @@ -3283,39 +3303,11 @@ class StreamingProcessor { | ||
| 3283 | 3303 | unblockGeneration(); |
| 3284 | 3304 | generatedPromptCache = ''; |
| 3285 | 3305 | |
| 3286 | - //console.log("Generated text size:", text.length, text) | |
| 3287 | - | |
| 3288 | 3306 | const isAborted = this.abortController.signal.aborted; |
| 3289 | 3307 | if (!isAborted && power_user.auto_swipe && !isAbortedgeneratedTextFiltered(text)) { |
| 3290 | - function containsBlacklistedWords(str, blacklist, threshold) { | |
| 3308 | + return swipe_right(); | |
| 3291 | - const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi'); | |
| 3292 | - const matches = str.match(regex) || []; | |
| 3293 | - return matches.length >= threshold; | |
| 3294 | - } | |
| 3295 | - | |
| 3296 | - const generatedTextFiltered = (text) => { | |
| 3297 | - if (text) { | |
| 3298 | - if (power_user.auto_swipe_minimum_length) { | |
| 3299 | - if (text.length < power_user.auto_swipe_minimum_length && text.length !== 0) { | |
| 3300 | - console.log('Generated text size too small'); | |
| 3301 | - return true; | |
| 3302 | - } | |
| 3303 | - } | |
| 3304 | - if (power_user.auto_swipe_blacklist_threshold) { | |
| 3305 | - if (containsBlacklistedWords(text, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) { | |
| 3306 | - console.log('Generated text has blacklisted words'); | |
| 3307 | - return true; | |
| 3308 | - } | |
| 3309 | - } | |
| 3310 | - } | |
| 3311 | - return false; | |
| 3312 | - }; | |
| 3313 | - | |
| 3314 | - if (generatedTextFiltered(text)) { | |
| 3315 | - swipe_right(); | |
| 3316 | - return; | |
| 3317 | - } | |
| 3318 | 3309 | } |
| 3310 | + | |
| 3319 | 3311 | playMessageSound(); |
| 3320 | 3312 | } |
| 3321 | 3313 | |
| @@ -3373,7 +3365,7 @@ class StreamingProcessor { | ||
| 3373 | 3365 | const timestamps = []; |
| 3374 | 3366 | for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) { |
| 3375 | 3367 | timestamps.push(Date.now()); |
| 3376 | 3368 | if (this.isStopped || this.abortController.signal.aborted) { |
| 3377 | 3369 | return; |
| 3378 | 3370 | } |
| 3379 | 3371 | |
| @@ -3383,7 +3375,7 @@ class StreamingProcessor { | ||
| 3383 | 3375 | if (logprobs) { |
| 3384 | 3376 | this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs])); |
| 3385 | 3377 | } |
| 3386 | 3378 | this.reasoning = getRegexedString(state?.reasoning ?? '', regex_placement.REASONING); |
| 3387 | 3379 | await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text); |
| 3388 | 3380 | await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text)); |
| 3389 | 3381 | } |
| @@ -3850,14 +3842,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3850 | 3842 | coreChat.pop(); |
| 3851 | 3843 | } |
| 3852 | 3844 | |
| 3853 | - const reasoning = new PromptReasoning(); | |
| 3854 | - for (let i = coreChat.length - 1; i >= 0; i--) { | |
| 3855 | - if (reasoning.isLimitReached()) { | |
| 3856 | - break; | |
| 3857 | - } | |
| 3858 | - coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) }; | |
| 3859 | - } | |
| 3860 | - | |
| 3861 | 3845 | coreChat = await Promise.all(coreChat.map(async (chatItem, index) => { |
| 3862 | 3846 | let message = chatItem.mes; |
| 3863 | 3847 | let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT; |
| @@ -3877,6 +3861,27 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3877 | 3861 | }; |
| 3878 | 3862 | })); |
| 3879 | 3863 | |
| 3864 | + const reasoning = new PromptReasoning(); | |
| 3865 | + for (let i = coreChat.length - 1; i >= 0; i--) { | |
| 3866 | + const depth = coreChat.length - i - 1; | |
| 3867 | + const isPrefix = isContinue && i === coreChat.length - 1; | |
| 3868 | + coreChat[i] = { | |
| 3869 | + ...coreChat[i], | |
| 3870 | + mes: reasoning.addToMessage( | |
| 3871 | + coreChat[i].mes, | |
| 3872 | + getRegexedString( | |
| 3873 | + String(coreChat[i].extra?.reasoning ?? ''), | |
| 3874 | + regex_placement.REASONING, | |
| 3875 | + { isPrompt: true, depth: depth }, | |
| 3876 | + ), | |
| 3877 | + isPrefix, | |
| 3878 | + ), | |
| 3879 | + }; | |
| 3880 | + if (reasoning.isLimitReached()) { | |
| 3881 | + break; | |
| 3882 | + } | |
| 3883 | + } | |
| 3884 | + | |
| 3880 | 3885 | // Determine token limit |
| 3881 | 3886 | let this_max_context = getMaxContextSize(); |
| 3882 | 3887 | |
| @@ -4462,7 +4467,13 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4462 | 4467 | // TODO: Make all extension prompts use an array/splice method |
| 4463 | 4468 | const lengthDiff = mesSend.length - cfgPrompt.depth; |
| 4464 | 4469 | const cfgDepth = lengthDiff >= 0 ? lengthDiff : 0; |
| 4465 | - finalMesSend[cfgDepth].extensionPrompts.push(`${cfgPrompt.value}\n`); | |
| 4470 | + const cfgMessage = finalMesSend[cfgDepth]; | |
| 4471 | + if (cfgMessage) { | |
| 4472 | + if (!Array.isArray(finalMesSend[cfgDepth].extensionPrompts)) { | |
| 4473 | + finalMesSend[cfgDepth].extensionPrompts = []; | |
| 4474 | + } | |
| 4475 | + finalMesSend[cfgDepth].extensionPrompts.push(`${cfgPrompt.value}\n`); | |
| 4476 | + } | |
| 4466 | 4477 | } |
| 4467 | 4478 | } |
| 4468 | 4479 | } |
| @@ -4785,6 +4796,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4785 | 4796 | const swipes = extractMultiSwipes(data, type); |
| 4786 | 4797 | |
| 4787 | 4798 | messageChunk = cleanUpMessage(getMessage, isImpersonate, isContinue, false); |
| 4799 | + reasoning = getRegexedString(reasoning, regex_placement.REASONING); | |
| 4800 | + | |
| 4801 | + if (power_user.trim_spaces) { | |
| 4802 | + reasoning = reasoning.trim(); | |
| 4803 | + } | |
| 4788 | 4804 | |
| 4789 | 4805 | if (isContinue) { |
| 4790 | 4806 | getMessage = continue_mag + getMessage; |
| @@ -4843,32 +4859,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4843 | 4859 | } |
| 4844 | 4860 | |
| 4845 | 4861 | const isAborted = abortController && abortController.signal.aborted; |
| 4846 | 4862 | if (!isAborted && power_user.auto_swipe && !isAbortedgeneratedTextFiltered(getMessage)) { |
| 4847 | - console.debug('checking for autoswipeblacklist on non-streaming message'); | |
| 4863 | + is_send_press = false; | |
| 4848 | - function containsBlacklistedWords(getMessage, blacklist, threshold) { | |
| 4864 | + return swipe_right(); | |
| 4849 | - console.debug('checking blacklisted words'); | |
| 4850 | - const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi'); | |
| 4851 | - const matches = getMessage.match(regex) || []; | |
| 4852 | - return matches.length >= threshold; | |
| 4853 | - } | |
| 4854 | - | |
| 4855 | - const generatedTextFiltered = (getMessage) => { | |
| 4856 | - if (power_user.auto_swipe_blacklist_threshold) { | |
| 4857 | - if (containsBlacklistedWords(getMessage, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) { | |
| 4858 | - console.debug('Generated text has blacklisted words'); | |
| 4859 | - return true; | |
| 4860 | - } | |
| 4861 | - } | |
| 4862 | - | |
| 4863 | - return false; | |
| 4864 | - }; | |
| 4865 | - if (generatedTextFiltered(getMessage)) { | |
| 4866 | - console.debug('swiping right automatically'); | |
| 4867 | - is_send_press = false; | |
| 4868 | - swipe_right(); | |
| 4869 | - // TODO: do we want to resolve after an auto-swipe? | |
| 4870 | - return; | |
| 4871 | - } | |
| 4872 | 4865 | } |
| 4873 | 4866 | |
| 4874 | 4867 | console.debug('/api/chats/save called by /Generate'); |
| @@ -5719,15 +5712,26 @@ function extractMessageFromData(data) { | ||
| 5719 | 5712 | * @returns {string} Extracted reasoning |
| 5720 | 5713 | */ |
| 5721 | 5714 | function extractReasoningFromData(data) { |
| 5722 | - if (main_api === 'openai' && oai_settings.show_thoughts) { | |
| 5715 | + switch (main_api) { | |
| 5723 | - switch (oai_settings.chat_completion_source) { | |
| 5716 | + case 'textgenerationwebui': | |
| 5724 | - case chat_completion_sources.DEEPSEEK: | |
| 5717 | + switch (textgen_settings.type) { | |
| 5725 | - return data?.choices?.[0]?.message?.reasoning_content ?? ''; | |
| 5718 | + case textgen_types.OPENROUTER: | |
| 5726 | - case chat_completion_sources.OPENROUTER: | |
| 5719 | + return data?.choices?.[0]?.reasoning ?? ''; | |
| 5727 | - return data?.choices?.[0]?.message?.reasoning ?? ''; | |
| 5720 | + } | |
| 5728 | - case chat_completion_sources.MAKERSUITE: | |
| 5721 | + break; | |
| 5729 | - return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; | |
| 5722 | + | |
| 5730 | - } | |
| 5723 | + case 'openai': | |
| 5724 | + if (!oai_settings.show_thoughts) break; | |
| 5725 | + | |
| 5726 | + switch (oai_settings.chat_completion_source) { | |
| 5727 | + case chat_completion_sources.DEEPSEEK: | |
| 5728 | + return data?.choices?.[0]?.message?.reasoning_content ?? ''; | |
| 5729 | + case chat_completion_sources.OPENROUTER: | |
| 5730 | + return data?.choices?.[0]?.message?.reasoning ?? ''; | |
| 5731 | + case chat_completion_sources.MAKERSUITE: | |
| 5732 | + return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; | |
| 5733 | + } | |
| 5734 | + break; | |
| 5731 | 5735 | } |
| 5732 | 5736 | |
| 5733 | 5737 | return ''; |
| @@ -5923,6 +5927,15 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes, | ||
| 5923 | 5927 | chat[chat.length - 1]['extra'] = {}; |
| 5924 | 5928 | } |
| 5925 | 5929 | |
| 5930 | + // Coerce null/undefined to empty string | |
| 5931 | + if (!chat[chat.length - 1]['extra']['reasoning']) { | |
| 5932 | + chat[chat.length - 1]['extra']['reasoning'] = ''; | |
| 5933 | + } | |
| 5934 | + | |
| 5935 | + if (!reasoning) { | |
| 5936 | + reasoning = ''; | |
| 5937 | + } | |
| 5938 | + | |
| 5926 | 5939 | let oldMessage = ''; |
| 5927 | 5940 | const generationFinished = new Date(); |
| 5928 | 5941 | const img = extractImageFromMessage(getMessage); |
| @@ -7190,9 +7203,11 @@ function messageEditAuto(div) { | ||
| 7190 | 7203 | mes.is_system, |
| 7191 | 7204 | mes.is_user, |
| 7192 | 7205 | this_edit_mes_id, |
| 7206 | + {}, | |
| 7207 | + false, | |
| 7193 | 7208 | )); |
| 7194 | 7209 | mesBlock.find('.mes_bias').empty(); |
| 7195 | 7210 | mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false)); |
| 7196 | 7211 | saveChatDebounced(); |
| 7197 | 7212 | } |
| 7198 | 7213 | |
| @@ -7214,10 +7229,12 @@ async function messageEditDone(div) { | ||
| 7214 | 7229 | mes.is_system, |
| 7215 | 7230 | mes.is_user, |
| 7216 | 7231 | this_edit_mes_id, |
| 7232 | + {}, | |
| 7233 | + false, | |
| 7217 | 7234 | ), |
| 7218 | 7235 | ); |
| 7219 | 7236 | mesBlock.find('.mes_bias').empty(); |
| 7220 | 7237 | mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false)); |
| 7221 | 7238 | appendMediaToMessage(mes, div.closest('.mes')); |
| 7222 | 7239 | addCopyToCodeBlocks(div.closest('.mes')); |
| 7223 | 7240 | |
| @@ -8721,11 +8738,6 @@ const swipe_right = () => { | ||
| 8721 | 8738 | easing: animation_easing, |
| 8722 | 8739 | queue: false, |
| 8723 | 8740 | complete: async function () { |
| 8724 | - /*if (!selected_group) { | |
| 8725 | - var typingIndicator = $("#typing_indicator_template .typing_indicator").clone(); | |
| 8726 | - typingIndicator.find(".typing_indicator_name").text(characters[this_chid].name); | |
| 8727 | - } */ | |
| 8728 | - /* $("#chat").append(typingIndicator); */ | |
| 8729 | 8741 | const is_animation_scroll = ($('#chat').scrollTop() >= ($('#chat').prop('scrollHeight') - $('#chat').outerHeight()) - 10); |
| 8730 | 8742 | //console.log(parseInt(chat[chat.length-1]['swipe_id'])); |
| 8731 | 8743 | //console.log(chat[chat.length-1]['swipes'].length); |
| @@ -10860,6 +10872,8 @@ jQuery(async function () { | ||
| 10860 | 10872 | chat[this_edit_mes_id].is_system, |
| 10861 | 10873 | chat[this_edit_mes_id].is_user, |
| 10862 | 10874 | this_edit_mes_id, |
| 10875 | + {}, | |
| 10876 | + false, | |
| 10863 | 10877 | )); |
| 10864 | 10878 | appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes')); |
| 10865 | 10879 | addCopyToCodeBlocks($(this).closest('.mes')); |
| @@ -566,7 +566,7 @@ export function initAuthorsNote() { | ||
| 566 | 566 | namedArgumentList: [], |
| 567 | 567 | unnamedArgumentList: [ |
| 568 | 568 | new SlashCommandArgument( |
| 569 | 569 | 'positionrole', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'], |
| 570 | 570 | ), |
| 571 | 571 | ], |
| 572 | 572 | helpString: ` |
| @@ -96,8 +96,13 @@ function highlightLockedBackground() { | ||
| 96 | 96 | }); |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | +/** | |
| 100 | + * Locks the background for the current chat | |
| 101 | + * @param {Event} e Click event | |
| 102 | + * @returns {string} Empty string | |
| 103 | + */ | |
| 99 | 104 | function onLockBackgroundClick(e) { |
| 100 | 105 | e?.stopPropagation(); |
| 101 | 106 | |
| 102 | 107 | const chatName = getCurrentChatId(); |
| 103 | 108 | |
| @@ -106,7 +111,7 @@ function onLockBackgroundClick(e) { | ||
| 106 | 111 | return ''; |
| 107 | 112 | } |
| 108 | 113 | |
| 109 | 114 | const relativeBgImage = getUrlParameter(this) ?? background_settings.url; |
| 110 | 115 | |
| 111 | 116 | saveBackgroundMetadata(relativeBgImage); |
| 112 | 117 | setCustomBackground(); |
| @@ -114,8 +119,13 @@ function onLockBackgroundClick(e) { | ||
| 114 | 119 | return ''; |
| 115 | 120 | } |
| 116 | 121 | |
| 122 | +/** | |
| 123 | + * Locks the background for the current chat | |
| 124 | + * @param {Event} e Click event | |
| 125 | + * @returns {string} Empty string | |
| 126 | + */ | |
| 117 | 127 | function onUnlockBackgroundClick(e) { |
| 118 | 128 | e?.stopPropagation(); |
| 119 | 129 | removeBackgroundMetadata(); |
| 120 | 130 | unsetCustomBackground(); |
| 121 | 131 | highlightLockedBackground(); |
| @@ -513,12 +523,12 @@ export function initBackgrounds() { | ||
| 513 | 523 | $('#add_bg_button').on('change', onBackgroundUploadSelected); |
| 514 | 524 | $('#bg-filter').on('input', onBackgroundFilterInput); |
| 515 | 525 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg', |
| 516 | - callback: onLockBackgroundClick, | |
| 526 | + callback: () => onLockBackgroundClick(new CustomEvent('click')), | |
| 517 | 527 | aliases: ['bglock'], |
| 518 | 528 | helpString: 'Locks a background for the currently selected chat', |
| 519 | 529 | })); |
| 520 | 530 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg', |
| 521 | - callback: onUnlockBackgroundClick, | |
| 531 | + callback: () => onUnlockBackgroundClick(new CustomEvent('click')), | |
| 522 | 532 | aliases: ['bgunlock'], |
| 523 | 533 | helpString: 'Unlocks a background for the currently selected chat', |
| 524 | 534 | })); |
| @@ -30,6 +30,7 @@ const CC_COMMANDS = [ | ||
| 30 | 30 | 'api-url', |
| 31 | 31 | 'model', |
| 32 | 32 | 'proxy', |
| 33 | + 'stop-strings', | |
| 33 | 34 | ]; |
| 34 | 35 | |
| 35 | 36 | const TC_COMMANDS = [ |
| @@ -43,6 +44,7 @@ const TC_COMMANDS = [ | ||
| 43 | 44 | 'context', |
| 44 | 45 | 'instruct-state', |
| 45 | 46 | 'tokenizer', |
| 47 | + 'stop-strings', | |
| 46 | 48 | ]; |
| 47 | 49 | |
| 48 | 50 | const FANCY_NAMES = { |
| @@ -57,6 +59,7 @@ const FANCY_NAMES = { | ||
| 57 | 59 | 'instruct': 'Instruct Template', |
| 58 | 60 | 'context': 'Context Template', |
| 59 | 61 | 'tokenizer': 'Tokenizer', |
| 62 | + 'stop-strings': 'Custom Stopping Strings', | |
| 60 | 63 | }; |
| 61 | 64 | |
| 62 | 65 | /** |
| @@ -138,6 +141,7 @@ const profilesProvider = () => [ | ||
| 138 | 141 | * @property {string} [context] Context Template |
| 139 | 142 | * @property {string} [instruct-state] Instruct Mode |
| 140 | 143 | * @property {string} [tokenizer] Tokenizer |
| 144 | + * @property {string} [stop-strings] Custom Stopping Strings | |
| 141 | 145 | * @property {string[]} [exclude] Commands to exclude |
| 142 | 146 | */ |
| 143 | 147 | |
| @@ -2178,7 +2178,6 @@ function migrateSettings() { | ||
| 2178 | 2178 | typeList: [ARGUMENT_TYPE.STRING], |
| 2179 | 2179 | isRequired: true, |
| 2180 | 2180 | enumProvider: commonEnumProviders.characters('character'), |
| 2181 | - forceEnum: true, | |
| 2182 | 2181 | }), |
| 2183 | 2182 | ], |
| 2184 | 2183 | helpString: 'Returns the last set sprite / expression for the named character.', |
| @@ -441,7 +441,6 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 441 | 441 | description: 'character name', |
| 442 | 442 | typeList: [ARGUMENT_TYPE.STRING], |
| 443 | 443 | enumProvider: commonEnumProviders.characters('character'), |
| 444 | - forceEnum: true, | |
| 445 | 444 | }), |
| 446 | 445 | SlashCommandNamedArgument.fromProps({ |
| 447 | 446 | name: 'group', |
| @@ -883,6 +883,10 @@ export class SlashCommandHandler { | ||
| 883 | 883 | } |
| 884 | 884 | } |
| 885 | 885 | getQuickReply(args) { |
| 886 | + if (!args.id && !args.label) { | |
| 887 | + toastr.error('Please provide a valid id or label.'); | |
| 888 | + return ''; | |
| 889 | + } | |
| 886 | 890 | try { |
| 887 | 891 | return JSON.stringify(this.api.getQrByLabel(args.set, args.id !== undefined ? Number(args.id) : args.label)); |
| 888 | 892 | } catch (ex) { |
| @@ -94,6 +94,12 @@ | ||
| 94 | 94 | <span data-i18n="World Info">World Info</span> |
| 95 | 95 | </label> |
| 96 | 96 | </div> |
| 97 | + <div data-i18n="[title]ext_regex_reasoning_desc" title="Reasoning block contents. When 'Only Format Prompt' is checked, it will also affect the reasoning contents added to the prompt."> | |
| 98 | + <label class="checkbox flex-container"> | |
| 99 | + <input type="checkbox" name="replace_position" value="6"> | |
| 100 | + <span data-i18n="Reasoning">Reasoning</span> | |
| 101 | + </label> | |
| 102 | + </div> | |
| 97 | 103 | <div class="flex-container wide100p marginTop5"> |
| 98 | 104 | <div class="flex1 flex-container flexNoGap"> |
| 99 | 105 | <small data-i18n="[title]ext_regex_min_depth_desc" title="When applied to prompts or display, only affect messages that are at least N levels deep. 0 = last message, 1 = penultimate message, etc. Only counts WI entries @Depth and usable messages, i.e. not hidden or system."> |
| @@ -20,6 +20,7 @@ const regex_placement = { | ||
| 20 | 20 | SLASH_COMMAND: 3, |
| 21 | 21 | // 4 - sendAs (legacy) |
| 22 | 22 | WORLD_INFO: 5, |
| 23 | + REASONING: 6, | |
| 23 | 24 | }; |
| 24 | 25 | |
| 25 | 26 | export const substitute_find_regex = { |
| @@ -94,7 +95,7 @@ function getRegexedString(rawString, placement, { characterOverride, isMarkdown, | ||
| 94 | 95 | // Script applies to Generate and input is Generate |
| 95 | 96 | (script.promptOnly && isPrompt) || |
| 96 | 97 | // Script applies to all cases when neither "only"s are true, but there's no need to do it when `isMarkdown`, the as source (chat history) should already be changed beforehand |
| 97 | 98 | (!script.markdownOnly && !script.promptOnly && !isMarkdown && !isPrompt) |
| 98 | 99 | ) { |
| 99 | 100 | if (isEdit && !script.runOnEdit) { |
| 100 | 101 | console.debug(`getRegexedString: Skipping script ${script.scriptName} because it does not run on edit`); |
| @@ -18,7 +18,7 @@ import { t } from '../../i18n.js'; | ||
| 18 | 18 | * @property {string} replaceString - The replace string |
| 19 | 19 | * @property {string[]} trimStrings - The trim strings |
| 20 | 20 | * @property {string?} findRegex - The find regex |
| 21 | 21 | * @property {stringnumber?} substituteRegex - The substitute regex |
| 22 | 22 | */ |
| 23 | 23 | |
| 24 | 24 | /** |
| @@ -388,7 +388,7 @@ class AllTalkTtsProvider { | ||
| 388 | 388 | } |
| 389 | 389 | |
| 390 | 390 | async fetchRvcVoiceObjects() { |
| 391 | 391 | if (this.settings.server_version == 'v2v1') { |
| 392 | 392 | console.log('Skipping RVC voices fetch for V1 server'); |
| 393 | 393 | return []; |
| 394 | 394 | } |
| @@ -1031,14 +1031,18 @@ class AllTalkTtsProvider { | ||
| 1031 | 1031 | console.error('fetchTtsGeneration Error Response Text:', errorText); |
| 1032 | 1032 | throw new Error(`HTTP ${response.status}: ${errorText}`); |
| 1033 | 1033 | } |
| 1034 | + | |
| 1034 | 1035 | const data = await response.json(); |
| 1035 | 1036 | |
| 1036 | - // Handle V1/V2 URL differences | |
| 1037 | + // V1 returns a complete URL, V2 returns a relative path | |
| 1037 | 1038 | const outputUrl =if (this.settings.server_version === 'v1') { |
| 1038 | - ? data.output_file_url // V1 returns full URL | |
| 1039 | + // V1: Use the complete URL directly from the response | |
| 1039 | - : `${this.settings.provider_endpoint}${data.output_file_url}`; // V2 returns relative path | |
| 1040 | + return data.output_file_url; | |
| 1041 | + } else { | |
| 1042 | + // V2: Combine the endpoint with the relative path | |
| 1043 | + return `${this.settings.provider_endpoint}${data.output_file_url}`; | |
| 1044 | + } | |
| 1040 | 1045 | |
| 1041 | - return outputUrl; | |
| 1042 | 1046 | } catch (error) { |
| 1043 | 1047 | console.error('[fetchTtsGeneration] Exception caught:', error); |
| 1044 | 1048 | throw error; |
| @@ -30,6 +30,7 @@ import { GoogleTranslateTtsProvider } from './google-translate.js'; | ||
| 30 | 30 | export { talkingAnimation }; |
| 31 | 31 | |
| 32 | 32 | const UPDATE_INTERVAL = 1000; |
| 33 | +const wrapper = new ModuleWorkerWrapper(moduleWorker); | |
| 33 | 34 | |
| 34 | 35 | let voiceMapEntries = []; |
| 35 | 36 | let voiceMap = {}; // {charName:voiceid, charName2:voiceid2} |
| @@ -120,7 +121,7 @@ async function onNarrateOneMessage() { | ||
| 120 | 121 | } |
| 121 | 122 | |
| 122 | 123 | resetTtsPlayback(); |
| 123 | 124 | ttsJobQueue.pushprocessAndQueueTtsMessage(message); |
| 124 | 125 | moduleWorker(); |
| 125 | 126 | } |
| 126 | 127 | |
| @@ -147,7 +148,7 @@ async function onNarrateText(args, text) { | ||
| 147 | 148 | } |
| 148 | 149 | |
| 149 | 150 | resetTtsPlayback(); |
| 150 | 151 | ttsJobQueue.pushprocessAndQueueTtsMessage({ mes: text, name: name }); |
| 151 | 152 | await moduleWorker(); |
| 152 | 153 | |
| 153 | 154 | // Return back to the chat voices |
| @@ -220,6 +221,36 @@ function isTtsProcessing() { | ||
| 220 | 221 | return processing; |
| 221 | 222 | } |
| 222 | 223 | |
| 224 | +/** | |
| 225 | + * Splits a message into lines and adds each non-empty line to the TTS job queue. | |
| 226 | + * @param {Object} message - The message object to be processed. | |
| 227 | + * @param {string} message.mes - The text of the message to be split into lines. | |
| 228 | + * @param {string} message.name - The name associated with the message. | |
| 229 | + * @returns {void} | |
| 230 | + */ | |
| 231 | +function processAndQueueTtsMessage(message) { | |
| 232 | + if (!extension_settings.tts.narrate_by_paragraphs) { | |
| 233 | + ttsJobQueue.push(message); | |
| 234 | + return; | |
| 235 | + } | |
| 236 | + | |
| 237 | + const lines = message.mes.split('\n'); | |
| 238 | + | |
| 239 | + for (let i = 0; i < lines.length; i++) { | |
| 240 | + const line = lines[i]; | |
| 241 | + | |
| 242 | + if (line.length === 0) { | |
| 243 | + continue; | |
| 244 | + } | |
| 245 | + | |
| 246 | + ttsJobQueue.push( | |
| 247 | + Object.assign({}, message, { | |
| 248 | + mes: line, | |
| 249 | + }), | |
| 250 | + ); | |
| 251 | + } | |
| 252 | +} | |
| 253 | + | |
| 223 | 254 | function debugTtsPlayback() { |
| 224 | 255 | console.log(JSON.stringify( |
| 225 | 256 | { |
| @@ -350,7 +381,7 @@ function onAudioControlClicked() { | ||
| 350 | 381 | talkingAnimation(false); |
| 351 | 382 | } else { |
| 352 | 383 | // Default play behavior if not processing or playing is to play the last message. |
| 353 | 384 | ttsJobQueue.pushprocessAndQueueTtsMessage(context.chat[context.chat.length - 1]); |
| 354 | 385 | } |
| 355 | 386 | updateUiAudioPlayState(); |
| 356 | 387 | } |
| @@ -376,6 +407,7 @@ function completeCurrentAudioJob() { | ||
| 376 | 407 | currentAudioJob = null; |
| 377 | 408 | talkingAnimation(false); //stop lip animation |
| 378 | 409 | // updateUiPlayState(); |
| 410 | + wrapper.update(); | |
| 379 | 411 | } |
| 380 | 412 | |
| 381 | 413 | /** |
| @@ -569,6 +601,7 @@ function loadSettings() { | ||
| 569 | 601 | $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only); |
| 570 | 602 | $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation); |
| 571 | 603 | $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation); |
| 604 | + $('#tts_narrate_by_paragraphs').prop('checked', extension_settings.tts.narrate_by_paragraphs); | |
| 572 | 605 | $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only); |
| 573 | 606 | $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user); |
| 574 | 607 | $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks); |
| @@ -638,6 +671,11 @@ function onPeriodicAutoGenerationClick() { | ||
| 638 | 671 | saveSettingsDebounced(); |
| 639 | 672 | } |
| 640 | 673 | |
| 674 | +function onNarrateByParagraphsClick() { | |
| 675 | + extension_settings.tts.narrate_by_paragraphs = !!$('#tts_narrate_by_paragraphs').prop('checked'); | |
| 676 | + saveSettingsDebounced(); | |
| 677 | +} | |
| 678 | + | |
| 641 | 679 | |
| 642 | 680 | function onNarrateDialoguesClick() { |
| 643 | 681 | extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked'); |
| @@ -816,7 +854,12 @@ async function onMessageEvent(messageId, lastCharIndex) { | ||
| 816 | 854 | lastChatId = context.chatId; |
| 817 | 855 | |
| 818 | 856 | console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`); |
| 819 | - ttsJobQueue.push(message); | |
| 857 | + | |
| 858 | + if (extension_settings.tts.periodic_auto_generation) { | |
| 859 | + ttsJobQueue.push(message); | |
| 860 | + } else { | |
| 861 | + processAndQueueTtsMessage(message); | |
| 862 | + } | |
| 820 | 863 | } |
| 821 | 864 | |
| 822 | 865 | async function onMessageDeleted() { |
| @@ -1156,6 +1199,7 @@ jQuery(async function () { | ||
| 1156 | 1199 | $('#tts_pass_asterisks').on('click', onPassAsterisksClick); |
| 1157 | 1200 | $('#tts_auto_generation').on('click', onAutoGenerationClick); |
| 1158 | 1201 | $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick); |
| 1202 | + $('#tts_narrate_by_paragraphs').on('click', onNarrateByParagraphsClick); | |
| 1159 | 1203 | $('#tts_narrate_user').on('click', onNarrateUserClick); |
| 1160 | 1204 | |
| 1161 | 1205 | $('#playback_rate').on('input', function () { |
| @@ -1177,7 +1221,6 @@ jQuery(async function () { | ||
| 1177 | 1221 | loadSettings(); // Depends on Extension Controls and loadTtsProvider |
| 1178 | 1222 | loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies |
| 1179 | 1223 | addAudioControl(); // Depends on Extension Controls |
| 1180 | - const wrapper = new ModuleWorkerWrapper(moduleWorker); | |
| 1181 | 1224 | setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things |
| 1182 | 1225 | eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback); |
| 1183 | 1226 | eventSource.on(event_types.CHAT_CHANGED, onChatChanged); |
| @@ -30,6 +30,10 @@ | ||
| 30 | 30 | <input type="checkbox" id="tts_periodic_auto_generation"> |
| 31 | 31 | <small data-i18n="Narrate by paragraphs (when streaming)">Narrate by paragraphs (when streaming)</small> |
| 32 | 32 | </label> |
| 33 | + <label class="checkbox_label" for="tts_narrate_by_paragraphs"> | |
| 34 | + <input type="checkbox" id="tts_narrate_by_paragraphs"> | |
| 35 | + <small data-i18n="Narrate by paragraphs (when not streaming)">Narrate by paragraphs (when not streaming)</small> | |
| 36 | + </label> | |
| 33 | 37 | <label class="checkbox_label" for="tts_narrate_quoted"> |
| 34 | 38 | <input type="checkbox" id="tts_narrate_quoted"> |
| 35 | 39 | <small data-i18n="Only narrate quotes">Only narrate "quotes"</small> |
| @@ -292,10 +292,11 @@ export function getGroupNames() { | ||
| 292 | 292 | |
| 293 | 293 | /** |
| 294 | 294 | * Finds the character ID for a group member. |
| 295 | 295 | * @param {number|string} arg 0-based member index or character name |
| 296 | - * @returns {number} 0-based character ID | |
| 296 | + * @param {Boolean} full Whether to return a key-value object containing extra data | |
| 297 | + * @returns {number|Object} 0-based character ID or key-value object if full is true | |
| 297 | 298 | */ |
| 298 | 299 | export function findGroupMemberId(arg, full = false) { |
| 299 | 300 | arg = arg?.trim(); |
| 300 | 301 | |
| 301 | 302 | if (!arg) { |
| @@ -311,15 +312,19 @@ export function findGroupMemberId(arg) { | ||
| 311 | 312 | } |
| 312 | 313 | |
| 313 | 314 | const index = parseInt(arg); |
| 314 | 315 | const searchByNamesearchByString = isNaN(index); |
| 315 | 316 | |
| 316 | 317 | if (searchByNamesearchByString) { |
| 317 | - const memberNames = group.members.map(x => ({ name: characters.find(y => y.avatar === x)?.name, index: characters.findIndex(y => y.avatar === x) })); | |
| 318 | + const memberNames = group.members.map(x => ({ | |
| 318 | - const fuse = new Fuse(memberNames, { keys: ['name'] }); | |
| 319 | + avatar: x, | |
| 320 | + name: characters.find(y => y.avatar === x)?.name, | |
| 321 | + index: characters.findIndex(y => y.avatar === x), | |
| 322 | + })); | |
| 323 | + const fuse = new Fuse(memberNames, { keys: ['avatar', 'name'] }); | |
| 319 | 324 | const result = fuse.search(arg); |
| 320 | 325 | |
| 321 | 326 | if (!result.length) { |
| 322 | 327 | console.warn(`WARN: No group member found withusing namestring ${arg}`); |
| 323 | 328 | return; |
| 324 | 329 | } |
| 325 | 330 | |
| @@ -330,9 +335,11 @@ export function findGroupMemberId(arg) { | ||
| 330 | 335 | return; |
| 331 | 336 | } |
| 332 | 337 | |
| 333 | 338 | console.log(`TriggeringTargeting group member ${chid} (${arg}) from search result`, result[0]); |
| 334 | - return chid; | |
| 339 | + | |
| 335 | - } else { | |
| 340 | + return !full ? chid : { ...{ id: chid }, ...result[0].item }; | |
| 341 | + } | |
| 342 | + else { | |
| 336 | 343 | const memberAvatar = group.members[index]; |
| 337 | 344 | |
| 338 | 345 | if (memberAvatar === undefined) { |
| @@ -347,8 +354,14 @@ export function findGroupMemberId(arg) { | ||
| 347 | 354 | return; |
| 348 | 355 | } |
| 349 | 356 | |
| 350 | 357 | console.log(`TriggeringTargeting group member ${memberAvatar} at index ${index}`); |
| 351 | - return chid; | |
| 358 | + | |
| 359 | + return !full ? chid : { | |
| 360 | + id: chid, | |
| 361 | + avatar: memberAvatar, | |
| 362 | + name: characters.find(y => y.avatar === memberAvatar)?.name, | |
| 363 | + index: index, | |
| 364 | + }; | |
| 352 | 365 | } |
| 353 | 366 | } |
| 354 | 367 | |
| @@ -805,7 +818,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { | ||
| 805 | 818 | |
| 806 | 819 | /** @type {any} Caution: JS war crimes ahead */ |
| 807 | 820 | let textResult = ''; |
| 808 | - let typingIndicator = $('#chat .typing_indicator'); | |
| 809 | 821 | const group = groups.find((x) => x.id === selected_group); |
| 810 | 822 | |
| 811 | 823 | if (!group || !Array.isArray(group.members) || !group.members.length) { |
| @@ -821,14 +833,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { | ||
| 821 | 833 | setCharacterId(undefined); |
| 822 | 834 | const userInput = String($('#send_textarea').val()); |
| 823 | 835 | |
| 824 | - if (typingIndicator.length === 0 && !isStreamingEnabled()) { | |
| 825 | - typingIndicator = $( | |
| 826 | - '#typing_indicator_template .typing_indicator', | |
| 827 | - ).clone(); | |
| 828 | - typingIndicator.hide(); | |
| 829 | - $('#chat').append(typingIndicator); | |
| 830 | - } | |
| 831 | - | |
| 832 | 836 | // id of this specific batch for regeneration purposes |
| 833 | 837 | group_generation_id = Date.now(); |
| 834 | 838 | const lastMessage = chat[chat.length - 1]; |
| @@ -906,14 +910,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { | ||
| 906 | 910 | } |
| 907 | 911 | await eventSource.emit(event_types.GROUP_MEMBER_DRAFTED, chId); |
| 908 | 912 | |
| 909 | - if (type !== 'swipe' && type !== 'impersonate' && !isStreamingEnabled()) { | |
| 910 | - // update indicator and scroll down | |
| 911 | - typingIndicator | |
| 912 | - .find('.typing_indicator_name') | |
| 913 | - .text(characters[chId].name); | |
| 914 | - typingIndicator.show(); | |
| 915 | - } | |
| 916 | - | |
| 917 | 913 | // Wait for generation to finish |
| 918 | 914 | textResult = await Generate(generateType, { automatic_trigger: by_auto_mode, ...(params || {}) }); |
| 919 | 915 | let messageChunk = textResult?.messageChunk; |
| @@ -930,8 +926,6 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) { | ||
| 930 | 926 | } |
| 931 | 927 | } |
| 932 | 928 | } finally { |
| 933 | - typingIndicator.hide(); | |
| 934 | - | |
| 935 | 929 | is_group_generating = false; |
| 936 | 930 | setSendButtonState(false); |
| 937 | 931 | setCharacterId(undefined); |
| @@ -27,24 +27,45 @@ export async function hideLoader() { | ||
| 27 | 27 | } |
| 28 | 28 | |
| 29 | 29 | return new Promise((resolve) => { |
| 30 | - // Spinner blurs/fades out | |
| 30 | + const spinner = $('#load-spinner'); | |
| 31 | - $('#load-spinner').on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function () { | |
| 31 | + if (!spinner.length) { | |
| 32 | + console.warn('Spinner element not found, skipping animation'); | |
| 33 | + cleanup(); | |
| 34 | + return; | |
| 35 | + } | |
| 36 | + | |
| 37 | + // Check if transitions are enabled | |
| 38 | + const transitionDuration = spinner[0] ? getComputedStyle(spinner[0]).transitionDuration : '0s'; | |
| 39 | + const hasTransitions = parseFloat(transitionDuration) > 0; | |
| 40 | + | |
| 41 | + if (hasTransitions) { | |
| 42 | + Promise.race([ | |
| 43 | + new Promise((r) => setTimeout(r, 500)), // Fallback timeout | |
| 44 | + new Promise((r) => spinner.one('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', r)), | |
| 45 | + ]).finally(cleanup); | |
| 46 | + } else { | |
| 47 | + cleanup(); | |
| 48 | + } | |
| 49 | + | |
| 50 | + function cleanup() { | |
| 32 | 51 | $('#loader').remove(); |
| 33 | 52 | // Yoink preloader entirely; it only exists to cover up unstyled content while loading JS |
| 34 | 53 | // If it's present, we remove it once and then it's gone. |
| 35 | 54 | yoinkPreloader(); |
| 36 | 55 | |
| 37 | 56 | loaderPopup.complete(POPUP_RESULT.AFFIRMATIVE).then(() => { |
| 38 | - loaderPopup = null; | |
| 57 | + .catch((err) => console.error('Error completing loaderPopup:', err)) | |
| 39 | - resolve(); | |
| 58 | + .finally(() => { | |
| 40 | - }); | |
| 59 | + loaderPopup = null; | |
| 41 | - }); | |
| 60 | + resolve(); | |
| 61 | + }); | |
| 62 | + } | |
| 42 | 63 | |
| 43 | - $('#load-spinner') | |
| 64 | + // Apply the styles | |
| 44 | 65 | spinner.css({ |
| 45 | 66 | 'filter': 'blur(15px)', |
| 46 | 67 | 'opacity': '0', |
| 47 | 68 | }); |
| 48 | 69 | }); |
| 49 | 70 | } |
| 50 | 71 | |
| @@ -258,8 +258,8 @@ const default_settings = { | ||
| 258 | 258 | ai21_model: 'jamba-1.5-large', |
| 259 | 259 | mistralai_model: 'mistral-large-latest', |
| 260 | 260 | cohere_model: 'command-r-plus', |
| 261 | 261 | perplexity_model: 'llama-3.1-70bsonar-instructpro', |
| 262 | 262 | groq_model: 'llama-3.13-70b-versatile', |
| 263 | 263 | nanogpt_model: 'gpt-4o-mini', |
| 264 | 264 | zerooneai_model: 'yi-large', |
| 265 | 265 | blockentropy_model: 'be-70b-base-llama3.1', |
| @@ -298,7 +298,7 @@ const default_settings = { | ||
| 298 | 298 | names_behavior: character_names_behavior.DEFAULT, |
| 299 | 299 | continue_postfix: continue_postfix_types.SPACE, |
| 300 | 300 | custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, |
| 301 | 301 | show_thoughts: falsetrue, |
| 302 | 302 | seed: -1, |
| 303 | 303 | n: 1, |
| 304 | 304 | }; |
| @@ -337,7 +337,7 @@ const oai_settings = { | ||
| 337 | 337 | ai21_model: 'jamba-1.5-large', |
| 338 | 338 | mistralai_model: 'mistral-large-latest', |
| 339 | 339 | cohere_model: 'command-r-plus', |
| 340 | 340 | perplexity_model: 'llama-3.1-70bsonar-instructpro', |
| 341 | 341 | groq_model: 'llama-3.1-70b-versatile', |
| 342 | 342 | nanogpt_model: 'gpt-4o-mini', |
| 343 | 343 | zerooneai_model: 'yi-large', |
| @@ -377,7 +377,7 @@ const oai_settings = { | ||
| 377 | 377 | names_behavior: character_names_behavior.DEFAULT, |
| 378 | 378 | continue_postfix: continue_postfix_types.SPACE, |
| 379 | 379 | custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, |
| 380 | 380 | show_thoughts: falsetrue, |
| 381 | 381 | seed: -1, |
| 382 | 382 | n: 1, |
| 383 | 383 | }; |
| @@ -1869,7 +1869,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 1869 | 1869 | const isQuiet = type === 'quiet'; |
| 1870 | 1870 | const isImpersonate = type === 'impersonate'; |
| 1871 | 1871 | const isContinue = type === 'continue'; |
| 1872 | 1872 | const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isGoogle && oai_settings.google_model.includes('bison')) && !(isOAI && (oai_settings.openai_model.startsWith('o1-') || oai_settings.openai_model.startsWith('o3'))); |
| 1873 | 1873 | const useLogprobs = !!power_user.request_token_probabilities; |
| 1874 | 1874 | const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom); |
| 1875 | 1875 | |
| @@ -1913,7 +1913,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 1913 | 1913 | 'user_name': name1, |
| 1914 | 1914 | 'char_name': name2, |
| 1915 | 1915 | 'group_names': getGroupNames(), |
| 1916 | 1916 | 'show_thoughtsinclude_reasoning': Boolean(oai_settings.show_thoughts), |
| 1917 | 1917 | }; |
| 1918 | 1918 | |
| 1919 | 1919 | // Empty array will produce a validation error |
| @@ -2050,7 +2050,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 2050 | 2050 | await ToolManager.registerFunctionToolsOpenAI(generate_data); |
| 2051 | 2051 | } |
| 2052 | 2052 | |
| 2053 | 2053 | if (isOAI && (oai_settings.openai_model.startsWith('o1-') || oai_settings.openai_model.startsWith('o3'))) { |
| 2054 | 2054 | generate_data.messages.forEach((msg) => { |
| 2055 | 2055 | if (msg.role === 'system') { |
| 2056 | 2056 | msg.role = 'user'; |
| @@ -2151,7 +2151,7 @@ function getStreamingReply(data, state) { | ||
| 2151 | 2151 | return data?.delta?.text || ''; |
| 2152 | 2152 | } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { |
| 2153 | 2153 | if (oai_settings.show_thoughts) { |
| 2154 | 2154 | state.reasoning += (data?.candidates?.[0]?.content?.parts?.filter(x => x.thought)?.map(x => x.text)?.[0] || ''); |
| 2155 | 2155 | } |
| 2156 | 2156 | return data?.candidates?.[0]?.content?.parts?.filter(x => !x.thought)?.map(x => x.text)?.[0] || ''; |
| 2157 | 2157 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { |
| @@ -2166,7 +2166,7 @@ function getStreamingReply(data, state) { | ||
| 2166 | 2166 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || ''); |
| 2167 | 2167 | } |
| 2168 | 2168 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; |
| 2169 | 2169 | } else { |
| 2170 | 2170 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; |
| 2171 | 2171 | } |
| 2172 | 2172 | } |
| @@ -4027,7 +4027,7 @@ function getMaxContextOpenAI(value) { | ||
| 4027 | 4027 | if (oai_settings.max_context_unlocked) { |
| 4028 | 4028 | return unlocked_max; |
| 4029 | 4029 | } |
| 4030 | 4030 | else if (value.startsWith('o1-') || value.startsWith('o3')) { |
| 4031 | 4031 | return max_128k; |
| 4032 | 4032 | } |
| 4033 | 4033 | else if (value.includes('chatgpt-4o-latest') || value.includes('gpt-4-turbo') || value.includes('gpt-4o') || value.includes('gpt-4-1106') || value.includes('gpt-4-0125') || value.includes('gpt-4-vision')) { |
| @@ -4380,28 +4380,19 @@ async function onModelChange() { | ||
| 4380 | 4380 | if (oai_settings.max_context_unlocked) { |
| 4381 | 4381 | $('#openai_max_context').attr('max', unlocked_max); |
| 4382 | 4382 | } |
| 4383 | + else if (['sonar', 'sonar-reasoning'].includes(oai_settings.perplexity_model)) { | |
| 4384 | + $('#openai_max_context').attr('max', 127000); | |
| 4385 | + } | |
| 4386 | + else if (['sonar-pro'].includes(oai_settings.perplexity_model)) { | |
| 4387 | + $('#openai_max_context').attr('max', 200000); | |
| 4388 | + } | |
| 4383 | 4389 | else if (oai_settings.perplexity_model.includes('llama-3.1')) { |
| 4384 | 4390 | const isOnline = oai_settings.perplexity_model.includes('online'); |
| 4385 | 4391 | const contextSize = isOnline ? 128 * 1024 - 4000 : 128 * 1024; |
| 4386 | 4392 | $('#openai_max_context').attr('max', contextSize); |
| 4387 | 4393 | } |
| 4388 | - else if (['llama-3-sonar-small-32k-chat', 'llama-3-sonar-large-32k-chat'].includes(oai_settings.perplexity_model)) { | |
| 4389 | - $('#openai_max_context').attr('max', max_32k); | |
| 4390 | - } | |
| 4391 | - else if (['llama-3-sonar-small-32k-online', 'llama-3-sonar-large-32k-online'].includes(oai_settings.perplexity_model)) { | |
| 4392 | - $('#openai_max_context').attr('max', 28000); | |
| 4393 | - } | |
| 4394 | - else if (['sonar-small-chat', 'sonar-medium-chat', 'codellama-70b-instruct', 'mistral-7b-instruct', 'mixtral-8x7b-instruct', 'mixtral-8x22b-instruct'].includes(oai_settings.perplexity_model)) { | |
| 4395 | - $('#openai_max_context').attr('max', max_16k); | |
| 4396 | - } | |
| 4397 | - else if (['llama-3-8b-instruct', 'llama-3-70b-instruct'].includes(oai_settings.perplexity_model)) { | |
| 4398 | - $('#openai_max_context').attr('max', max_8k); | |
| 4399 | - } | |
| 4400 | - else if (['sonar-small-online', 'sonar-medium-online'].includes(oai_settings.perplexity_model)) { | |
| 4401 | - $('#openai_max_context').attr('max', 12000); | |
| 4402 | - } | |
| 4403 | 4394 | else { |
| 4404 | 4395 | $('#openai_max_context').attr('max', max_4kmax_128k); |
| 4405 | 4396 | } |
| 4406 | 4397 | oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); |
| 4407 | 4398 | $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); |
| @@ -4412,24 +4403,30 @@ async function onModelChange() { | ||
| 4412 | 4403 | if (oai_settings.chat_completion_source == chat_completion_sources.GROQ) { |
| 4413 | 4404 | if (oai_settings.max_context_unlocked) { |
| 4414 | 4405 | $('#openai_max_context').attr('max', unlocked_max); |
| 4415 | - } | |
| 4406 | + } else if (oai_settings.groq_model.includes('gemma2-9b-it')) { | |
| 4416 | - else if (oai_settings.groq_model.includes('llama-3.2') && oai_settings.groq_model.includes('-preview')) { | |
| 4417 | 4407 | $('#openai_max_context').attr('max', max_8k); |
| 4418 | - } | |
| 4408 | + } else if (oai_settings.groq_model.includes('llama-3.3-70b-versatile')) { | |
| 4419 | - else if (oai_settings.groq_model.includes('llama-3.3') || oai_settings.groq_model.includes('llama-3.2') || oai_settings.groq_model.includes('llama-3.1')) { | |
| 4420 | 4409 | $('#openai_max_context').attr('max', max_128k); |
| 4421 | - } | |
| 4410 | + } else if (oai_settings.groq_model.includes('llama-3.1-8b-instant')) { | |
| 4422 | - else if (oai_settings.groq_model.includes('llama3-groq')) { | |
| 4411 | + $('#openai_max_context').attr('max', max_128k); | |
| 4412 | + } else if (oai_settings.groq_model.includes('llama3-70b-8192')) { | |
| 4423 | 4413 | $('#openai_max_context').attr('max', max_8k); |
| 4424 | - } | |
| 4414 | + } else if (oai_settings.groq_model.includes('llama3-8b-8192')) { | |
| 4425 | - else if (['llama3-8b-8192', 'llama3-70b-8192', 'gemma-7b-it', 'gemma2-9b-it'].includes(oai_settings.groq_model)) { | |
| 4426 | 4415 | $('#openai_max_context').attr('max', max_8k); |
| 4427 | - } | |
| 4416 | + } else if (oai_settings.groq_model.includes('mixtral-8x7b-32768')) { | |
| 4428 | - else if (['mixtral-8x7b-32768'].includes(oai_settings.groq_model)) { | |
| 4429 | 4417 | $('#openai_max_context').attr('max', max_32k); |
| 4430 | - } | |
| 4418 | + } else if (oai_settings.groq_model.includes('deepseek-r1-distill-llama-70b')) { | |
| 4431 | - else { | |
| 4419 | + $('#openai_max_context').attr('max', max_128k); | |
| 4432 | - $('#openai_max_context').attr('max', max_4k); | |
| 4420 | + } else if (oai_settings.groq_model.includes('llama-3.3-70b-specdec')) { | |
| 4421 | + $('#openai_max_context').attr('max', max_8k); | |
| 4422 | + } else if (oai_settings.groq_model.includes('llama-3.2-1b-preview')) { | |
| 4423 | + $('#openai_max_context').attr('max', max_128k); | |
| 4424 | + } else if (oai_settings.groq_model.includes('llama-3.2-3b-preview')) { | |
| 4425 | + $('#openai_max_context').attr('max', max_128k); | |
| 4426 | + } else if (oai_settings.groq_model.includes('llama-3.2-11b-vision-preview')) { | |
| 4427 | + $('#openai_max_context').attr('max', max_128k); | |
| 4428 | + } else if (oai_settings.groq_model.includes('llama-3.2-90b-vision-preview')) { | |
| 4429 | + $('#openai_max_context').attr('max', max_128k); | |
| 4433 | 4430 | } |
| 4434 | 4431 | oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); |
| 4435 | 4432 | $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); |
| @@ -254,6 +254,7 @@ let power_user = { | ||
| 254 | 254 | }, |
| 255 | 255 | |
| 256 | 256 | reasoning: { |
| 257 | + auto_parse: false, | |
| 257 | 258 | add_to_prompts: false, |
| 258 | 259 | prefix: '<think>\n', |
| 259 | 260 | suffix: '\n</think>', |
| @@ -2917,6 +2918,46 @@ export function flushEphemeralStoppingStrings() { | ||
| 2917 | 2918 | } |
| 2918 | 2919 | |
| 2919 | 2920 | /** |
| 2921 | + * Checks if the generated text should be filtered based on the auto-swipe settings. | |
| 2922 | + * @param {string} text The text to check | |
| 2923 | + * @returns {boolean} If the generated text should be filtered | |
| 2924 | + */ | |
| 2925 | +export function generatedTextFiltered(text) { | |
| 2926 | + /** | |
| 2927 | + * Checks if the given text contains any of the blacklisted words. | |
| 2928 | + * @param {string} text The text to check | |
| 2929 | + * @param {string[]} blacklist The list of blacklisted words | |
| 2930 | + * @param {number} threshold The number of blacklisted words that need to be present to trigger the check | |
| 2931 | + * @returns {boolean} Whether the text contains blacklisted words | |
| 2932 | + */ | |
| 2933 | + function containsBlacklistedWords(text, blacklist, threshold) { | |
| 2934 | + const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi'); | |
| 2935 | + const matches = text.match(regex) || []; | |
| 2936 | + return matches.length >= threshold; | |
| 2937 | + } | |
| 2938 | + | |
| 2939 | + // Make sure a generated text is non-empty | |
| 2940 | + // Otherwise we might get in a loop with a broken API | |
| 2941 | + text = text.trim(); | |
| 2942 | + if (text.length > 0) { | |
| 2943 | + if (power_user.auto_swipe_minimum_length) { | |
| 2944 | + if (text.length < power_user.auto_swipe_minimum_length) { | |
| 2945 | + console.log('Generated text size too small'); | |
| 2946 | + return true; | |
| 2947 | + } | |
| 2948 | + } | |
| 2949 | + if (power_user.auto_swipe_blacklist.length && power_user.auto_swipe_blacklist_threshold) { | |
| 2950 | + if (containsBlacklistedWords(text, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) { | |
| 2951 | + console.log('Generated text has blacklisted words'); | |
| 2952 | + return true; | |
| 2953 | + } | |
| 2954 | + } | |
| 2955 | + } | |
| 2956 | + | |
| 2957 | + return false; | |
| 2958 | +} | |
| 2959 | + | |
| 2960 | +/** | |
| 2920 | 2961 | * Gets the custom stopping strings from the power user settings. |
| 2921 | 2962 | * @param {number | undefined} limit Number of strings to return. If 0 or undefined, returns all strings. |
| 2922 | 2963 | * @returns {string[]} An array of custom stopping strings |
| @@ -3887,9 +3928,9 @@ $(document).ready(() => { | ||
| 3887 | 3928 | helpString: 'Start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.', |
| 3888 | 3929 | })); |
| 3889 | 3930 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 3890 | 3931 | name: 'delmodedel', |
| 3891 | 3932 | callback: doDelMode, |
| 3892 | 3933 | aliases: ['deldelete', 'delmode'], |
| 3893 | 3934 | unnamedArgumentList: [ |
| 3894 | 3935 | new SlashCommandArgument( |
| 3895 | 3936 | 'optional number', [ARGUMENT_TYPE.NUMBER], false, |
| @@ -4072,4 +4113,45 @@ $(document).ready(() => { | ||
| 4072 | 4113 | ], |
| 4073 | 4114 | helpString: 'activates a movingUI preset by name', |
| 4074 | 4115 | })); |
| 4116 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 4117 | + name: 'stop-strings', | |
| 4118 | + aliases: ['stopping-strings', 'custom-stopping-strings', 'custom-stop-strings'], | |
| 4119 | + helpString: ` | |
| 4120 | + <div> | |
| 4121 | + Sets a list of custom stopping strings. Gets the list if no value is provided. | |
| 4122 | + </div> | |
| 4123 | + <div> | |
| 4124 | + <strong>Examples:</strong> | |
| 4125 | + </div> | |
| 4126 | + <ul> | |
| 4127 | + <li>Value must be a JSON-serialized array: <pre><code class="language-stscript">/stop-strings ["goodbye", "farewell"]</code></pre></li> | |
| 4128 | + <li>Pipe characters must be escaped with a backslash: <pre><code class="language-stscript">/stop-strings ["left\\|right"]</code></pre></li> | |
| 4129 | + </ul> | |
| 4130 | + `, | |
| 4131 | + returns: ARGUMENT_TYPE.LIST, | |
| 4132 | + unnamedArgumentList: [ | |
| 4133 | + SlashCommandArgument.fromProps({ | |
| 4134 | + description: 'list of strings', | |
| 4135 | + typeList: [ARGUMENT_TYPE.LIST], | |
| 4136 | + acceptsMultiple: false, | |
| 4137 | + isRequired: false, | |
| 4138 | + }), | |
| 4139 | + ], | |
| 4140 | + callback: (_, value) => { | |
| 4141 | + if (String(value ?? '').trim()) { | |
| 4142 | + const parsedValue = ((x) => { try { return JSON.parse(x.toString()); } catch { return null; } })(value); | |
| 4143 | + if (!parsedValue || !Array.isArray(parsedValue)) { | |
| 4144 | + throw new Error('Invalid list format. The value must be a JSON-serialized array of strings.'); | |
| 4145 | + } | |
| 4146 | + parsedValue.forEach((item, index) => { | |
| 4147 | + parsedValue[index] = String(item); | |
| 4148 | + }); | |
| 4149 | + power_user.custom_stopping_strings = JSON.stringify(parsedValue); | |
| 4150 | + $('#custom_stopping_strings').val(power_user.custom_stopping_strings); | |
| 4151 | + saveSettingsDebounced(); | |
| 4152 | + } | |
| 4153 | + | |
| 4154 | + return power_user.custom_stopping_strings; | |
| 4155 | + }, | |
| 4156 | + })); | |
| 4075 | 4157 | }); |
| @@ -586,6 +586,7 @@ class PresetManager { | ||
| 586 | 586 | 'tabby_model', |
| 587 | 587 | 'derived', |
| 588 | 588 | 'generic_model', |
| 589 | + 'include_reasoning', | |
| 589 | 590 | ]; |
| 590 | 591 | const settings = Object.assign({}, getSettingsByApiId(this.apiId)); |
| 591 | 592 | |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | import { chat, closeMessageEditor, event_types, eventSource, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js'; |
| 2 | +import { getRegexedString, regex_placement } from './extensions/regex/engine.js'; | |
| 2 | 3 | import { t } from './i18n.js'; |
| 3 | 4 | import { MacrosParser } from './macros.js'; |
| 4 | 5 | import { Popup } from './popup.js'; |
| @@ -7,7 +8,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js'; | ||
| 7 | 8 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; |
| 8 | 9 | import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 9 | 10 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 10 | 11 | import { copyText, escapeRegex, isFalseBoolean } from './utils.js'; |
| 11 | 12 | |
| 12 | 13 | /** |
| 13 | 14 | * Gets a message from a jQuery element. |
| @@ -49,11 +50,12 @@ export class PromptReasoning { | ||
| 49 | 50 | * Add reasoning to a message according to the power user settings. |
| 50 | 51 | * @param {string} content Message content |
| 51 | 52 | * @param {string} reasoning Message reasoning |
| 53 | + * @param {boolean} isPrefix Whether this is the last message prefix | |
| 52 | 54 | * @returns {string} Message content with reasoning |
| 53 | 55 | */ |
| 54 | 56 | addToMessage(content, reasoning, isPrefix) { |
| 55 | 57 | // Disabled or reached limit of additions |
| 56 | 58 | if (!isPrefix && (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions)) { |
| 57 | 59 | return content; |
| 58 | 60 | } |
| 59 | 61 | |
| @@ -70,6 +72,11 @@ export class PromptReasoning { | ||
| 70 | 72 | const separator = substituteParams(power_user.reasoning.separator || ''); |
| 71 | 73 | const suffix = substituteParams(power_user.reasoning.suffix || ''); |
| 72 | 74 | |
| 75 | + // Combine parts with reasoning only | |
| 76 | + if (isPrefix && !content) { | |
| 77 | + return `${prefix}${reasoning}`; | |
| 78 | + } | |
| 79 | + | |
| 73 | 80 | // Combine parts with reasoning and content |
| 74 | 81 | return `${prefix}${reasoning}${suffix}${separator}${content}`; |
| 75 | 82 | } |
| @@ -105,11 +112,18 @@ function loadReasoningSettings() { | ||
| 105 | 112 | power_user.reasoning.max_additions = Number($(this).val()); |
| 106 | 113 | saveSettingsDebounced(); |
| 107 | 114 | }); |
| 115 | + | |
| 116 | + $('#reasoning_auto_parse').prop('checked', power_user.reasoning.auto_parse); | |
| 117 | + $('#reasoning_auto_parse').on('change', function () { | |
| 118 | + power_user.reasoning.auto_parse = !!$(this).prop('checked'); | |
| 119 | + saveSettingsDebounced(); | |
| 120 | + }); | |
| 108 | 121 | } |
| 109 | 122 | |
| 110 | 123 | function registerReasoningSlashCommands() { |
| 111 | 124 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 112 | 125 | name: 'reasoning-get', |
| 126 | + aliases: ['get-reasoning'], | |
| 113 | 127 | returns: ARGUMENT_TYPE.STRING, |
| 114 | 128 | helpString: t`Get the contents of a reasoning block of a message. Returns an empty string if the message does not have a reasoning block.`, |
| 115 | 129 | unnamedArgumentList: [ |
| @@ -129,6 +143,7 @@ function registerReasoningSlashCommands() { | ||
| 129 | 143 | |
| 130 | 144 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 131 | 145 | name: 'reasoning-set', |
| 146 | + aliases: ['set-reasoning'], | |
| 132 | 147 | returns: ARGUMENT_TYPE.STRING, |
| 133 | 148 | helpString: t`Set the reasoning block of a message. Returns the reasoning block content.`, |
| 134 | 149 | namedArgumentList: [ |
| @@ -146,7 +161,7 @@ function registerReasoningSlashCommands() { | ||
| 146 | 161 | }), |
| 147 | 162 | ], |
| 148 | 163 | callback: async (args, value) => { |
| 149 | 164 | const messageId = !isNaN(Number(args[0].at)) ? Number(args[0].at) : chat.length - 1; |
| 150 | 165 | const message = chat[messageId]; |
| 151 | 166 | if (!message?.extra) { |
| 152 | 167 | return ''; |
| @@ -160,6 +175,50 @@ function registerReasoningSlashCommands() { | ||
| 160 | 175 | return message.extra.reasoning; |
| 161 | 176 | }, |
| 162 | 177 | })); |
| 178 | + | |
| 179 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 180 | + name: 'reasoning-parse', | |
| 181 | + aliases: ['parse-reasoning'], | |
| 182 | + returns: 'reasoning string', | |
| 183 | + helpString: t`Extracts the reasoning block from a string using the Reasoning Formatting settings.`, | |
| 184 | + namedArgumentList: [ | |
| 185 | + SlashCommandNamedArgument.fromProps({ | |
| 186 | + name: 'regex', | |
| 187 | + description: 'Whether to apply regex scripts to the reasoning content.', | |
| 188 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 189 | + defaultValue: 'true', | |
| 190 | + isRequired: false, | |
| 191 | + enumProvider: commonEnumProviders.boolean('trueFalse'), | |
| 192 | + }), | |
| 193 | + ], | |
| 194 | + unnamedArgumentList: [ | |
| 195 | + SlashCommandArgument.fromProps({ | |
| 196 | + description: 'input string', | |
| 197 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 198 | + }), | |
| 199 | + ], | |
| 200 | + callback: (args, value) => { | |
| 201 | + if (!value) { | |
| 202 | + return ''; | |
| 203 | + } | |
| 204 | + | |
| 205 | + if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) { | |
| 206 | + toastr.warning(t`Both prefix and suffix must be set in the Reasoning Formatting settings.`); | |
| 207 | + return String(value); | |
| 208 | + } | |
| 209 | + | |
| 210 | + const parsedReasoning = parseReasoningFromString(String(value)); | |
| 211 | + | |
| 212 | + if (!parsedReasoning) { | |
| 213 | + return ''; | |
| 214 | + } | |
| 215 | + | |
| 216 | + const applyRegex = !isFalseBoolean(String(args.regex ?? '')); | |
| 217 | + return applyRegex | |
| 218 | + ? getRegexedString(parsedReasoning.reasoning, regex_placement.REASONING) | |
| 219 | + : parsedReasoning.reasoning; | |
| 220 | + }, | |
| 221 | + })); | |
| 163 | 222 | } |
| 164 | 223 | |
| 165 | 224 | function registerReasoningMacros() { |
| @@ -168,7 +227,7 @@ function registerReasoningMacros() { | ||
| 168 | 227 | MacrosParser.registerMacro('reasoningSeparator', () => power_user.reasoning.separator, t`Reasoning Separator`); |
| 169 | 228 | } |
| 170 | 229 | |
| 171 | 230 | function setReasoningEventHandlers() { |
| 172 | 231 | $(document).on('click', '.mes_reasoning_copy', (e) => { |
| 173 | 232 | e.stopPropagation(); |
| 174 | 233 | e.preventDefault(); |
| @@ -224,7 +283,7 @@ function setReasoningEventHandlers(){ | ||
| 224 | 283 | } |
| 225 | 284 | |
| 226 | 285 | const textarea = messageBlock.find('.reasoning_edit_textarea'); |
| 227 | 286 | const reasoning = getRegexedString(String(textarea.val()), regex_placement.REASONING, { isEdit: true }); |
| 228 | 287 | message.extra.reasoning = reasoning; |
| 229 | 288 | await saveChatConditional(); |
| 230 | 289 | updateMessageBlock(messageId, message); |
| @@ -289,9 +348,101 @@ function setReasoningEventHandlers(){ | ||
| 289 | 348 | }); |
| 290 | 349 | } |
| 291 | 350 | |
| 351 | +/** | |
| 352 | + * Parses reasoning from a string using the power user reasoning settings. | |
| 353 | + * @typedef {Object} ParsedReasoning | |
| 354 | + * @property {string} reasoning Reasoning block | |
| 355 | + * @property {string} content Message content | |
| 356 | + * @param {string} str Content of the message | |
| 357 | + * @returns {ParsedReasoning|null} Parsed reasoning block and message content | |
| 358 | + */ | |
| 359 | +function parseReasoningFromString(str) { | |
| 360 | + // Both prefix and suffix must be defined | |
| 361 | + if (!power_user.reasoning.prefix || !power_user.reasoning.suffix) { | |
| 362 | + return null; | |
| 363 | + } | |
| 364 | + | |
| 365 | + try { | |
| 366 | + const regex = new RegExp(`${escapeRegex(power_user.reasoning.prefix)}(.*?)${escapeRegex(power_user.reasoning.suffix)}`, 's'); | |
| 367 | + | |
| 368 | + let didReplace = false; | |
| 369 | + let reasoning = ''; | |
| 370 | + let content = String(str).replace(regex, (_match, captureGroup) => { | |
| 371 | + didReplace = true; | |
| 372 | + reasoning = captureGroup; | |
| 373 | + return ''; | |
| 374 | + }); | |
| 375 | + | |
| 376 | + if (didReplace && power_user.trim_spaces) { | |
| 377 | + reasoning = reasoning.trim(); | |
| 378 | + content = content.trim(); | |
| 379 | + } | |
| 380 | + | |
| 381 | + return { reasoning, content }; | |
| 382 | + } catch (error) { | |
| 383 | + console.error('[Reasoning] Error parsing reasoning block', error); | |
| 384 | + return null; | |
| 385 | + } | |
| 386 | +} | |
| 387 | + | |
| 388 | +function registerReasoningAppEvents() { | |
| 389 | + eventSource.makeFirst(event_types.MESSAGE_RECEIVED, (/** @type {number} */ idx) => { | |
| 390 | + if (!power_user.reasoning.auto_parse) { | |
| 391 | + return; | |
| 392 | + } | |
| 393 | + | |
| 394 | + console.debug('[Reasoning] Auto-parsing reasoning block for message', idx); | |
| 395 | + const message = chat[idx]; | |
| 396 | + | |
| 397 | + if (!message) { | |
| 398 | + console.warn('[Reasoning] Message not found', idx); | |
| 399 | + return null; | |
| 400 | + } | |
| 401 | + | |
| 402 | + if (!message.mes || message.mes === '...') { | |
| 403 | + console.debug('[Reasoning] Message content is empty or a placeholder', idx); | |
| 404 | + return null; | |
| 405 | + } | |
| 406 | + | |
| 407 | + const parsedReasoning = parseReasoningFromString(message.mes); | |
| 408 | + | |
| 409 | + // No reasoning block found | |
| 410 | + if (!parsedReasoning) { | |
| 411 | + return; | |
| 412 | + } | |
| 413 | + | |
| 414 | + // Make sure the message has an extra object | |
| 415 | + if (!message.extra || typeof message.extra !== 'object') { | |
| 416 | + message.extra = {}; | |
| 417 | + } | |
| 418 | + | |
| 419 | + const contentUpdated = !!parsedReasoning.reasoning || parsedReasoning.content !== message.mes; | |
| 420 | + | |
| 421 | + // If reasoning was found, add it to the message | |
| 422 | + if (parsedReasoning.reasoning) { | |
| 423 | + message.extra.reasoning = getRegexedString(parsedReasoning.reasoning, regex_placement.REASONING); | |
| 424 | + } | |
| 425 | + | |
| 426 | + // Update the message text if it was changed | |
| 427 | + if (parsedReasoning.content !== message.mes) { | |
| 428 | + message.mes = parsedReasoning.content; | |
| 429 | + } | |
| 430 | + | |
| 431 | + // Find if a message already exists in DOM and must be updated | |
| 432 | + if (contentUpdated) { | |
| 433 | + const messageRendered = document.querySelector(`.mes[mesid="${idx}"]`) !== null; | |
| 434 | + if (messageRendered) { | |
| 435 | + console.debug('[Reasoning] Updating message block', idx); | |
| 436 | + updateMessageBlock(idx, message); | |
| 437 | + } | |
| 438 | + } | |
| 439 | + }); | |
| 440 | +} | |
| 441 | + | |
| 292 | 442 | export function initReasoning() { |
| 293 | 443 | loadReasoningSettings(); |
| 294 | 444 | setReasoningEventHandlers(); |
| 295 | 445 | registerReasoningSlashCommands(); |
| 296 | 446 | registerReasoningMacros(); |
| 447 | + registerReasoningAppEvents(); | |
| 297 | 448 | } |
| @@ -59,7 +59,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat | ||
| 59 | 59 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 60 | 60 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 61 | 61 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; |
| 62 | 62 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, regexFromString, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| 63 | 63 | import { registerVariableCommands, resolveVariable } from './variables.js'; |
| 64 | 64 | import { background_settings } from './backgrounds.js'; |
| 65 | 65 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| @@ -235,7 +235,6 @@ export function initDefaultSlashCommands() { | ||
| 235 | 235 | description: 'Character name - or unique character identifier (avatar key)', |
| 236 | 236 | typeList: [ARGUMENT_TYPE.STRING], |
| 237 | 237 | enumProvider: commonEnumProviders.characters('character'), |
| 238 | - forceEnum: false, | |
| 239 | 238 | }), |
| 240 | 239 | ], |
| 241 | 240 | helpString: ` |
| @@ -274,7 +273,6 @@ export function initDefaultSlashCommands() { | ||
| 274 | 273 | typeList: [ARGUMENT_TYPE.STRING], |
| 275 | 274 | isRequired: true, |
| 276 | 275 | enumProvider: commonEnumProviders.characters('character'), |
| 277 | - forceEnum: false, | |
| 278 | 276 | }), |
| 279 | 277 | SlashCommandNamedArgument.fromProps({ |
| 280 | 278 | name: 'avatar', |
| @@ -518,7 +516,6 @@ export function initDefaultSlashCommands() { | ||
| 518 | 516 | typeList: [ARGUMENT_TYPE.STRING], |
| 519 | 517 | isRequired: true, |
| 520 | 518 | enumProvider: commonEnumProviders.characters('all'), |
| 521 | - forceEnum: true, | |
| 522 | 519 | }), |
| 523 | 520 | ], |
| 524 | 521 | helpString: 'Opens up a chat with the character or group by its name', |
| @@ -734,6 +731,57 @@ export function initDefaultSlashCommands() { | ||
| 734 | 731 | helpString: 'Unhides a message from the prompt.', |
| 735 | 732 | })); |
| 736 | 733 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 734 | + name: 'member-get', | |
| 735 | + aliases: ['getmember', 'memberget'], | |
| 736 | + callback: (async ({ field = 'name' }, arg) => { | |
| 737 | + if (!selected_group) { | |
| 738 | + toastr.warning('Cannot run /member-get command outside of a group chat.'); | |
| 739 | + return ''; | |
| 740 | + } | |
| 741 | + if (field === '') { | |
| 742 | + toastr.warning('\'/member-get field=\' argument required!'); | |
| 743 | + return ''; | |
| 744 | + } | |
| 745 | + field = field.toString(); | |
| 746 | + arg = arg.toString(); | |
| 747 | + if (!['name', 'index', 'id', 'avatar'].includes(field)) { | |
| 748 | + toastr.warning('\'/member-get field=\' argument required!'); | |
| 749 | + return ''; | |
| 750 | + } | |
| 751 | + const isId = !isNaN(parseInt(arg)); | |
| 752 | + const groupMember = findGroupMemberId(arg, true); | |
| 753 | + if (!groupMember) { | |
| 754 | + toastr.warn(`No group member found using ${isId ? 'id' : 'string'} ${arg}`); | |
| 755 | + return ''; | |
| 756 | + } | |
| 757 | + return groupMember[field]; | |
| 758 | + }), | |
| 759 | + namedArgumentList: [ | |
| 760 | + SlashCommandNamedArgument.fromProps({ | |
| 761 | + name: 'field', | |
| 762 | + description: 'Whether to retrieve the name, index, id, or avatar.', | |
| 763 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 764 | + isRequired: true, | |
| 765 | + defaultValue: 'name', | |
| 766 | + enumList: [ | |
| 767 | + new SlashCommandEnumValue('name', 'Character name'), | |
| 768 | + new SlashCommandEnumValue('index', 'Group member index'), | |
| 769 | + new SlashCommandEnumValue('avatar', 'Character avatar'), | |
| 770 | + new SlashCommandEnumValue('id', 'Character index'), | |
| 771 | + ], | |
| 772 | + }), | |
| 773 | + ], | |
| 774 | + unnamedArgumentList: [ | |
| 775 | + SlashCommandArgument.fromProps({ | |
| 776 | + description: 'member index (starts with 0), name, or avatar', | |
| 777 | + typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], | |
| 778 | + isRequired: true, | |
| 779 | + enumProvider: commonEnumProviders.groupMembers(), | |
| 780 | + }), | |
| 781 | + ], | |
| 782 | + helpString: 'Retrieves a group member\'s name, index, id, or avatar.', | |
| 783 | + })); | |
| 784 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 737 | 785 | name: 'member-disable', |
| 738 | 786 | callback: disableGroupMemberCallback, |
| 739 | 787 | aliases: ['disable', 'disablemember', 'memberdisable'], |
| @@ -843,7 +891,8 @@ export function initDefaultSlashCommands() { | ||
| 843 | 891 | helpString: 'Moves a group member down in the group chat list.', |
| 844 | 892 | })); |
| 845 | 893 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 846 | 894 | name: 'member-peek', |
| 895 | + aliases: ['peek', 'memberpeek', 'peekmember'], | |
| 847 | 896 | callback: peekCallback, |
| 848 | 897 | unnamedArgumentList: [ |
| 849 | 898 | SlashCommandArgument.fromProps({ |
| @@ -1009,7 +1058,6 @@ export function initDefaultSlashCommands() { | ||
| 1009 | 1058 | typeList: [ARGUMENT_TYPE.STRING], |
| 1010 | 1059 | defaultValue: 'System', |
| 1011 | 1060 | enumProvider: () => [...commonEnumProviders.characters('character')(), new SlashCommandEnumValue('System', null, enumTypes.enum, enumIcons.assistant)], |
| 1012 | - forceEnum: false, | |
| 1013 | 1061 | }), |
| 1014 | 1062 | new SlashCommandNamedArgument( |
| 1015 | 1063 | 'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER], false, |
| @@ -1903,7 +1951,7 @@ export function initDefaultSlashCommands() { | ||
| 1903 | 1951 | returns: 'uppercase string', |
| 1904 | 1952 | unnamedArgumentList: [ |
| 1905 | 1953 | new SlashCommandArgument( |
| 1906 | 1954 | 'stringtext to affect', [ARGUMENT_TYPE.STRING], true, false, |
| 1907 | 1955 | ), |
| 1908 | 1956 | ], |
| 1909 | 1957 | helpString: 'Converts the provided string to uppercase.', |
| @@ -1915,7 +1963,7 @@ export function initDefaultSlashCommands() { | ||
| 1915 | 1963 | returns: 'lowercase string', |
| 1916 | 1964 | unnamedArgumentList: [ |
| 1917 | 1965 | new SlashCommandArgument( |
| 1918 | 1966 | 'stringtext to affect', [ARGUMENT_TYPE.STRING], true, false, |
| 1919 | 1967 | ), |
| 1920 | 1968 | ], |
| 1921 | 1969 | helpString: 'Converts the provided string to lowercase.', |
| @@ -1935,7 +1983,7 @@ export function initDefaultSlashCommands() { | ||
| 1935 | 1983 | ], |
| 1936 | 1984 | unnamedArgumentList: [ |
| 1937 | 1985 | new SlashCommandArgument( |
| 1938 | 1986 | 'stringtext to affect', [ARGUMENT_TYPE.STRING], true, false, |
| 1939 | 1987 | ), |
| 1940 | 1988 | ], |
| 1941 | 1989 | helpString: ` |
| @@ -1999,6 +2047,62 @@ export function initDefaultSlashCommands() { | ||
| 1999 | 2047 | return ''; |
| 2000 | 2048 | }, |
| 2001 | 2049 | })); |
| 2050 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 2051 | + name: 'replace', | |
| 2052 | + aliases: ['re'], | |
| 2053 | + callback: (async ({ mode = 'literal', pattern, replacer = '' }, text) => { | |
| 2054 | + if (pattern === '') | |
| 2055 | + throw new Error('Argument of \'pattern=\' cannot be empty'); | |
| 2056 | + switch (mode) { | |
| 2057 | + case 'literal': | |
| 2058 | + return text.replaceAll(pattern, replacer); | |
| 2059 | + case 'regex': | |
| 2060 | + return text.replace(regexFromString(pattern), replacer); | |
| 2061 | + default: | |
| 2062 | + throw new Error('Invalid \'/replace mode=\' argument specified!'); | |
| 2063 | + } | |
| 2064 | + }), | |
| 2065 | + returns: 'replaced text', | |
| 2066 | + namedArgumentList: [ | |
| 2067 | + SlashCommandNamedArgument.fromProps({ | |
| 2068 | + name: 'mode', | |
| 2069 | + description: 'Replaces occurrence(s) of a pattern', | |
| 2070 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 2071 | + defaultValue: 'literal', | |
| 2072 | + enumList: ['literal', 'regex'], | |
| 2073 | + }), | |
| 2074 | + new SlashCommandNamedArgument( | |
| 2075 | + 'pattern', 'pattern to search with', [ARGUMENT_TYPE.STRING], true, false, | |
| 2076 | + ), | |
| 2077 | + new SlashCommandNamedArgument( | |
| 2078 | + 'replacer', 'replacement text for matches', [ARGUMENT_TYPE.STRING], false, false, '', | |
| 2079 | + ), | |
| 2080 | + ], | |
| 2081 | + unnamedArgumentList: [ | |
| 2082 | + new SlashCommandArgument( | |
| 2083 | + 'text to affect', [ARGUMENT_TYPE.STRING], true, false, | |
| 2084 | + ), | |
| 2085 | + ], | |
| 2086 | + helpString: ` | |
| 2087 | + <div> | |
| 2088 | + Replaces text within the provided string based on the pattern. | |
| 2089 | + </div> | |
| 2090 | + <div> | |
| 2091 | + If <code>mode</code> is <code>literal</code> (or omitted), <code>pattern</code> is a literal search string (case-sensitive).<br /> | |
| 2092 | + If <code>mode</code> is <code>regex</code>, <code>pattern</code> is parsed as an ECMAScript Regular Expression.<br /> | |
| 2093 | + The <code>replacer</code> replaces based on the <code>pattern</code> in the input text.<br /> | |
| 2094 | + If <code>replacer</code> is omitted, the replacement(s) will be an empty string.<br /> | |
| 2095 | + </div> | |
| 2096 | + <div> | |
| 2097 | + <strong>Example:</strong> | |
| 2098 | + <pre>/let x Blue house and blue car || </pre> | |
| 2099 | + <pre>/replace pattern="blue" {{var::x}} | /echo |/# Blue house and car ||</pre> | |
| 2100 | + <pre>/replace pattern="blue" replacer="red" {{var::x}} | /echo |/# Blue house and red car ||</pre> | |
| 2101 | + <pre>/replace mode=regex pattern="/blue/i" replacer="red" {{var::x}} | /echo |/# red house and blue car ||</pre> | |
| 2102 | + <pre>/replace mode=regex pattern="/blue/gi" replacer="red" {{var::x}} | /echo |/# red house and red car ||</pre> | |
| 2103 | + </div> | |
| 2104 | + `, | |
| 2105 | + })); | |
| 2002 | 2106 | |
| 2003 | 2107 | registerVariableCommands(); |
| 2004 | 2108 | } |
| @@ -2991,7 +3095,7 @@ function performGroupMemberAction(chid, action) { | ||
| 2991 | 3095 | |
| 2992 | 3096 | async function disableGroupMemberCallback(_, arg) { |
| 2993 | 3097 | if (!selected_group) { |
| 2994 | 3098 | toastr.warning('Cannot run /member-disable command outside of a group chat.'); |
| 2995 | 3099 | return ''; |
| 2996 | 3100 | } |
| 2997 | 3101 | |
| @@ -3008,7 +3112,7 @@ async function disableGroupMemberCallback(_, arg) { | ||
| 3008 | 3112 | |
| 3009 | 3113 | async function enableGroupMemberCallback(_, arg) { |
| 3010 | 3114 | if (!selected_group) { |
| 3011 | 3115 | toastr.warning('Cannot run /member-enable command outside of a group chat.'); |
| 3012 | 3116 | return ''; |
| 3013 | 3117 | } |
| 3014 | 3118 | |
| @@ -3025,7 +3129,7 @@ async function enableGroupMemberCallback(_, arg) { | ||
| 3025 | 3129 | |
| 3026 | 3130 | async function moveGroupMemberUpCallback(_, arg) { |
| 3027 | 3131 | if (!selected_group) { |
| 3028 | 3132 | toastr.warning('Cannot run /memberupmember-up command outside of a group chat.'); |
| 3029 | 3133 | return ''; |
| 3030 | 3134 | } |
| 3031 | 3135 | |
| @@ -3042,7 +3146,7 @@ async function moveGroupMemberUpCallback(_, arg) { | ||
| 3042 | 3146 | |
| 3043 | 3147 | async function moveGroupMemberDownCallback(_, arg) { |
| 3044 | 3148 | if (!selected_group) { |
| 3045 | 3149 | toastr.warning('Cannot run /memberdownmember-down command outside of a group chat.'); |
| 3046 | 3150 | return ''; |
| 3047 | 3151 | } |
| 3048 | 3152 | |
| @@ -3059,12 +3163,12 @@ async function moveGroupMemberDownCallback(_, arg) { | ||
| 3059 | 3163 | |
| 3060 | 3164 | async function peekCallback(_, arg) { |
| 3061 | 3165 | if (!selected_group) { |
| 3062 | 3166 | toastr.warning('Cannot run /member-peek command outside of a group chat.'); |
| 3063 | 3167 | return ''; |
| 3064 | 3168 | } |
| 3065 | 3169 | |
| 3066 | 3170 | if (is_group_generating) { |
| 3067 | 3171 | toastr.warning('Cannot run /member-peek command while the group reply is generating.'); |
| 3068 | 3172 | return ''; |
| 3069 | 3173 | } |
| 3070 | 3174 | |
| @@ -3081,12 +3185,7 @@ async function peekCallback(_, arg) { | ||
| 3081 | 3185 | |
| 3082 | 3186 | async function removeGroupMemberCallback(_, arg) { |
| 3083 | 3187 | if (!selected_group) { |
| 3084 | 3188 | toastr.warning('Cannot run /memberremovemember-remove command outside of a group chat.'); |
| 3085 | - return ''; | |
| 3086 | - } | |
| 3087 | - | |
| 3088 | - if (is_group_generating) { | |
| 3089 | - toastr.warning('Cannot run /memberremove command while the group reply is generating.'); | |
| 3090 | 3189 | return ''; |
| 3091 | 3190 | } |
| 3092 | 3191 | |
| @@ -3194,12 +3293,7 @@ function findPersonaByName(name) { | ||
| 3194 | 3293 | } |
| 3195 | 3294 | |
| 3196 | 3295 | async function sendUserMessageCallback(args, text) { |
| 3197 | - if (!text) { | |
| 3296 | + text = String(text ?? '').trim(); | |
| 3198 | - toastr.warning('You must specify text to send'); | |
| 3199 | - return; | |
| 3200 | - } | |
| 3201 | - | |
| 3202 | - text = text.trim(); | |
| 3203 | 3297 | const compact = isTrueBoolean(args?.compact); |
| 3204 | 3298 | const bias = extractMessageBias(text); |
| 3205 | 3299 | |
| @@ -3508,13 +3602,7 @@ export function getNameAndAvatarForMessage(character, name = null) { | ||
| 3508 | 3602 | } |
| 3509 | 3603 | |
| 3510 | 3604 | export async function sendMessageAs(args, text) { |
| 3511 | - if (!text) { | |
| 3512 | - toastr.warning('You must specify text to send as'); | |
| 3513 | - return ''; | |
| 3514 | - } | |
| 3515 | - | |
| 3516 | 3605 | let name = args.name?.trim(); |
| 3517 | - let mesText; | |
| 3518 | 3606 | |
| 3519 | 3607 | if (!name) { |
| 3520 | 3608 | const namelessWarningKey = 'sendAsNamelessWarningShown'; |
| @@ -3525,7 +3613,7 @@ export async function sendMessageAs(args, text) { | ||
| 3525 | 3613 | name = name2; |
| 3526 | 3614 | } |
| 3527 | 3615 | |
| 3528 | 3616 | let mesText = String(text ?? '').trim(); |
| 3529 | 3617 | |
| 3530 | 3618 | // Requires a regex check after the slash command is pushed to output |
| 3531 | 3619 | mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND, { characterOverride: name }); |
| @@ -3603,11 +3691,7 @@ export async function sendMessageAs(args, text) { | ||
| 3603 | 3691 | } |
| 3604 | 3692 | |
| 3605 | 3693 | export async function sendNarratorMessage(args, text) { |
| 3606 | - if (!text) { | |
| 3694 | + text = String(text ?? ''); | |
| 3607 | - toastr.warning('You must specify text to send'); | |
| 3608 | - return ''; | |
| 3609 | - } | |
| 3610 | - | |
| 3611 | 3695 | const name = chat_metadata[NARRATOR_NAME_KEY] || NARRATOR_NAME_DEFAULT; |
| 3612 | 3696 | // Messages that do nothing but set bias will be hidden from the context |
| 3613 | 3697 | const bias = extractMessageBias(text); |
| @@ -3698,18 +3782,13 @@ export async function promptQuietForLoudResponse(who, text) { | ||
| 3698 | 3782 | } |
| 3699 | 3783 | |
| 3700 | 3784 | async function sendCommentMessage(args, text) { |
| 3701 | - if (!text) { | |
| 3702 | - toastr.warning('You must specify text to send'); | |
| 3703 | - return ''; | |
| 3704 | - } | |
| 3705 | - | |
| 3706 | 3785 | const compact = isTrueBoolean(args?.compact); |
| 3707 | 3786 | const message = { |
| 3708 | 3787 | name: COMMENT_NAME_DEFAULT, |
| 3709 | 3788 | is_user: false, |
| 3710 | 3789 | is_system: true, |
| 3711 | 3790 | send_date: getMessageTimeStamp(), |
| 3712 | 3791 | mes: substituteParams(String(text ?? '').trim()), |
| 3713 | 3792 | force_avatar: comment_avatar, |
| 3714 | 3793 | extra: { |
| 3715 | 3794 | type: system_message_types.COMMENT, |
| @@ -69,6 +69,7 @@ import { textgenerationwebui_settings } from './textgen-settings.js'; | ||
| 69 | 69 | import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js'; |
| 70 | 70 | import { ToolManager } from './tool-calling.js'; |
| 71 | 71 | import { timestampToMoment, uuidv4 } from './utils.js'; |
| 72 | +import { getGlobalVariable, getLocalVariable, setGlobalVariable, setLocalVariable } from './variables.js'; | |
| 72 | 73 | |
| 73 | 74 | export function getContext() { |
| 74 | 75 | return { |
| @@ -175,6 +176,16 @@ export function getContext() { | ||
| 175 | 176 | humanizedDateTime, |
| 176 | 177 | updateMessageBlock, |
| 177 | 178 | appendMediaToMessage, |
| 179 | + variables: { | |
| 180 | + local: { | |
| 181 | + get: getLocalVariable, | |
| 182 | + set: setLocalVariable, | |
| 183 | + }, | |
| 184 | + global: { | |
| 185 | + get: getGlobalVariable, | |
| 186 | + set: setGlobalVariable, | |
| 187 | + }, | |
| 188 | + }, | |
| 178 | 189 | }; |
| 179 | 190 | } |
| 180 | 191 | |
| @@ -54,6 +54,12 @@ const OPENROUTER_PROVIDERS = [ | ||
| 54 | 54 | 'xAI', |
| 55 | 55 | 'Cloudflare', |
| 56 | 56 | 'SF Compute', |
| 57 | + 'Minimax', | |
| 58 | + 'Nineteen', | |
| 59 | + 'Liquid', | |
| 60 | + 'Nebius', | |
| 61 | + 'Chutes', | |
| 62 | + 'Kluster', | |
| 57 | 63 | '01.AI', |
| 58 | 64 | 'HuggingFace', |
| 59 | 65 | 'Mancer', |
| @@ -172,6 +172,7 @@ const settings = { | ||
| 172 | 172 | //truncation_length: 2048, |
| 173 | 173 | ban_eos_token: false, |
| 174 | 174 | skip_special_tokens: true, |
| 175 | + include_reasoning: true, | |
| 175 | 176 | streaming: false, |
| 176 | 177 | mirostat_mode: 0, |
| 177 | 178 | mirostat_tau: 5, |
| @@ -263,6 +264,7 @@ export const setting_names = [ | ||
| 263 | 264 | 'add_bos_token', |
| 264 | 265 | 'ban_eos_token', |
| 265 | 266 | 'skip_special_tokens', |
| 267 | + 'include_reasoning', | |
| 266 | 268 | 'streaming', |
| 267 | 269 | 'mirostat_mode', |
| 268 | 270 | 'mirostat_tau', |
| @@ -501,7 +503,7 @@ export function loadTextGenSettings(data, loadedSettings) { | ||
| 501 | 503 | for (const [type, selector] of Object.entries(SERVER_INPUTS)) { |
| 502 | 504 | const control = $(selector); |
| 503 | 505 | control.val(settings.server_urls[type] ?? '').on('input', function () { |
| 504 | 506 | settings.server_urls[type] = String($(this).val()).trim(); |
| 505 | 507 | saveSettingsDebounced(); |
| 506 | 508 | }); |
| 507 | 509 | } |
| @@ -740,6 +742,7 @@ jQuery(function () { | ||
| 740 | 742 | 'add_bos_token_textgenerationwebui': true, |
| 741 | 743 | 'temperature_last_textgenerationwebui': true, |
| 742 | 744 | 'skip_special_tokens_textgenerationwebui': true, |
| 745 | + 'include_reasoning_textgenerationwebui': true, | |
| 743 | 746 | 'top_a_textgenerationwebui': 0, |
| 744 | 747 | 'top_a_counter_textgenerationwebui': 0, |
| 745 | 748 | 'mirostat_mode_textgenerationwebui': 0, |
| @@ -986,7 +989,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | ||
| 986 | 989 | let logprobs = null; |
| 987 | 990 | const swipes = []; |
| 988 | 991 | const toolCalls = []; |
| 989 | 992 | const state = { reasoning: '' }; |
| 990 | 993 | while (true) { |
| 991 | 994 | const { done, value } = await reader.read(); |
| 992 | 995 | if (done) return; |
| @@ -1003,6 +1006,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | ||
| 1003 | 1006 | const newText = data?.choices?.[0]?.text || data?.content || ''; |
| 1004 | 1007 | text += newText; |
| 1005 | 1008 | logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities); |
| 1009 | + state.reasoning += data?.choices?.[0]?.reasoning ?? ''; | |
| 1006 | 1010 | } |
| 1007 | 1011 | |
| 1008 | 1012 | yield { text, swipes, logprobs, toolCalls, state }; |
| @@ -1266,6 +1270,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, | ||
| 1266 | 1270 | 'truncation_length': max_context, |
| 1267 | 1271 | 'ban_eos_token': settings.ban_eos_token, |
| 1268 | 1272 | 'skip_special_tokens': settings.skip_special_tokens, |
| 1273 | + 'include_reasoning': settings.include_reasoning, | |
| 1269 | 1274 | 'top_a': settings.top_a, |
| 1270 | 1275 | 'tfs': settings.tfs, |
| 1271 | 1276 | 'epsilon_cutoff': [OOBA, MANCER].includes(settings.type) ? settings.epsilon_cutoff : undefined, |
| @@ -679,6 +679,9 @@ export function getTokenizerModel() { | ||
| 679 | 679 | } |
| 680 | 680 | |
| 681 | 681 | if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) { |
| 682 | + if (oai_settings.perplexity_model.includes('sonar-reasoning')) { | |
| 683 | + return deepseekTokenizer; | |
| 684 | + } | |
| 682 | 685 | if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) { |
| 683 | 686 | return llama3Tokenizer; |
| 684 | 687 | } |
| @@ -1733,17 +1733,17 @@ export function hasAnimation(control) { | ||
| 1733 | 1733 | |
| 1734 | 1734 | /** |
| 1735 | 1735 | * Run an action once an animation on a control ends. If the control has no animation, the action will be executed immediately. |
| 1736 | - * | |
| 1736 | + * The action will be executed after the animation ends or after the timeout, whichever comes first. | |
| 1737 | 1737 | * @param {HTMLElement} control - The control element to listen for animation end event |
| 1738 | 1738 | * @param {(control:*?) => void} callback - The callback function to be executed when the animation ends |
| 1739 | + * @param {number} [timeout=500] - The timeout in milliseconds to wait for the animation to end before executing the callback | |
| 1739 | 1740 | */ |
| 1740 | 1741 | export function runAfterAnimation(control, callback, timeout = 500) { |
| 1741 | 1742 | if (hasAnimation(control)) { |
| 1742 | - const onAnimationEnd = () => { | |
| 1743 | + Promise.race([ | |
| 1743 | - control.removeEventListener('animationend', onAnimationEnd); | |
| 1744 | + new Promise((r) => setTimeout(r, timeout)), // Fallback timeout | |
| 1744 | - callback(control); | |
| 1745 | + new Promise((r) => control.addEventListener('animationend', r, { once: true })), | |
| 1745 | - }; | |
| 1746 | + ]).finally(() => callback(control)); | |
| 1746 | - control.addEventListener('animationend', onAnimationEnd); | |
| 1747 | 1747 | } else { |
| 1748 | 1748 | callback(control); |
| 1749 | 1749 | } |
| @@ -19,7 +19,7 @@ import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; | ||
| 19 | 19 | |
| 20 | 20 | const MAX_LOOPS = 100; |
| 21 | 21 | |
| 22 | 22 | export function getLocalVariable(name, args = {}) { |
| 23 | 23 | if (!chat_metadata.variables) { |
| 24 | 24 | chat_metadata.variables = {}; |
| 25 | 25 | } |
| @@ -45,7 +45,7 @@ function getLocalVariable(name, args = {}) { | ||
| 45 | 45 | return (localVariable?.trim?.() === '' || isNaN(Number(localVariable))) ? (localVariable || '') : Number(localVariable); |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | 48 | export function setLocalVariable(name, value, args = {}) { |
| 49 | 49 | if (!name) { |
| 50 | 50 | throw new Error('Variable name cannot be empty or undefined.'); |
| 51 | 51 | } |
| @@ -80,7 +80,7 @@ function setLocalVariable(name, value, args = {}) { | ||
| 80 | 80 | return value; |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | 83 | export function getGlobalVariable(name, args = {}) { |
| 84 | 84 | let globalVariable = extension_settings.variables.global[args.key ?? name]; |
| 85 | 85 | if (args.index !== undefined) { |
| 86 | 86 | try { |
| @@ -102,7 +102,7 @@ function getGlobalVariable(name, args = {}) { | ||
| 102 | 102 | return (globalVariable?.trim?.() === '' || isNaN(Number(globalVariable))) ? (globalVariable || '') : Number(globalVariable); |
| 103 | 103 | } |
| 104 | 104 | |
| 105 | 105 | export function setGlobalVariable(name, value, args = {}) { |
| 106 | 106 | if (!name) { |
| 107 | 107 | throw new Error('Variable name cannot be empty or undefined.'); |
| 108 | 108 | } |
| @@ -399,6 +399,11 @@ input[type='checkbox']:focus-visible { | ||
| 399 | 399 | color: var(--SmartThemeEmColor); |
| 400 | 400 | } |
| 401 | 401 | |
| 402 | +.mes_text q i, | |
| 403 | +.mes_text q em { | |
| 404 | + color: inherit; | |
| 405 | +} | |
| 406 | + | |
| 402 | 407 | .mes_text u, |
| 403 | 408 | .mes_reasoning u { |
| 404 | 409 | color: var(--SmartThemeUnderlineColor); |
| @@ -4597,23 +4602,6 @@ body .ui-widget-content li:hover { | ||
| 4597 | 4602 | opacity: 1; |
| 4598 | 4603 | } |
| 4599 | 4604 | |
| 4600 | -.typing_indicator { | |
| 4601 | - position: sticky; | |
| 4602 | - bottom: 10px; | |
| 4603 | - margin: 10px; | |
| 4604 | - opacity: 0.85; | |
| 4605 | - text-shadow: 0px 0px calc(var(--shadowWidth) * 1px) var(--SmartThemeShadowColor); | |
| 4606 | - order: 9999; | |
| 4607 | -} | |
| 4608 | - | |
| 4609 | -.typing_indicator:after { | |
| 4610 | - display: inline-block; | |
| 4611 | - vertical-align: bottom; | |
| 4612 | - animation: ellipsis steps(4, end) 1500ms infinite; | |
| 4613 | - content: ""; | |
| 4614 | - width: 0px; | |
| 4615 | -} | |
| 4616 | - | |
| 4617 | 4605 | #group_avatar_preview .missing-avatar { |
| 4618 | 4606 | display: inline; |
| 4619 | 4607 | vertical-align: middle; |
| @@ -369,6 +369,7 @@ export const OPENROUTER_KEYS = [ | ||
| 369 | 369 | 'prompt', |
| 370 | 370 | 'stop', |
| 371 | 371 | 'provider', |
| 372 | + 'include_reasoning', | |
| 372 | 373 | ]; |
| 373 | 374 | |
| 374 | 375 | // https://github.com/vllm-project/vllm/blob/0f8a91401c89ac0a8018def3756829611b57727f/vllm/entrypoints/openai/protocol.py#L220 |
| @@ -288,7 +288,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 288 | 288 | |
| 289 | 289 | const model = String(request.body.model); |
| 290 | 290 | const stream = Boolean(request.body.stream); |
| 291 | 291 | const showThoughts = Boolean(request.body.show_thoughtsinclude_reasoning); |
| 292 | 292 | const isThinking = model.includes('thinking'); |
| 293 | 293 | |
| 294 | 294 | const generationConfig = { |
| @@ -1000,7 +1000,7 @@ router.post('/generate', jsonParser, function (request, response) { | ||
| 1000 | 1000 | bodyParams['route'] = 'fallback'; |
| 1001 | 1001 | } |
| 1002 | 1002 | |
| 1003 | 1003 | if (request.body.show_thoughtsinclude_reasoning) { |
| 1004 | 1004 | bodyParams['include_reasoning'] = true; |
| 1005 | 1005 | } |
| 1006 | 1006 | |
| @@ -162,7 +162,7 @@ router.post('/transcript', jsonParser, async (request, response) => { | ||
| 162 | 162 | |
| 163 | 163 | router.post('/searxng', jsonParser, async (request, response) => { |
| 164 | 164 | try { |
| 165 | 165 | const { baseUrl, query, preferences, categories } = request.body; |
| 166 | 166 | |
| 167 | 167 | if (!baseUrl || !query) { |
| 168 | 168 | console.error('Missing required parameters for /searxng'); |
| @@ -193,6 +193,9 @@ router.post('/searxng', jsonParser, async (request, response) => { | ||
| 193 | 193 | if (preferences) { |
| 194 | 194 | searchParams.append('preferences', preferences); |
| 195 | 195 | } |
| 196 | + if (categories) { | |
| 197 | + searchParams.append('categories', categories); | |
| 198 | + } | |
| 196 | 199 | searchUrl.search = searchParams.toString(); |
| 197 | 200 | |
| 198 | 201 | const searchResult = await fetch(searchUrl, { headers: visitHeaders }); |
| @@ -220,7 +223,7 @@ router.post('/tavily', jsonParser, async (request, response) => { | ||
| 220 | 223 | return response.sendStatus(400); |
| 221 | 224 | } |
| 222 | 225 | |
| 223 | 226 | const { query, include_images } = request.body; |
| 224 | 227 | |
| 225 | 228 | const body = { |
| 226 | 229 | query: query, |
| @@ -229,7 +232,7 @@ router.post('/tavily', jsonParser, async (request, response) => { | ||
| 229 | 232 | topic: 'general', |
| 230 | 233 | include_answer: true, |
| 231 | 234 | include_raw_content: false, |
| 232 | 235 | include_images: false!!include_images, |
| 233 | 236 | include_image_descriptions: false, |
| 234 | 237 | include_domains: [], |
| 235 | 238 | max_results: 10, |
| @@ -297,6 +300,7 @@ router.post('/koboldcpp', jsonParser, async (request, response) => { | ||
| 297 | 300 | router.post('/visit', jsonParser, async (request, response) => { |
| 298 | 301 | try { |
| 299 | 302 | const url = request.body.url; |
| 303 | + const html = Boolean(request.body.html ?? true); | |
| 300 | 304 | |
| 301 | 305 | if (!url) { |
| 302 | 306 | console.error('No url provided for /visit'); |
| @@ -340,13 +344,20 @@ router.post('/visit', jsonParser, async (request, response) => { | ||
| 340 | 344 | } |
| 341 | 345 | |
| 342 | 346 | const contentType = String(result.headers.get('content-type')); |
| 343 | - if (!contentType.includes('text/html')) { | |
| 347 | + | |
| 344 | - console.error(`Visit failed, content-type is ${contentType}, expected text/html`); | |
| 348 | + if (html) { | |
| 345 | - return response.sendStatus(500); | |
| 349 | + if (!contentType.includes('text/html')) { | |
| 350 | + console.error(`Visit failed, content-type is ${contentType}, expected text/html`); | |
| 351 | + return response.sendStatus(500); | |
| 352 | + } | |
| 353 | + | |
| 354 | + const text = await result.text(); | |
| 355 | + return response.send(text); | |
| 346 | 356 | } |
| 347 | 357 | |
| 348 | - const text = await result.text(); | |
| 358 | + response.setHeader('Content-Type', contentType); | |
| 349 | - return response.send(text); | |
| 359 | + const buffer = await result.arrayBuffer(); | |
| 360 | + return response.send(Buffer.from(buffer)); | |
| 350 | 361 | } catch (error) { |
| 351 | 362 | console.error(error); |
| 352 | 363 | return response.sendStatus(500); |
| @@ -533,6 +533,7 @@ comfy.post('/delete-workflow', jsonParser, async (request, response) => { | ||
| 533 | 533 | |
| 534 | 534 | comfy.post('/generate', jsonParser, async (request, response) => { |
| 535 | 535 | try { |
| 536 | + let item; | |
| 536 | 537 | const url = new URL(urlJoin(request.body.url, '/prompt')); |
| 537 | 538 | |
| 538 | 539 | const controller = new AbortController(); |
| @@ -557,7 +558,6 @@ comfy.post('/generate', jsonParser, async (request, response) => { | ||
| 557 | 558 | /** @type {any} */ |
| 558 | 559 | const data = await promptResult.json(); |
| 559 | 560 | const id = data.prompt_id; |
| 560 | - let item; | |
| 561 | 561 | const historyUrl = new URL(urlJoin(request.body.url, '/history')); |
| 562 | 562 | while (true) { |
| 563 | 563 | const result = await fetch(historyUrl); |
| @@ -398,7 +398,7 @@ function getWebTokenizersChunks(tokenizer, ids) { | ||
| 398 | 398 | * @returns {string} Tokenizer model to use |
| 399 | 399 | */ |
| 400 | 400 | export function getTokenizerModel(requestModel) { |
| 401 | 401 | if (requestModel.includes('o1-preview') || requestModel.includes('o1-mini') || requestModel.includes('o3-mini')) { |
| 402 | 402 | return 'gpt-4o'; |
| 403 | 403 | } |
| 404 | 404 | |