Merge branch 'staging' into support-multiple-expressions
| @@ -274,7 +274,7 @@ You will need two mandatory directory mappings and a port mapping to allow Silly | |||
| 274 | 1. Open your Command Line | 274 | 1. Open your Command Line |
| 275 | 2. Run the following command | 275 | 2. Run the following command |
| 276 | 276 | ||
| 277 | `docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' -v '[extensions]':'/home/node/app/public/scripts/extensions/third-party':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'` | 277 | `docker run --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' -v '[extensions]':'/home/node/app/public/scripts/extensions/third-party':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'` |
| 278 | 278 | ||
| 279 | > Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config. | 279 | > Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config. |
| 280 | 280 | ||
| @@ -472,6 +472,11 @@ label[for="trim_spaces"]:has(input:checked) i.warning { | |||
| 472 | display: none; | 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 | #claude_function_prefill_warning { | 480 | #claude_function_prefill_warning { |
| 476 | display: none; | 481 | display: none; |
| 477 | color: red; | 482 | color: red; |
| @@ -1587,6 +1587,10 @@ | |||
| 1587 | <input type="checkbox" id="skip_special_tokens_textgenerationwebui" /> | 1587 | <input type="checkbox" id="skip_special_tokens_textgenerationwebui" /> |
| 1588 | <small data-i18n="Skip Special Tokens">Skip Special Tokens</small> | 1588 | <small data-i18n="Skip Special Tokens">Skip Special Tokens</small> |
| 1589 | </label> | 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 | <label data-tg-type="ooba, aphrodite, tabby" class="checkbox_label flexGrow flexShrink" for="temperature_last_textgenerationwebui"> | 1594 | <label data-tg-type="ooba, aphrodite, tabby" class="checkbox_label flexGrow flexShrink" for="temperature_last_textgenerationwebui"> |
| 1591 | <input type="checkbox" id="temperature_last_textgenerationwebui" /> | 1595 | <input type="checkbox" id="temperature_last_textgenerationwebui" /> |
| 1592 | <label> | 1596 | <label> |
| @@ -1977,17 +1981,17 @@ | |||
| 1977 | </span> | 1981 | </span> |
| 1978 | </div> | 1982 | </div> |
| 1979 | </div> | 1983 | </div> |
| 1980 | <div class="range-block" data-source="makersuite,deepseek"> | 1984 | <div class="range-block" data-source="makersuite,deepseek,openrouter"> |
| 1981 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> | 1985 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> |
| 1982 | <input id="openai_show_thoughts" type="checkbox" /> | 1986 | <input id="openai_show_thoughts" type="checkbox" /> |
| 1983 | <span> | 1987 | <span> |
| 1984 | <span data-i18n="Show model thoughts">Show model thoughts</span> | 1988 | <span data-i18n="Request model reasoning">Request model reasoning</span> |
| 1985 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i> | 1989 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i> |
| 1986 | </span> | 1990 | </span> |
| 1987 | </label> | 1991 | </label> |
| 1988 | <div class="toggle-description justifyLeft marginBot5"> | 1992 | <div class="toggle-description justifyLeft marginBot5"> |
| 1989 | <span data-i18n="Display the model's internal thoughts in the response."> | 1993 | <span data-i18n="Allows the model to return its thinking process."> |
| 1990 | Display the model's internal thoughts in the response. | 1994 | Allows the model to return its thinking process. |
| 1991 | </span> | 1995 | </span> |
| 1992 | </div> | 1996 | </div> |
| 1993 | </div> | 1997 | </div> |
| @@ -3227,32 +3231,19 @@ | |||
| 3227 | <h4 data-i18n="Perplexity Model">Perplexity Model</h4> | 3231 | <h4 data-i18n="Perplexity Model">Perplexity Model</h4> |
| 3228 | <select id="model_perplexity_select"> | 3232 | <select id="model_perplexity_select"> |
| 3229 | <optgroup label="Perplexity Sonar Models"> | 3233 | <optgroup label="Perplexity Sonar Models"> |
| 3234 | <option value="sonar">sonar</option> | ||
| 3235 | <option value="sonar-pro">sonar-pro</option> | ||
| 3236 | <option value="sonar-reasoning">sonar-reasoning</option> | ||
| 3237 | </optgroup> | ||
| 3238 | <optgroup label="Deprecated Models"> | ||
| 3239 | <!-- These are scheduled for deprecation after 2/22/2025 --> | ||
| 3230 | <option value="llama-3.1-sonar-small-128k-online">llama-3.1-sonar-small-128k-online</option> | 3240 | <option value="llama-3.1-sonar-small-128k-online">llama-3.1-sonar-small-128k-online</option> |
| 3231 | <option value="llama-3.1-sonar-large-128k-online">llama-3.1-sonar-large-128k-online</option> | 3241 | <option value="llama-3.1-sonar-large-128k-online">llama-3.1-sonar-large-128k-online</option> |
| 3232 | <option value="llama-3.1-sonar-huge-128k-online">llama-3.1-sonar-huge-128k-online</option> | 3242 | <option value="llama-3.1-sonar-huge-128k-online">llama-3.1-sonar-huge-128k-online</option> |
| 3233 | </optgroup> | 3243 | <!-- These are not listed on the site anymore --> |
| 3234 | <optgroup label="Perplexity Chat Models"> | ||
| 3235 | <option value="llama-3.1-sonar-small-128k-chat">llama-3.1-sonar-small-128k-chat</option> | 3244 | <option value="llama-3.1-sonar-small-128k-chat">llama-3.1-sonar-small-128k-chat</option> |
| 3236 | <option value="llama-3.1-sonar-large-128k-chat">llama-3.1-sonar-large-128k-chat</option> | 3245 | <option value="llama-3.1-sonar-large-128k-chat">llama-3.1-sonar-large-128k-chat</option> |
| 3237 | </optgroup> | 3246 | </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 | </select> | 3247 | </select> |
| 3257 | </div> | 3248 | </div> |
| 3258 | <form id="cohere_form" data-source="cohere" action="javascript:void(null);" method="post" enctype="multipart/form-data"> | 3249 | <form id="cohere_form" data-source="cohere" action="javascript:void(null);" method="post" enctype="multipart/form-data"> |
| @@ -3800,6 +3791,45 @@ | |||
| 3800 | </div> | 3791 | </div> |
| 3801 | </div> | 3792 | </div> |
| 3802 | <div> | 3793 | <div> |
| 3794 | <h4 class="standoutHeader"> | ||
| 3795 | <span data-i18n="Reasoning">Reasoning</span> | ||
| 3796 | </h4> | ||
| 3797 | <div> | ||
| 3798 | <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"> | ||
| 3799 | <input id="reasoning_auto_parse" type="checkbox" /> | ||
| 3800 | <small data-i18n="Auto-Parse Reasoning"> | ||
| 3801 | Auto-Parse Reasoning | ||
| 3802 | </small> | ||
| 3803 | </label> | ||
| 3804 | <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"> | ||
| 3805 | <input id="reasoning_add_to_prompts" type="checkbox" /> | ||
| 3806 | <small data-i18n="Add Reasoning to Prompts"> | ||
| 3807 | Add Reasoning to Prompts | ||
| 3808 | </small> | ||
| 3809 | </label> | ||
| 3810 | <div class="flex-container"> | ||
| 3811 | <div class="flex1" title="Inserted before the reasoning content." data-i18n="[title]reasoning_prefix"> | ||
| 3812 | <small data-i18n="Prefix">Prefix</small> | ||
| 3813 | <textarea id="reasoning_prefix" class="text_pole textarea_compact autoSetHeight"></textarea> | ||
| 3814 | </div> | ||
| 3815 | <div class="flex1" title="Inserted after the reasoning content." data-i18n="[title]reasoning_suffix"> | ||
| 3816 | <small data-i18n="Suffix">Suffix</small> | ||
| 3817 | <textarea id="reasoning_suffix" class="text_pole textarea_compact autoSetHeight"></textarea> | ||
| 3818 | </div> | ||
| 3819 | </div> | ||
| 3820 | <div class="flex-container"> | ||
| 3821 | <div class="flex1" title="Inserted between the reasoning and the message content." data-i18n="[title]reasoning_separator"> | ||
| 3822 | <small data-i18n="Separator">Separator</small> | ||
| 3823 | <textarea id="reasoning_separator" class="text_pole textarea_compact autoSetHeight"></textarea> | ||
| 3824 | </div> | ||
| 3825 | <div class="flex1" title="Maximum number of reasoning blocks to be added per prompt, counting from the last message." data-i18n="[title]reasoning_max_additions"> | ||
| 3826 | <small data-i18n="Max Additions">Max Additions</small> | ||
| 3827 | <input id="reasoning_max_additions" class="text_pole textarea_compact" type="number" min="0" max="999"></textarea> | ||
| 3828 | </div> | ||
| 3829 | </div> | ||
| 3830 | </div> | ||
| 3831 | </div> | ||
| 3832 | <div> | ||
| 3803 | <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4> | 3833 | <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4> |
| 3804 | <div> | 3834 | <div> |
| 3805 | <small> | 3835 | <small> |
| @@ -6221,14 +6251,26 @@ | |||
| 6221 | <div class="mes_edit_buttons"> | 6251 | <div class="mes_edit_buttons"> |
| 6222 | <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div> | 6252 | <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div> |
| 6223 | <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div> | 6253 | <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div> |
| 6224 | <div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message"> | 6254 | <div class="mes_edit_add_reasoning menu_button fa-solid fa-lightbulb" title="Add a reasoning block" data-i18n="[title]Add a reasoning block"></div> |
| 6225 | </div> | 6255 | <div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message"></div> |
| 6226 | <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div> | 6256 | <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div> |
| 6227 | <div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down"> | 6257 | <div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down"></div> |
| 6228 | </div> | ||
| 6229 | <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div> | 6258 | <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div> |
| 6230 | </div> | 6259 | </div> |
| 6231 | </div> | 6260 | </div> |
| 6261 | <details class="mes_reasoning_details"> | ||
| 6262 | <summary class="mes_reasoning_summary"> | ||
| 6263 | <span data-i18n="Reasoning">Reasoning</span> | ||
| 6264 | <div class="mes_reasoning_actions"> | ||
| 6265 | <div class="mes_reasoning_edit_done mes_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div> | ||
| 6266 | <div class="mes_reasoning_edit_cancel mes_button fa-solid fa-xmark" title="Cancel edit" data-i18n="[title]Cancel edit"></div> | ||
| 6267 | <div class="mes_reasoning_edit mes_button fa-solid fa-pencil" title="Edit reasoning" data-i18n="[title]Edit reasoning"></div> | ||
| 6268 | <div class="mes_reasoning_copy mes_button fa-solid fa-copy" title="Copy reasoning" data-i18n="[title]Copy reasoning"></div> | ||
| 6269 | <div class="mes_reasoning_delete mes_button fa-solid fa-trash-can" title="Remove reasoning" data-i18n="[title]Remove reasoning"></div> | ||
| 6270 | </div> | ||
| 6271 | </summary> | ||
| 6272 | <div class="mes_reasoning"></div> | ||
| 6273 | </details> | ||
| 6232 | <div class="mes_text"></div> | 6274 | <div class="mes_text"></div> |
| 6233 | <div class="mes_img_container"> | 6275 | <div class="mes_img_container"> |
| 6234 | <div class="mes_img_controls"> | 6276 | <div class="mes_img_controls"> |
| @@ -1385,8 +1385,8 @@ | |||
| 1385 | "enable_functions_desc_1": "Autorise l'utilisation", | 1385 | "enable_functions_desc_1": "Autorise l'utilisation", |
| 1386 | "enable_functions_desc_2": "outils de fonction", | 1386 | "enable_functions_desc_2": "outils de fonction", |
| 1387 | "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.", | 1387 | "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.", |
| 1388 | "Show model thoughts": "Afficher les pensées du modèle", | 1388 | "Request model reasoning": "Demander les pensées du modèle", |
| 1389 | "Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.", | 1389 | "Allows the model to return its thinking process.": "Permet au modèle de retourner son processus de réflexion.", |
| 1390 | "Confirm token parsing with": "Confirmer l'analyse des tokens avec", | 1390 | "Confirm token parsing with": "Confirmer l'analyse des tokens avec", |
| 1391 | "openai_logit_bias_no_items": "Aucun élément", | 1391 | "openai_logit_bias_no_items": "Aucun élément", |
| 1392 | "api_no_connection": "Pas de connection...", | 1392 | "api_no_connection": "Pas de connection...", |
| @@ -266,8 +266,8 @@ | |||
| 266 | "Use system prompt": "使用系统提示词", | 266 | "Use system prompt": "使用系统提示词", |
| 267 | "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过", | 267 | "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过", |
| 268 | "Merges_all_system_messages_desc_2": "字段发送。", | 268 | "Merges_all_system_messages_desc_2": "字段发送。", |
| 269 | "Show model thoughts": "展示思维链", | 269 | "Request model reasoning": "请求思维链", |
| 270 | "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。", | 270 | "Allows the model to return its thinking process.": "允许模型返回其思维过程。", |
| 271 | "Assistant Prefill": "AI预填", | 271 | "Assistant Prefill": "AI预填", |
| 272 | "Expand the editor": "展开编辑器", | 272 | "Expand the editor": "展开编辑器", |
| 273 | "Start Claude's answer with...": "以如下内容开始Claude的回答...", | 273 | "Start Claude's answer with...": "以如下内容开始Claude的回答...", |
| @@ -2356,8 +2356,8 @@ | |||
| 2356 | "Forbid": "禁止", | 2356 | "Forbid": "禁止", |
| 2357 | "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。", | 2357 | "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。", |
| 2358 | "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。", | 2358 | "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。", |
| 2359 | "Show model thoughts": "顯示模型思維鏈", | 2359 | "Request model reasoning": "請求模型思維鏈", |
| 2360 | "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。", | 2360 | "Allows the model to return its thinking process.": "讓模型回傳其思考過程。", |
| 2361 | "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]", | 2361 | "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]", |
| 2362 | "Model ID (optional)": "模型 ID(可選)", | 2362 | "Model ID (optional)": "模型 ID(可選)", |
| 2363 | "DeepSeek API Key": "DeepSeek API 金鑰", | 2363 | "DeepSeek API Key": "DeepSeek API 金鑰", |
| @@ -95,6 +95,7 @@ import { | |||
| 95 | resetMovableStyles, | 95 | resetMovableStyles, |
| 96 | forceCharacterEditorTokenize, | 96 | forceCharacterEditorTokenize, |
| 97 | applyPowerUserSettings, | 97 | applyPowerUserSettings, |
| 98 | generatedTextFiltered, | ||
| 98 | } from './scripts/power-user.js'; | 99 | } from './scripts/power-user.js'; |
| 99 | 100 | ||
| 100 | import { | 101 | import { |
| @@ -169,6 +170,7 @@ import { | |||
| 169 | toggleDrawer, | 170 | toggleDrawer, |
| 170 | isElementInViewport, | 171 | isElementInViewport, |
| 171 | copyText, | 172 | copyText, |
| 173 | escapeHtml, | ||
| 172 | } from './scripts/utils.js'; | 174 | } from './scripts/utils.js'; |
| 173 | import { debounce_timeout } from './scripts/constants.js'; | 175 | import { debounce_timeout } from './scripts/constants.js'; |
| 174 | 176 | ||
| @@ -267,6 +269,7 @@ import { initSettingsSearch } from './scripts/setting-search.js'; | |||
| 267 | import { initBulkEdit } from './scripts/bulk-edit.js'; | 269 | import { initBulkEdit } from './scripts/bulk-edit.js'; |
| 268 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; | 270 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; |
| 269 | import { getContext } from './scripts/st-context.js'; | 271 | import { getContext } from './scripts/st-context.js'; |
| 272 | import { initReasoning, PromptReasoning } from './scripts/reasoning.js'; | ||
| 270 | 273 | ||
| 271 | // API OBJECT FOR EXTERNAL WIRING | 274 | // API OBJECT FOR EXTERNAL WIRING |
| 272 | globalThis.SillyTavern = { | 275 | globalThis.SillyTavern = { |
| @@ -982,6 +985,7 @@ async function firstLoadInit() { | |||
| 982 | initServerHistory(); | 985 | initServerHistory(); |
| 983 | initSettingsSearch(); | 986 | initSettingsSearch(); |
| 984 | initBulkEdit(); | 987 | initBulkEdit(); |
| 988 | initReasoning(); | ||
| 985 | await initScrapers(); | 989 | await initScrapers(); |
| 986 | doDailyExtensionUpdatesCheck(); | 990 | doDailyExtensionUpdatesCheck(); |
| 987 | await hideLoader(); | 991 | await hideLoader(); |
| @@ -1991,14 +1995,15 @@ export async function sendTextareaMessage() { | |||
| 1991 | * @param {boolean} isUser If the message was sent by the user | 1995 | * @param {boolean} isUser If the message was sent by the user |
| 1992 | * @param {number} messageId Message index in chat array | 1996 | * @param {number} messageId Message index in chat array |
| 1993 | * @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides | 1997 | * @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides |
| 1998 | * @param {boolean} [isReasoning] If the message is reasoning output | ||
| 1994 | * @returns {string} HTML string | 1999 | * @returns {string} HTML string |
| 1995 | */ | 2000 | */ |
| 1996 | export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}) { | 2001 | export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}, isReasoning = false) { |
| 1997 | if (!mes) { | 2002 | if (!mes) { |
| 1998 | return ''; | 2003 | return ''; |
| 1999 | } | 2004 | } |
| 2000 | 2005 | ||
| 2001 | if (Number(messageId) === 0 && !isSystem && !isUser) { | 2006 | if (Number(messageId) === 0 && !isSystem && !isUser && !isReasoning) { |
| 2002 | const mesBeforeReplace = mes; | 2007 | const mesBeforeReplace = mes; |
| 2003 | const chatMessage = chat[messageId]; | 2008 | const chatMessage = chat[messageId]; |
| 2004 | mes = substituteParams(mes, undefined, ch_name); | 2009 | mes = substituteParams(mes, undefined, ch_name); |
| @@ -2027,6 +2032,9 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san | |||
| 2027 | if (!isSystem) { | 2032 | if (!isSystem) { |
| 2028 | function getRegexPlacement() { | 2033 | function getRegexPlacement() { |
| 2029 | try { | 2034 | try { |
| 2035 | if (isReasoning) { | ||
| 2036 | return regex_placement.REASONING; | ||
| 2037 | } | ||
| 2030 | if (isUser) { | 2038 | if (isUser) { |
| 2031 | return regex_placement.USER_INPUT; | 2039 | return regex_placement.USER_INPUT; |
| 2032 | } else if (chat[messageId]?.extra?.type === 'narrator') { | 2040 | } else if (chat[messageId]?.extra?.type === 'narrator') { |
| @@ -2060,6 +2068,17 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san | |||
| 2060 | mes = mes.replaceAll('<', '<').replaceAll('>', '>'); | 2068 | mes = mes.replaceAll('<', '<').replaceAll('>', '>'); |
| 2061 | } | 2069 | } |
| 2062 | 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 | |||
| 2063 | if (!isSystem) { | 2082 | if (!isSystem) { |
| 2064 | // Save double quotes in tags as a special character to prevent them from being encoded | 2083 | // Save double quotes in tags as a special character to prevent them from being encoded |
| 2065 | if (!power_user.encode_tags) { | 2084 | if (!power_user.encode_tags) { |
| @@ -2200,6 +2219,7 @@ function getMessageFromTemplate({ | |||
| 2200 | isUser, | 2219 | isUser, |
| 2201 | avatarImg, | 2220 | avatarImg, |
| 2202 | bias, | 2221 | bias, |
| 2222 | reasoning, | ||
| 2203 | isSystem, | 2223 | isSystem, |
| 2204 | title, | 2224 | title, |
| 2205 | timerValue, | 2225 | timerValue, |
| @@ -2224,6 +2244,7 @@ function getMessageFromTemplate({ | |||
| 2224 | mes.find('.avatar img').attr('src', avatarImg); | 2244 | mes.find('.avatar img').attr('src', avatarImg); |
| 2225 | mes.find('.ch_name .name_text').text(characterName); | 2245 | mes.find('.ch_name .name_text').text(characterName); |
| 2226 | mes.find('.mes_bias').html(bias); | 2246 | mes.find('.mes_bias').html(bias); |
| 2247 | mes.find('.mes_reasoning').html(reasoning); | ||
| 2227 | mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`); | 2248 | mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`); |
| 2228 | mes.find('.mesIDDisplay').text(`#${mesId}`); | 2249 | mes.find('.mesIDDisplay').text(`#${mesId}`); |
| 2229 | tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`); | 2250 | tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| @@ -2238,10 +2259,16 @@ function getMessageFromTemplate({ | |||
| 2238 | return mes; | 2259 | return mes; |
| 2239 | } | 2260 | } |
| 2240 | 2261 | ||
| 2262 | /** | ||
| 2263 | * Re-renders a message block with updated content. | ||
| 2264 | * @param {number} messageId Message ID | ||
| 2265 | * @param {object} message Message object | ||
| 2266 | */ | ||
| 2241 | export function updateMessageBlock(messageId, message) { | 2267 | export function updateMessageBlock(messageId, message) { |
| 2242 | const messageElement = $(`#chat [mesid="${messageId}"]`); | 2268 | const messageElement = $(`#chat [mesid="${messageId}"]`); |
| 2243 | const text = message?.extra?.display_text ?? message.mes; | 2269 | const text = message?.extra?.display_text ?? message.mes; |
| 2244 | messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId)); | 2270 | messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId, {}, false)); |
| 2271 | messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, messageId, {}, true)); | ||
| 2245 | addCopyToCodeBlocks(messageElement); | 2272 | addCopyToCodeBlocks(messageElement); |
| 2246 | appendMediaToMessage(message, messageElement); | 2273 | appendMediaToMessage(message, messageElement); |
| 2247 | } | 2274 | } |
| @@ -2398,8 +2425,10 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | |||
| 2398 | mes.is_user, | 2425 | mes.is_user, |
| 2399 | chat.indexOf(mes), | 2426 | chat.indexOf(mes), |
| 2400 | sanitizerOverrides, | 2427 | sanitizerOverrides, |
| 2428 | false, | ||
| 2401 | ); | 2429 | ); |
| 2402 | const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1); | 2430 | const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1, {}, false); |
| 2431 | const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, chat.indexOf(mes), {}, true); | ||
| 2403 | let bookmarkLink = mes?.extra?.bookmark_link ?? ''; | 2432 | let bookmarkLink = mes?.extra?.bookmark_link ?? ''; |
| 2404 | 2433 | ||
| 2405 | let params = { | 2434 | let params = { |
| @@ -2409,6 +2438,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | |||
| 2409 | isUser: mes.is_user, | 2438 | isUser: mes.is_user, |
| 2410 | avatarImg: avatarImg, | 2439 | avatarImg: avatarImg, |
| 2411 | bias: bias, | 2440 | bias: bias, |
| 2441 | reasoning: reasoning, | ||
| 2412 | isSystem: isSystem, | 2442 | isSystem: isSystem, |
| 2413 | title: title, | 2443 | title: title, |
| 2414 | bookmarkLink: bookmarkLink, | 2444 | bookmarkLink: bookmarkLink, |
| @@ -2468,6 +2498,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | |||
| 2468 | const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`); | 2498 | const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`); |
| 2469 | swipeMessage.attr('swipeid', params.swipeId); | 2499 | swipeMessage.attr('swipeid', params.swipeId); |
| 2470 | swipeMessage.find('.mes_text').html(messageText).attr('title', title); | 2500 | swipeMessage.find('.mes_text').html(messageText).attr('title', title); |
| 2501 | swipeMessage.find('.mes_reasoning').html(reasoning); | ||
| 2471 | swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`); | 2502 | swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`); |
| 2472 | appendMediaToMessage(mes, swipeMessage); | 2503 | appendMediaToMessage(mes, swipeMessage); |
| 2473 | if (power_user.timestamp_model_icon && params.extra?.api) { | 2504 | if (power_user.timestamp_model_icon && params.extra?.api) { |
| @@ -3044,7 +3075,7 @@ export function isStreamingEnabled() { | |||
| 3044 | (main_api == 'openai' && | 3075 | (main_api == 'openai' && |
| 3045 | oai_settings.stream_openai && | 3076 | oai_settings.stream_openai && |
| 3046 | !noStreamSources.includes(oai_settings.chat_completion_source) && | 3077 | !noStreamSources.includes(oai_settings.chat_completion_source) && |
| 3047 | !(oai_settings.chat_completion_source == chat_completion_sources.OPENAI && oai_settings.openai_model.startsWith('o1-')) && | 3078 | !(oai_settings.chat_completion_source == chat_completion_sources.OPENAI && (oai_settings.openai_model.startsWith('o1') || oai_settings.openai_model.startsWith('o3'))) && |
| 3048 | !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison'))) | 3079 | !(oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE && oai_settings.google_model.includes('bison'))) |
| 3049 | || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming) | 3080 | || (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming) |
| 3050 | || (main_api == 'novel' && nai_settings.streaming_novel) | 3081 | || (main_api == 'novel' && nai_settings.streaming_novel) |
| @@ -3078,6 +3109,7 @@ class StreamingProcessor { | |||
| 3078 | this.messageTextDom = null; | 3109 | this.messageTextDom = null; |
| 3079 | this.messageTimerDom = null; | 3110 | this.messageTimerDom = null; |
| 3080 | this.messageTokenCounterDom = null; | 3111 | this.messageTokenCounterDom = null; |
| 3112 | this.messageReasoningDom = null; | ||
| 3081 | /** @type {HTMLTextAreaElement} */ | 3113 | /** @type {HTMLTextAreaElement} */ |
| 3082 | this.sendTextarea = document.querySelector('#send_textarea'); | 3114 | this.sendTextarea = document.querySelector('#send_textarea'); |
| 3083 | this.type = type; | 3115 | this.type = type; |
| @@ -3093,6 +3125,7 @@ class StreamingProcessor { | |||
| 3093 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ | 3125 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ |
| 3094 | this.messageLogprobs = []; | 3126 | this.messageLogprobs = []; |
| 3095 | this.toolCalls = []; | 3127 | this.toolCalls = []; |
| 3128 | this.reasoning = ''; | ||
| 3096 | } | 3129 | } |
| 3097 | 3130 | ||
| 3098 | #checkDomElements(messageId) { | 3131 | #checkDomElements(messageId) { |
| @@ -3101,6 +3134,7 @@ class StreamingProcessor { | |||
| 3101 | this.messageTextDom = this.messageDom?.querySelector('.mes_text'); | 3134 | this.messageTextDom = this.messageDom?.querySelector('.mes_text'); |
| 3102 | this.messageTimerDom = this.messageDom?.querySelector('.mes_timer'); | 3135 | this.messageTimerDom = this.messageDom?.querySelector('.mes_timer'); |
| 3103 | this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay'); | 3136 | this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay'); |
| 3137 | this.messageReasoningDom = this.messageDom?.querySelector('.mes_reasoning'); | ||
| 3104 | } | 3138 | } |
| 3105 | } | 3139 | } |
| 3106 | 3140 | ||
| @@ -3137,7 +3171,7 @@ class StreamingProcessor { | |||
| 3137 | this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true })); | 3171 | this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true })); |
| 3138 | } | 3172 | } |
| 3139 | else { | 3173 | else { |
| 3140 | await saveReply(this.type, text, true); | 3174 | await saveReply(this.type, text, true, '', [], ''); |
| 3141 | messageId = chat.length - 1; | 3175 | messageId = chat.length - 1; |
| 3142 | this.#checkDomElements(messageId); | 3176 | this.#checkDomElements(messageId); |
| 3143 | this.showMessageButtons(messageId); | 3177 | this.showMessageButtons(messageId); |
| @@ -3178,18 +3212,27 @@ class StreamingProcessor { | |||
| 3178 | this.#checkDomElements(messageId); | 3212 | this.#checkDomElements(messageId); |
| 3179 | this.#updateMessageBlockVisibility(); | 3213 | this.#updateMessageBlockVisibility(); |
| 3180 | const currentTime = new Date(); | 3214 | const currentTime = new Date(); |
| 3181 | // Don't waste time calculating token count for streaming | ||
| 3182 | const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(processedText, 0) : 0; | ||
| 3183 | const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount); | ||
| 3184 | chat[messageId]['mes'] = processedText; | 3215 | chat[messageId]['mes'] = processedText; |
| 3185 | chat[messageId]['gen_started'] = this.timeStarted; | 3216 | chat[messageId]['gen_started'] = this.timeStarted; |
| 3186 | chat[messageId]['gen_finished'] = currentTime; | 3217 | chat[messageId]['gen_finished'] = currentTime; |
| 3187 | 3218 | ||
| 3188 | if (currentTokenCount) { | ||
| 3189 | if (!chat[messageId]['extra']) { | 3219 | if (!chat[messageId]['extra']) { |
| 3190 | chat[messageId]['extra'] = {}; | 3220 | chat[messageId]['extra'] = {}; |
| 3191 | } | 3221 | } |
| 3192 | 3222 | ||
| 3223 | if (this.reasoning) { | ||
| 3224 | chat[messageId]['extra']['reasoning'] = power_user.trim_spaces ? this.reasoning.trim() : this.reasoning; | ||
| 3225 | if (this.messageReasoningDom instanceof HTMLElement) { | ||
| 3226 | const formattedReasoning = messageFormatting(this.reasoning, '', false, false, messageId, {}, true); | ||
| 3227 | this.messageReasoningDom.innerHTML = formattedReasoning; | ||
| 3228 | } | ||
| 3229 | } | ||
| 3230 | |||
| 3231 | // Don't waste time calculating token count for streaming | ||
| 3232 | const tokenCountText = (this.reasoning || '') + processedText; | ||
| 3233 | const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(tokenCountText, 0) : 0; | ||
| 3234 | |||
| 3235 | if (currentTokenCount) { | ||
| 3193 | chat[messageId]['extra']['token_count'] = currentTokenCount; | 3236 | chat[messageId]['extra']['token_count'] = currentTokenCount; |
| 3194 | if (this.messageTokenCounterDom instanceof HTMLElement) { | 3237 | if (this.messageTokenCounterDom instanceof HTMLElement) { |
| 3195 | this.messageTokenCounterDom.textContent = `${currentTokenCount}t`; | 3238 | this.messageTokenCounterDom.textContent = `${currentTokenCount}t`; |
| @@ -3207,14 +3250,19 @@ class StreamingProcessor { | |||
| 3207 | chat[messageId].is_system, | 3250 | chat[messageId].is_system, |
| 3208 | chat[messageId].is_user, | 3251 | chat[messageId].is_user, |
| 3209 | messageId, | 3252 | messageId, |
| 3253 | {}, | ||
| 3254 | false, | ||
| 3210 | ); | 3255 | ); |
| 3211 | if (this.messageTextDom instanceof HTMLElement) { | 3256 | if (this.messageTextDom instanceof HTMLElement) { |
| 3212 | this.messageTextDom.innerHTML = formattedText; | 3257 | this.messageTextDom.innerHTML = formattedText; |
| 3213 | } | 3258 | } |
| 3259 | |||
| 3260 | const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount); | ||
| 3214 | if (this.messageTimerDom instanceof HTMLElement) { | 3261 | if (this.messageTimerDom instanceof HTMLElement) { |
| 3215 | this.messageTimerDom.textContent = timePassed.timerValue; | 3262 | this.messageTimerDom.textContent = timePassed.timerValue; |
| 3216 | this.messageTimerDom.title = timePassed.timerTitle; | 3263 | this.messageTimerDom.title = timePassed.timerTitle; |
| 3217 | } | 3264 | } |
| 3265 | |||
| 3218 | this.setFirstSwipe(messageId); | 3266 | this.setFirstSwipe(messageId); |
| 3219 | } | 3267 | } |
| 3220 | 3268 | ||
| @@ -3255,39 +3303,11 @@ class StreamingProcessor { | |||
| 3255 | unblockGeneration(); | 3303 | unblockGeneration(); |
| 3256 | generatedPromptCache = ''; | 3304 | generatedPromptCache = ''; |
| 3257 | 3305 | ||
| 3258 | //console.log("Generated text size:", text.length, text) | ||
| 3259 | |||
| 3260 | const isAborted = this.abortController.signal.aborted; | 3306 | const isAborted = this.abortController.signal.aborted; |
| 3261 | if (power_user.auto_swipe && !isAborted) { | 3307 | if (!isAborted && power_user.auto_swipe && generatedTextFiltered(text)) { |
| 3262 | function containsBlacklistedWords(str, blacklist, threshold) { | 3308 | return swipe_right(); |
| 3263 | const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi'); | ||
| 3264 | const matches = str.match(regex) || []; | ||
| 3265 | return matches.length >= threshold; | ||
| 3266 | } | ||
| 3267 | |||
| 3268 | const generatedTextFiltered = (text) => { | ||
| 3269 | if (text) { | ||
| 3270 | if (power_user.auto_swipe_minimum_length) { | ||
| 3271 | if (text.length < power_user.auto_swipe_minimum_length && text.length !== 0) { | ||
| 3272 | console.log('Generated text size too small'); | ||
| 3273 | return true; | ||
| 3274 | } | ||
| 3275 | } | 3309 | } |
| 3276 | if (power_user.auto_swipe_blacklist_threshold) { | ||
| 3277 | if (containsBlacklistedWords(text, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) { | ||
| 3278 | console.log('Generated text has blacklisted words'); | ||
| 3279 | return true; | ||
| 3280 | } | ||
| 3281 | } | ||
| 3282 | } | ||
| 3283 | return false; | ||
| 3284 | }; | ||
| 3285 | 3310 | ||
| 3286 | if (generatedTextFiltered(text)) { | ||
| 3287 | swipe_right(); | ||
| 3288 | return; | ||
| 3289 | } | ||
| 3290 | } | ||
| 3291 | playMessageSound(); | 3311 | playMessageSound(); |
| 3292 | } | 3312 | } |
| 3293 | 3313 | ||
| @@ -3321,7 +3341,7 @@ class StreamingProcessor { | |||
| 3321 | } | 3341 | } |
| 3322 | 3342 | ||
| 3323 | /** | 3343 | /** |
| 3324 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[] }, void, void>} | 3344 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>} |
| 3325 | */ | 3345 | */ |
| 3326 | *nullStreamingGeneration() { | 3346 | *nullStreamingGeneration() { |
| 3327 | throw new Error('Generation function for streaming is not hooked up'); | 3347 | throw new Error('Generation function for streaming is not hooked up'); |
| @@ -3343,9 +3363,9 @@ class StreamingProcessor { | |||
| 3343 | try { | 3363 | try { |
| 3344 | const sw = new Stopwatch(1000 / power_user.streaming_fps); | 3364 | const sw = new Stopwatch(1000 / power_user.streaming_fps); |
| 3345 | const timestamps = []; | 3365 | const timestamps = []; |
| 3346 | for await (const { text, swipes, logprobs, toolCalls } of this.generator()) { | 3366 | for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) { |
| 3347 | timestamps.push(Date.now()); | 3367 | timestamps.push(Date.now()); |
| 3348 | if (this.isStopped) { | 3368 | if (this.isStopped || this.abortController.signal.aborted) { |
| 3349 | return; | 3369 | return; |
| 3350 | } | 3370 | } |
| 3351 | 3371 | ||
| @@ -3355,6 +3375,7 @@ class StreamingProcessor { | |||
| 3355 | if (logprobs) { | 3375 | if (logprobs) { |
| 3356 | this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs])); | 3376 | this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs])); |
| 3357 | } | 3377 | } |
| 3378 | this.reasoning = getRegexedString(state?.reasoning ?? '', regex_placement.REASONING); | ||
| 3358 | await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text); | 3379 | await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text); |
| 3359 | await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text)); | 3380 | await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text)); |
| 3360 | } | 3381 | } |
| @@ -3840,6 +3861,27 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 3840 | }; | 3861 | }; |
| 3841 | })); | 3862 | })); |
| 3842 | 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 | |||
| 3843 | // Determine token limit | 3885 | // Determine token limit |
| 3844 | let this_max_context = getMaxContextSize(); | 3886 | let this_max_context = getMaxContextSize(); |
| 3845 | 3887 | ||
| @@ -4742,11 +4784,17 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4742 | //const getData = await response.json(); | 4784 | //const getData = await response.json(); |
| 4743 | let getMessage = extractMessageFromData(data); | 4785 | let getMessage = extractMessageFromData(data); |
| 4744 | let title = extractTitleFromData(data); | 4786 | let title = extractTitleFromData(data); |
| 4787 | let reasoning = extractReasoningFromData(data); | ||
| 4745 | kobold_horde_model = title; | 4788 | kobold_horde_model = title; |
| 4746 | 4789 | ||
| 4747 | const swipes = extractMultiSwipes(data, type); | 4790 | const swipes = extractMultiSwipes(data, type); |
| 4748 | 4791 | ||
| 4749 | messageChunk = cleanUpMessage(getMessage, isImpersonate, isContinue, false); | 4792 | messageChunk = cleanUpMessage(getMessage, isImpersonate, isContinue, false); |
| 4793 | reasoning = getRegexedString(reasoning, regex_placement.REASONING); | ||
| 4794 | |||
| 4795 | if (power_user.trim_spaces) { | ||
| 4796 | reasoning = reasoning.trim(); | ||
| 4797 | } | ||
| 4750 | 4798 | ||
| 4751 | if (isContinue) { | 4799 | if (isContinue) { |
| 4752 | getMessage = continue_mag + getMessage; | 4800 | getMessage = continue_mag + getMessage; |
| @@ -4768,10 +4816,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4768 | else { | 4816 | else { |
| 4769 | // Without streaming we'll be having a full message on continuation. Treat it as a last chunk. | 4817 | // Without streaming we'll be having a full message on continuation. Treat it as a last chunk. |
| 4770 | if (originalType !== 'continue') { | 4818 | if (originalType !== 'continue') { |
| 4771 | ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes)); | 4819 | ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes, reasoning)); |
| 4772 | } | 4820 | } |
| 4773 | else { | 4821 | else { |
| 4774 | ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes)); | 4822 | ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes, reasoning)); |
| 4775 | } | 4823 | } |
| 4776 | 4824 | ||
| 4777 | // This relies on `saveReply` having been called to add the message to the chat, so it must be last. | 4825 | // This relies on `saveReply` having been called to add the message to the chat, so it must be last. |
| @@ -4805,32 +4853,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4805 | } | 4853 | } |
| 4806 | 4854 | ||
| 4807 | const isAborted = abortController && abortController.signal.aborted; | 4855 | const isAborted = abortController && abortController.signal.aborted; |
| 4808 | if (power_user.auto_swipe && !isAborted) { | 4856 | if (!isAborted && power_user.auto_swipe && generatedTextFiltered(getMessage)) { |
| 4809 | console.debug('checking for autoswipeblacklist on non-streaming message'); | ||
| 4810 | function containsBlacklistedWords(getMessage, blacklist, threshold) { | ||
| 4811 | console.debug('checking blacklisted words'); | ||
| 4812 | const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi'); | ||
| 4813 | const matches = getMessage.match(regex) || []; | ||
| 4814 | return matches.length >= threshold; | ||
| 4815 | } | ||
| 4816 | |||
| 4817 | const generatedTextFiltered = (getMessage) => { | ||
| 4818 | if (power_user.auto_swipe_blacklist_threshold) { | ||
| 4819 | if (containsBlacklistedWords(getMessage, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) { | ||
| 4820 | console.debug('Generated text has blacklisted words'); | ||
| 4821 | return true; | ||
| 4822 | } | ||
| 4823 | } | ||
| 4824 | |||
| 4825 | return false; | ||
| 4826 | }; | ||
| 4827 | if (generatedTextFiltered(getMessage)) { | ||
| 4828 | console.debug('swiping right automatically'); | ||
| 4829 | is_send_press = false; | 4857 | is_send_press = false; |
| 4830 | swipe_right(); | 4858 | return swipe_right(); |
| 4831 | // TODO: do we want to resolve after an auto-swipe? | ||
| 4832 | return; | ||
| 4833 | } | ||
| 4834 | } | 4859 | } |
| 4835 | 4860 | ||
| 4836 | console.debug('/api/chats/save called by /Generate'); | 4861 | console.debug('/api/chats/save called by /Generate'); |
| @@ -5659,7 +5684,6 @@ function extractMessageFromData(data) { | |||
| 5659 | return data; | 5684 | return data; |
| 5660 | } | 5685 | } |
| 5661 | 5686 | ||
| 5662 | function getTextContext() { | ||
| 5663 | switch (main_api) { | 5687 | switch (main_api) { |
| 5664 | case 'kobold': | 5688 | case 'kobold': |
| 5665 | return data.results[0].text; | 5689 | return data.results[0].text; |
| @@ -5676,14 +5700,35 @@ function extractMessageFromData(data) { | |||
| 5676 | } | 5700 | } |
| 5677 | } | 5701 | } |
| 5678 | 5702 | ||
| 5679 | const content = getTextContext(); | 5703 | /** |
| 5704 | * Extracts the reasoning from the response data. | ||
| 5705 | * @param {object} data Response data | ||
| 5706 | * @returns {string} Extracted reasoning | ||
| 5707 | */ | ||
| 5708 | function extractReasoningFromData(data) { | ||
| 5709 | switch (main_api) { | ||
| 5710 | case 'textgenerationwebui': | ||
| 5711 | switch (textgen_settings.type) { | ||
| 5712 | case textgen_types.OPENROUTER: | ||
| 5713 | return data?.choices?.[0]?.reasoning ?? ''; | ||
| 5714 | } | ||
| 5715 | break; | ||
| 5680 | 5716 | ||
| 5681 | if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) { | 5717 | case 'openai': |
| 5682 | const thoughts = data?.choices?.[0]?.message?.reasoning_content ?? ''; | 5718 | if (!oai_settings.show_thoughts) break; |
| 5683 | return [thoughts, content].filter(x => x).join('\n\n'); | 5719 | |
| 5720 | switch (oai_settings.chat_completion_source) { | ||
| 5721 | case chat_completion_sources.DEEPSEEK: | ||
| 5722 | return data?.choices?.[0]?.message?.reasoning_content ?? ''; | ||
| 5723 | case chat_completion_sources.OPENROUTER: | ||
| 5724 | return data?.choices?.[0]?.message?.reasoning ?? ''; | ||
| 5725 | case chat_completion_sources.MAKERSUITE: | ||
| 5726 | return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; | ||
| 5727 | } | ||
| 5728 | break; | ||
| 5684 | } | 5729 | } |
| 5685 | 5730 | ||
| 5686 | return content; | 5731 | return ''; |
| 5687 | } | 5732 | } |
| 5688 | 5733 | ||
| 5689 | /** | 5734 | /** |
| @@ -5866,7 +5911,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc | |||
| 5866 | return getMessage; | 5911 | return getMessage; |
| 5867 | } | 5912 | } |
| 5868 | 5913 | ||
| 5869 | export async function saveReply(type, getMessage, fromStreaming, title, swipes) { | 5914 | export async function saveReply(type, getMessage, fromStreaming, title, swipes, reasoning) { |
| 5870 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || | 5915 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || |
| 5871 | chat[chat.length - 1]['is_user'])) { | 5916 | chat[chat.length - 1]['is_user'])) { |
| 5872 | type = 'normal'; | 5917 | type = 'normal'; |
| @@ -5891,8 +5936,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5891 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5936 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5892 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5937 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5893 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 5938 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5939 | chat[chat.length - 1]['extra']['reasoning'] = reasoning; | ||
| 5894 | if (power_user.message_token_count_enabled) { | 5940 | if (power_user.message_token_count_enabled) { |
| 5895 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 5941 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 5942 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5896 | } | 5943 | } |
| 5897 | const chat_id = (chat.length - 1); | 5944 | const chat_id = (chat.length - 1); |
| 5898 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); | 5945 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5911,8 +5958,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5911 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5958 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5912 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5959 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5913 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 5960 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5961 | chat[chat.length - 1]['extra']['reasoning'] += reasoning; | ||
| 5914 | if (power_user.message_token_count_enabled) { | 5962 | if (power_user.message_token_count_enabled) { |
| 5915 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 5963 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 5964 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5916 | } | 5965 | } |
| 5917 | const chat_id = (chat.length - 1); | 5966 | const chat_id = (chat.length - 1); |
| 5918 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); | 5967 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5928,8 +5977,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5928 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5977 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5929 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5978 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5930 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 5979 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5980 | chat[chat.length - 1]['extra']['reasoning'] += reasoning; | ||
| 5931 | if (power_user.message_token_count_enabled) { | 5981 | if (power_user.message_token_count_enabled) { |
| 5932 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 5982 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 5983 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5933 | } | 5984 | } |
| 5934 | const chat_id = (chat.length - 1); | 5985 | const chat_id = (chat.length - 1); |
| 5935 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); | 5986 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5945,6 +5996,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5945 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5996 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5946 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5997 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5947 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 5998 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5999 | chat[chat.length - 1]['extra']['reasoning'] = reasoning; | ||
| 5948 | if (power_user.trim_spaces) { | 6000 | if (power_user.trim_spaces) { |
| 5949 | getMessage = getMessage.trim(); | 6001 | getMessage = getMessage.trim(); |
| 5950 | } | 6002 | } |
| @@ -5954,7 +6006,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5954 | chat[chat.length - 1]['gen_finished'] = generationFinished; | 6006 | chat[chat.length - 1]['gen_finished'] = generationFinished; |
| 5955 | 6007 | ||
| 5956 | if (power_user.message_token_count_enabled) { | 6008 | if (power_user.message_token_count_enabled) { |
| 5957 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 6009 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 6010 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5958 | } | 6011 | } |
| 5959 | 6012 | ||
| 5960 | if (selected_group) { | 6013 | if (selected_group) { |
| @@ -6019,6 +6072,19 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 6019 | return { type, getMessage }; | 6072 | return { type, getMessage }; |
| 6020 | } | 6073 | } |
| 6021 | 6074 | ||
| 6075 | export function syncCurrentSwipeInfoExtras() { | ||
| 6076 | if (!chat.length) { | ||
| 6077 | return; | ||
| 6078 | } | ||
| 6079 | const currentMessage = chat[chat.length - 1]; | ||
| 6080 | if (currentMessage && Array.isArray(currentMessage.swipe_info) && typeof currentMessage.swipe_id === 'number') { | ||
| 6081 | const swipeInfo = currentMessage.swipe_info[currentMessage.swipe_id]; | ||
| 6082 | if (swipeInfo && typeof swipeInfo === 'object') { | ||
| 6083 | swipeInfo.extra = structuredClone(currentMessage.extra); | ||
| 6084 | } | ||
| 6085 | } | ||
| 6086 | } | ||
| 6087 | |||
| 6022 | function saveImageToMessage(img, mes) { | 6088 | function saveImageToMessage(img, mes) { |
| 6023 | if (mes && img.image) { | 6089 | if (mes && img.image) { |
| 6024 | if (!mes.extra || typeof mes.extra !== 'object') { | 6090 | if (!mes.extra || typeof mes.extra !== 'object') { |
| @@ -7122,9 +7188,11 @@ function messageEditAuto(div) { | |||
| 7122 | mes.is_system, | 7188 | mes.is_system, |
| 7123 | mes.is_user, | 7189 | mes.is_user, |
| 7124 | this_edit_mes_id, | 7190 | this_edit_mes_id, |
| 7191 | {}, | ||
| 7192 | false, | ||
| 7125 | )); | 7193 | )); |
| 7126 | mesBlock.find('.mes_bias').empty(); | 7194 | mesBlock.find('.mes_bias').empty(); |
| 7127 | mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1)); | 7195 | mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false)); |
| 7128 | saveChatDebounced(); | 7196 | saveChatDebounced(); |
| 7129 | } | 7197 | } |
| 7130 | 7198 | ||
| @@ -7146,10 +7214,12 @@ async function messageEditDone(div) { | |||
| 7146 | mes.is_system, | 7214 | mes.is_system, |
| 7147 | mes.is_user, | 7215 | mes.is_user, |
| 7148 | this_edit_mes_id, | 7216 | this_edit_mes_id, |
| 7217 | {}, | ||
| 7218 | false, | ||
| 7149 | ), | 7219 | ), |
| 7150 | ); | 7220 | ); |
| 7151 | mesBlock.find('.mes_bias').empty(); | 7221 | mesBlock.find('.mes_bias').empty(); |
| 7152 | mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1)); | 7222 | mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false)); |
| 7153 | appendMediaToMessage(mes, div.closest('.mes')); | 7223 | appendMediaToMessage(mes, div.closest('.mes')); |
| 7154 | addCopyToCodeBlocks(div.closest('.mes')); | 7224 | addCopyToCodeBlocks(div.closest('.mes')); |
| 7155 | 7225 | ||
| @@ -7997,11 +8067,25 @@ function updateEditArrowClasses() { | |||
| 7997 | } | 8067 | } |
| 7998 | } | 8068 | } |
| 7999 | 8069 | ||
| 8000 | function closeMessageEditor() { | 8070 | /** |
| 8071 | * Closes the message editor. | ||
| 8072 | * @param {'message'|'reasoning'|'all'} what What to close. Default is 'all'. | ||
| 8073 | */ | ||
| 8074 | export function closeMessageEditor(what = 'all') { | ||
| 8075 | if (what === 'message' || what === 'all') { | ||
| 8001 | if (this_edit_mes_id) { | 8076 | if (this_edit_mes_id) { |
| 8002 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click(); | 8077 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click(); |
| 8003 | } | 8078 | } |
| 8004 | } | 8079 | } |
| 8080 | if (what === 'reasoning' || what === 'all') { | ||
| 8081 | document.querySelectorAll('.reasoning_edit_textarea').forEach((el) => { | ||
| 8082 | const cancelButton = el.closest('.mes')?.querySelector('.mes_reasoning_edit_cancel'); | ||
| 8083 | if (cancelButton instanceof HTMLElement) { | ||
| 8084 | cancelButton.click(); | ||
| 8085 | } | ||
| 8086 | }); | ||
| 8087 | } | ||
| 8088 | } | ||
| 8005 | 8089 | ||
| 8006 | export function setGenerationProgress(progress) { | 8090 | export function setGenerationProgress(progress) { |
| 8007 | if (!progress) { | 8091 | if (!progress) { |
| @@ -8432,6 +8516,9 @@ function swipe_left() { // when we swipe left..but no generation. | |||
| 8432 | streamingProcessor.onStopStreaming(); | 8516 | streamingProcessor.onStopStreaming(); |
| 8433 | } | 8517 | } |
| 8434 | 8518 | ||
| 8519 | // Make sure ad-hoc changes to extras are saved before swiping away | ||
| 8520 | syncCurrentSwipeInfoExtras(); | ||
| 8521 | |||
| 8435 | const swipe_duration = 120; | 8522 | const swipe_duration = 120; |
| 8436 | const swipe_range = '700px'; | 8523 | const swipe_range = '700px'; |
| 8437 | chat[chat.length - 1]['swipe_id']--; | 8524 | chat[chat.length - 1]['swipe_id']--; |
| @@ -8483,7 +8570,8 @@ function swipe_left() { // when we swipe left..but no generation. | |||
| 8483 | } | 8570 | } |
| 8484 | 8571 | ||
| 8485 | const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`); | 8572 | const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`); |
| 8486 | const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0); | 8573 | const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes; |
| 8574 | const tokenCount = await getTokenCountAsync(tokenCountText, 0); | ||
| 8487 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; | 8575 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8488 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); | 8576 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8489 | } | 8577 | } |
| @@ -8566,6 +8654,9 @@ const swipe_right = () => { | |||
| 8566 | return unblockGeneration(); | 8654 | return unblockGeneration(); |
| 8567 | } | 8655 | } |
| 8568 | 8656 | ||
| 8657 | // Make sure ad-hoc changes to extras are saved before swiping away | ||
| 8658 | syncCurrentSwipeInfoExtras(); | ||
| 8659 | |||
| 8569 | const swipe_duration = 200; | 8660 | const swipe_duration = 200; |
| 8570 | const swipe_range = 700; | 8661 | const swipe_range = 700; |
| 8571 | //console.log(swipe_range); | 8662 | //console.log(swipe_range); |
| @@ -8647,6 +8738,7 @@ const swipe_right = () => { | |||
| 8647 | // resets the timer | 8738 | // resets the timer |
| 8648 | swipeMessage.find('.mes_timer').html(''); | 8739 | swipeMessage.find('.mes_timer').html(''); |
| 8649 | swipeMessage.find('.tokenCounterDisplay').text(''); | 8740 | swipeMessage.find('.tokenCounterDisplay').text(''); |
| 8741 | swipeMessage.find('.mes_reasoning').html(''); | ||
| 8650 | } else { | 8742 | } else { |
| 8651 | //console.log('showing previously generated swipe candidate, or "..."'); | 8743 | //console.log('showing previously generated swipe candidate, or "..."'); |
| 8652 | //console.log('onclick right swipe calling addOneMessage'); | 8744 | //console.log('onclick right swipe calling addOneMessage'); |
| @@ -8657,7 +8749,8 @@ const swipe_right = () => { | |||
| 8657 | chat[chat.length - 1].extra = {}; | 8749 | chat[chat.length - 1].extra = {}; |
| 8658 | } | 8750 | } |
| 8659 | 8751 | ||
| 8660 | const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0); | 8752 | const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes; |
| 8753 | const tokenCount = await getTokenCountAsync(tokenCountText, 0); | ||
| 8661 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; | 8754 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8662 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); | 8755 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8663 | } | 8756 | } |
| @@ -9459,7 +9552,8 @@ function addDebugFunctions() { | |||
| 9459 | message.extra = {}; | 9552 | message.extra = {}; |
| 9460 | } | 9553 | } |
| 9461 | 9554 | ||
| 9462 | message.extra.token_count = await getTokenCountAsync(message.mes, 0); | 9555 | const tokenCountText = (message?.extra?.reasoning || '') + message.mes; |
| 9556 | message.extra.token_count = await getTokenCountAsync(tokenCountText, 0); | ||
| 9463 | } | 9557 | } |
| 9464 | 9558 | ||
| 9465 | await saveChatConditional(); | 9559 | await saveChatConditional(); |
| @@ -10768,6 +10862,8 @@ jQuery(async function () { | |||
| 10768 | chat[this_edit_mes_id].is_system, | 10862 | chat[this_edit_mes_id].is_system, |
| 10769 | chat[this_edit_mes_id].is_user, | 10863 | chat[this_edit_mes_id].is_user, |
| 10770 | this_edit_mes_id, | 10864 | this_edit_mes_id, |
| 10865 | {}, | ||
| 10866 | false, | ||
| 10771 | )); | 10867 | )); |
| 10772 | appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes')); | 10868 | appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes')); |
| 10773 | addCopyToCodeBlocks($(this).closest('.mes')); | 10869 | addCopyToCodeBlocks($(this).closest('.mes')); |
| @@ -11228,14 +11324,15 @@ jQuery(async function () { | |||
| 11228 | 11324 | ||
| 11229 | $(document).keyup(function (e) { | 11325 | $(document).keyup(function (e) { |
| 11230 | if (e.key === 'Escape') { | 11326 | if (e.key === 'Escape') { |
| 11231 | const isEditVisible = $('#curEditTextarea').is(':visible'); | 11327 | const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0; |
| 11232 | if (isEditVisible && power_user.auto_save_msg_edits === false) { | 11328 | if (isEditVisible && power_user.auto_save_msg_edits === false) { |
| 11233 | closeMessageEditor(); | 11329 | closeMessageEditor('all'); |
| 11234 | $('#send_textarea').focus(); | 11330 | $('#send_textarea').focus(); |
| 11235 | return; | 11331 | return; |
| 11236 | } | 11332 | } |
| 11237 | if (isEditVisible && power_user.auto_save_msg_edits === true) { | 11333 | if (isEditVisible && power_user.auto_save_msg_edits === true) { |
| 11238 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click(); | 11334 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click(); |
| 11335 | closeMessageEditor('reasoning'); | ||
| 11239 | $('#send_textarea').focus(); | 11336 | $('#send_textarea').focus(); |
| 11240 | return; | 11337 | return; |
| 11241 | } | 11338 | } |
| @@ -566,7 +566,7 @@ export function initAuthorsNote() { | |||
| 566 | namedArgumentList: [], | 566 | namedArgumentList: [], |
| 567 | unnamedArgumentList: [ | 567 | unnamedArgumentList: [ |
| 568 | new SlashCommandArgument( | 568 | new SlashCommandArgument( |
| 569 | 'position', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'], | 569 | 'role', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'user', 'assistant'], |
| 570 | ), | 570 | ), |
| 571 | ], | 571 | ], |
| 572 | helpString: ` | 572 | helpString: ` |
| @@ -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 | function onLockBackgroundClick(e) { | 104 | function onLockBackgroundClick(e) { |
| 100 | e.stopPropagation(); | 105 | e?.stopPropagation(); |
| 101 | 106 | ||
| 102 | const chatName = getCurrentChatId(); | 107 | const chatName = getCurrentChatId(); |
| 103 | 108 | ||
| @@ -106,7 +111,7 @@ function onLockBackgroundClick(e) { | |||
| 106 | return ''; | 111 | return ''; |
| 107 | } | 112 | } |
| 108 | 113 | ||
| 109 | const relativeBgImage = getUrlParameter(this); | 114 | const relativeBgImage = getUrlParameter(this) ?? background_settings.url; |
| 110 | 115 | ||
| 111 | saveBackgroundMetadata(relativeBgImage); | 116 | saveBackgroundMetadata(relativeBgImage); |
| 112 | setCustomBackground(); | 117 | setCustomBackground(); |
| @@ -114,8 +119,13 @@ function onLockBackgroundClick(e) { | |||
| 114 | return ''; | 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 | function onUnlockBackgroundClick(e) { | 127 | function onUnlockBackgroundClick(e) { |
| 118 | e.stopPropagation(); | 128 | e?.stopPropagation(); |
| 119 | removeBackgroundMetadata(); | 129 | removeBackgroundMetadata(); |
| 120 | unsetCustomBackground(); | 130 | unsetCustomBackground(); |
| 121 | highlightLockedBackground(); | 131 | highlightLockedBackground(); |
| @@ -482,10 +492,10 @@ function highlightNewBackground(bg) { | |||
| 482 | */ | 492 | */ |
| 483 | function setFittingClass(fitting) { | 493 | function setFittingClass(fitting) { |
| 484 | const backgrounds = $('#bg1, #bg_custom'); | 494 | const backgrounds = $('#bg1, #bg_custom'); |
| 485 | backgrounds.toggleClass('cover', fitting === 'cover'); | 495 | for (const option of ['cover', 'contain', 'stretch', 'center']) { |
| 486 | backgrounds.toggleClass('contain', fitting === 'contain'); | 496 | backgrounds.toggleClass(option, option === fitting); |
| 487 | backgrounds.toggleClass('stretch', fitting === 'stretch'); | 497 | } |
| 488 | backgrounds.toggleClass('center', fitting === 'center'); | 498 | background_settings.fitting = fitting; |
| 489 | } | 499 | } |
| 490 | 500 | ||
| 491 | function onBackgroundFilterInput() { | 501 | function onBackgroundFilterInput() { |
| @@ -513,12 +523,12 @@ export function initBackgrounds() { | |||
| 513 | $('#add_bg_button').on('change', onBackgroundUploadSelected); | 523 | $('#add_bg_button').on('change', onBackgroundUploadSelected); |
| 514 | $('#bg-filter').on('input', onBackgroundFilterInput); | 524 | $('#bg-filter').on('input', onBackgroundFilterInput); |
| 515 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg', | 525 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg', |
| 516 | callback: onLockBackgroundClick, | 526 | callback: () => onLockBackgroundClick(new CustomEvent('click')), |
| 517 | aliases: ['bglock'], | 527 | aliases: ['bglock'], |
| 518 | helpString: 'Locks a background for the currently selected chat', | 528 | helpString: 'Locks a background for the currently selected chat', |
| 519 | })); | 529 | })); |
| 520 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg', | 530 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg', |
| 521 | callback: onUnlockBackgroundClick, | 531 | callback: () => onUnlockBackgroundClick(new CustomEvent('click')), |
| 522 | aliases: ['bgunlock'], | 532 | aliases: ['bgunlock'], |
| 523 | helpString: 'Unlocks a background for the currently selected chat', | 533 | helpString: 'Unlocks a background for the currently selected chat', |
| 524 | })); | 534 | })); |
| @@ -30,6 +30,7 @@ const CC_COMMANDS = [ | |||
| 30 | 'api-url', | 30 | 'api-url', |
| 31 | 'model', | 31 | 'model', |
| 32 | 'proxy', | 32 | 'proxy', |
| 33 | 'stop-strings', | ||
| 33 | ]; | 34 | ]; |
| 34 | 35 | ||
| 35 | const TC_COMMANDS = [ | 36 | const TC_COMMANDS = [ |
| @@ -43,6 +44,7 @@ const TC_COMMANDS = [ | |||
| 43 | 'context', | 44 | 'context', |
| 44 | 'instruct-state', | 45 | 'instruct-state', |
| 45 | 'tokenizer', | 46 | 'tokenizer', |
| 47 | 'stop-strings', | ||
| 46 | ]; | 48 | ]; |
| 47 | 49 | ||
| 48 | const FANCY_NAMES = { | 50 | const FANCY_NAMES = { |
| @@ -57,6 +59,7 @@ const FANCY_NAMES = { | |||
| 57 | 'instruct': 'Instruct Template', | 59 | 'instruct': 'Instruct Template', |
| 58 | 'context': 'Context Template', | 60 | 'context': 'Context Template', |
| 59 | 'tokenizer': 'Tokenizer', | 61 | 'tokenizer': 'Tokenizer', |
| 62 | 'stop-strings': 'Custom Stopping Strings', | ||
| 60 | }; | 63 | }; |
| 61 | 64 | ||
| 62 | /** | 65 | /** |
| @@ -138,6 +141,7 @@ const profilesProvider = () => [ | |||
| 138 | * @property {string} [context] Context Template | 141 | * @property {string} [context] Context Template |
| 139 | * @property {string} [instruct-state] Instruct Mode | 142 | * @property {string} [instruct-state] Instruct Mode |
| 140 | * @property {string} [tokenizer] Tokenizer | 143 | * @property {string} [tokenizer] Tokenizer |
| 144 | * @property {string} [stop-strings] Custom Stopping Strings | ||
| 141 | * @property {string[]} [exclude] Commands to exclude | 145 | * @property {string[]} [exclude] Commands to exclude |
| 142 | */ | 146 | */ |
| 143 | 147 | ||
| @@ -883,6 +883,10 @@ export class SlashCommandHandler { | |||
| 883 | } | 883 | } |
| 884 | } | 884 | } |
| 885 | getQuickReply(args) { | 885 | getQuickReply(args) { |
| 886 | if (!args.id && !args.label) { | ||
| 887 | toastr.error('Please provide a valid id or label.'); | ||
| 888 | return ''; | ||
| 889 | } | ||
| 886 | try { | 890 | try { |
| 887 | return JSON.stringify(this.api.getQrByLabel(args.set, args.id !== undefined ? Number(args.id) : args.label)); | 891 | return JSON.stringify(this.api.getQrByLabel(args.set, args.id !== undefined ? Number(args.id) : args.label)); |
| 888 | } catch (ex) { | 892 | } catch (ex) { |
| @@ -94,6 +94,12 @@ | |||
| 94 | <span data-i18n="World Info">World Info</span> | 94 | <span data-i18n="World Info">World Info</span> |
| 95 | </label> | 95 | </label> |
| 96 | </div> | 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 | <div class="flex-container wide100p marginTop5"> | 103 | <div class="flex-container wide100p marginTop5"> |
| 98 | <div class="flex1 flex-container flexNoGap"> | 104 | <div class="flex1 flex-container flexNoGap"> |
| 99 | <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."> | 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 | SLASH_COMMAND: 3, | 20 | SLASH_COMMAND: 3, |
| 21 | // 4 - sendAs (legacy) | 21 | // 4 - sendAs (legacy) |
| 22 | WORLD_INFO: 5, | 22 | WORLD_INFO: 5, |
| 23 | REASONING: 6, | ||
| 23 | }; | 24 | }; |
| 24 | 25 | ||
| 25 | export const substitute_find_regex = { | 26 | export const substitute_find_regex = { |
| @@ -94,7 +95,7 @@ function getRegexedString(rawString, placement, { characterOverride, isMarkdown, | |||
| 94 | // Script applies to Generate and input is Generate | 95 | // Script applies to Generate and input is Generate |
| 95 | (script.promptOnly && isPrompt) || | 96 | (script.promptOnly && isPrompt) || |
| 96 | // 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 | // 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 | (!script.markdownOnly && !script.promptOnly && !isMarkdown) | 98 | (!script.markdownOnly && !script.promptOnly && !isMarkdown && !isPrompt) |
| 98 | ) { | 99 | ) { |
| 99 | if (isEdit && !script.runOnEdit) { | 100 | if (isEdit && !script.runOnEdit) { |
| 100 | console.debug(`getRegexedString: Skipping script ${script.scriptName} because it does not run on edit`); | 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 | * @property {string} replaceString - The replace string | 18 | * @property {string} replaceString - The replace string |
| 19 | * @property {string[]} trimStrings - The trim strings | 19 | * @property {string[]} trimStrings - The trim strings |
| 20 | * @property {string?} findRegex - The find regex | 20 | * @property {string?} findRegex - The find regex |
| 21 | * @property {string?} substituteRegex - The substitute regex | 21 | * @property {number?} substituteRegex - The substitute regex |
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| @@ -388,7 +388,7 @@ class AllTalkTtsProvider { | |||
| 388 | } | 388 | } |
| 389 | 389 | ||
| 390 | async fetchRvcVoiceObjects() { | 390 | async fetchRvcVoiceObjects() { |
| 391 | if (this.settings.server_version == 'v2') { | 391 | if (this.settings.server_version == 'v1') { |
| 392 | console.log('Skipping RVC voices fetch for V1 server'); | 392 | console.log('Skipping RVC voices fetch for V1 server'); |
| 393 | return []; | 393 | return []; |
| 394 | } | 394 | } |
| @@ -1031,14 +1031,18 @@ class AllTalkTtsProvider { | |||
| 1031 | console.error('fetchTtsGeneration Error Response Text:', errorText); | 1031 | console.error('fetchTtsGeneration Error Response Text:', errorText); |
| 1032 | throw new Error(`HTTP ${response.status}: ${errorText}`); | 1032 | throw new Error(`HTTP ${response.status}: ${errorText}`); |
| 1033 | } | 1033 | } |
| 1034 | |||
| 1034 | const data = await response.json(); | 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 | const outputUrl = this.settings.server_version === 'v1' | 1038 | 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 | } catch (error) { | 1046 | } catch (error) { |
| 1043 | console.error('[fetchTtsGeneration] Exception caught:', error); | 1047 | console.error('[fetchTtsGeneration] Exception caught:', error); |
| 1044 | throw error; | 1048 | throw error; |
| @@ -29,6 +29,7 @@ import { POPUP_TYPE, callGenericPopup } from '../../popup.js'; | |||
| 29 | import { GoogleTranslateTtsProvider } from './google-translate.js'; | 29 | import { GoogleTranslateTtsProvider } from './google-translate.js'; |
| 30 | 30 | ||
| 31 | const UPDATE_INTERVAL = 1000; | 31 | const UPDATE_INTERVAL = 1000; |
| 32 | const wrapper = new ModuleWorkerWrapper(moduleWorker); | ||
| 32 | 33 | ||
| 33 | let voiceMapEntries = []; | 34 | let voiceMapEntries = []; |
| 34 | let voiceMap = {}; // {charName:voiceid, charName2:voiceid2} | 35 | let voiceMap = {}; // {charName:voiceid, charName2:voiceid2} |
| @@ -118,7 +119,7 @@ async function onNarrateOneMessage() { | |||
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | resetTtsPlayback(); | 121 | resetTtsPlayback(); |
| 121 | ttsJobQueue.push(message); | 122 | processAndQueueTtsMessage(message); |
| 122 | moduleWorker(); | 123 | moduleWorker(); |
| 123 | } | 124 | } |
| 124 | 125 | ||
| @@ -145,7 +146,7 @@ async function onNarrateText(args, text) { | |||
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | resetTtsPlayback(); | 148 | resetTtsPlayback(); |
| 148 | ttsJobQueue.push({ mes: text, name: name }); | 149 | processAndQueueTtsMessage({ mes: text, name: name }); |
| 149 | await moduleWorker(); | 150 | await moduleWorker(); |
| 150 | 151 | ||
| 151 | // Return back to the chat voices | 152 | // Return back to the chat voices |
| @@ -197,6 +198,36 @@ function isTtsProcessing() { | |||
| 197 | return processing; | 198 | return processing; |
| 198 | } | 199 | } |
| 199 | 200 | ||
| 201 | /** | ||
| 202 | * Splits a message into lines and adds each non-empty line to the TTS job queue. | ||
| 203 | * @param {Object} message - The message object to be processed. | ||
| 204 | * @param {string} message.mes - The text of the message to be split into lines. | ||
| 205 | * @param {string} message.name - The name associated with the message. | ||
| 206 | * @returns {void} | ||
| 207 | */ | ||
| 208 | function processAndQueueTtsMessage(message) { | ||
| 209 | if (!extension_settings.tts.narrate_by_paragraphs) { | ||
| 210 | ttsJobQueue.push(message); | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | const lines = message.mes.split('\n'); | ||
| 215 | |||
| 216 | for (let i = 0; i < lines.length; i++) { | ||
| 217 | const line = lines[i]; | ||
| 218 | |||
| 219 | if (line.length === 0) { | ||
| 220 | continue; | ||
| 221 | } | ||
| 222 | |||
| 223 | ttsJobQueue.push( | ||
| 224 | Object.assign({}, message, { | ||
| 225 | mes: line, | ||
| 226 | }), | ||
| 227 | ); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 200 | function debugTtsPlayback() { | 231 | function debugTtsPlayback() { |
| 201 | console.log(JSON.stringify( | 232 | console.log(JSON.stringify( |
| 202 | { | 233 | { |
| @@ -326,7 +357,7 @@ function onAudioControlClicked() { | |||
| 326 | resetTtsPlayback(); | 357 | resetTtsPlayback(); |
| 327 | } else { | 358 | } else { |
| 328 | // Default play behavior if not processing or playing is to play the last message. | 359 | // Default play behavior if not processing or playing is to play the last message. |
| 329 | ttsJobQueue.push(context.chat[context.chat.length - 1]); | 360 | processAndQueueTtsMessage(context.chat[context.chat.length - 1]); |
| 330 | } | 361 | } |
| 331 | updateUiAudioPlayState(); | 362 | updateUiAudioPlayState(); |
| 332 | } | 363 | } |
| @@ -351,6 +382,7 @@ function completeCurrentAudioJob() { | |||
| 351 | audioQueueProcessorReady = true; | 382 | audioQueueProcessorReady = true; |
| 352 | currentAudioJob = null; | 383 | currentAudioJob = null; |
| 353 | // updateUiPlayState(); | 384 | // updateUiPlayState(); |
| 385 | wrapper.update(); | ||
| 354 | } | 386 | } |
| 355 | 387 | ||
| 356 | /** | 388 | /** |
| @@ -440,7 +472,7 @@ async function processTtsQueue() { | |||
| 440 | } | 472 | } |
| 441 | 473 | ||
| 442 | if (extension_settings.tts.skip_tags) { | 474 | if (extension_settings.tts.skip_tags) { |
| 443 | text = text.replace(/<.*?>.*?<\/.*?>/g, '').trim(); | 475 | text = text.replace(/<.*?>[\s\S]*?<\/.*?>/g, '').trim(); |
| 444 | } | 476 | } |
| 445 | 477 | ||
| 446 | if (!extension_settings.tts.pass_asterisks) { | 478 | if (!extension_settings.tts.pass_asterisks) { |
| @@ -543,6 +575,7 @@ function loadSettings() { | |||
| 543 | $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only); | 575 | $('#tts_narrate_quoted').prop('checked', extension_settings.tts.narrate_quoted_only); |
| 544 | $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation); | 576 | $('#tts_auto_generation').prop('checked', extension_settings.tts.auto_generation); |
| 545 | $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation); | 577 | $('#tts_periodic_auto_generation').prop('checked', extension_settings.tts.periodic_auto_generation); |
| 578 | $('#tts_narrate_by_paragraphs').prop('checked', extension_settings.tts.narrate_by_paragraphs); | ||
| 546 | $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only); | 579 | $('#tts_narrate_translated_only').prop('checked', extension_settings.tts.narrate_translated_only); |
| 547 | $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user); | 580 | $('#tts_narrate_user').prop('checked', extension_settings.tts.narrate_user); |
| 548 | $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks); | 581 | $('#tts_pass_asterisks').prop('checked', extension_settings.tts.pass_asterisks); |
| @@ -612,6 +645,11 @@ function onPeriodicAutoGenerationClick() { | |||
| 612 | saveSettingsDebounced(); | 645 | saveSettingsDebounced(); |
| 613 | } | 646 | } |
| 614 | 647 | ||
| 648 | function onNarrateByParagraphsClick() { | ||
| 649 | extension_settings.tts.narrate_by_paragraphs = !!$('#tts_narrate_by_paragraphs').prop('checked'); | ||
| 650 | saveSettingsDebounced(); | ||
| 651 | } | ||
| 652 | |||
| 615 | 653 | ||
| 616 | function onNarrateDialoguesClick() { | 654 | function onNarrateDialoguesClick() { |
| 617 | extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked'); | 655 | extension_settings.tts.narrate_dialogues_only = !!$('#tts_narrate_dialogues').prop('checked'); |
| @@ -790,7 +828,12 @@ async function onMessageEvent(messageId, lastCharIndex) { | |||
| 790 | lastChatId = context.chatId; | 828 | lastChatId = context.chatId; |
| 791 | 829 | ||
| 792 | console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`); | 830 | console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`); |
| 831 | |||
| 832 | if (extension_settings.tts.periodic_auto_generation) { | ||
| 793 | ttsJobQueue.push(message); | 833 | ttsJobQueue.push(message); |
| 834 | } else { | ||
| 835 | processAndQueueTtsMessage(message); | ||
| 836 | } | ||
| 794 | } | 837 | } |
| 795 | 838 | ||
| 796 | async function onMessageDeleted() { | 839 | async function onMessageDeleted() { |
| @@ -1130,6 +1173,7 @@ jQuery(async function () { | |||
| 1130 | $('#tts_pass_asterisks').on('click', onPassAsterisksClick); | 1173 | $('#tts_pass_asterisks').on('click', onPassAsterisksClick); |
| 1131 | $('#tts_auto_generation').on('click', onAutoGenerationClick); | 1174 | $('#tts_auto_generation').on('click', onAutoGenerationClick); |
| 1132 | $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick); | 1175 | $('#tts_periodic_auto_generation').on('click', onPeriodicAutoGenerationClick); |
| 1176 | $('#tts_narrate_by_paragraphs').on('click', onNarrateByParagraphsClick); | ||
| 1133 | $('#tts_narrate_user').on('click', onNarrateUserClick); | 1177 | $('#tts_narrate_user').on('click', onNarrateUserClick); |
| 1134 | 1178 | ||
| 1135 | $('#playback_rate').on('input', function () { | 1179 | $('#playback_rate').on('input', function () { |
| @@ -1151,7 +1195,6 @@ jQuery(async function () { | |||
| 1151 | loadSettings(); // Depends on Extension Controls and loadTtsProvider | 1195 | loadSettings(); // Depends on Extension Controls and loadTtsProvider |
| 1152 | loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies | 1196 | loadTtsProvider(extension_settings.tts.currentProvider); // No dependencies |
| 1153 | addAudioControl(); // Depends on Extension Controls | 1197 | addAudioControl(); // Depends on Extension Controls |
| 1154 | const wrapper = new ModuleWorkerWrapper(moduleWorker); | ||
| 1155 | setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things | 1198 | setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things |
| 1156 | eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback); | 1199 | eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback); |
| 1157 | eventSource.on(event_types.CHAT_CHANGED, onChatChanged); | 1200 | eventSource.on(event_types.CHAT_CHANGED, onChatChanged); |
| @@ -30,6 +30,10 @@ | |||
| 30 | <input type="checkbox" id="tts_periodic_auto_generation"> | 30 | <input type="checkbox" id="tts_periodic_auto_generation"> |
| 31 | <small data-i18n="Narrate by paragraphs (when streaming)">Narrate by paragraphs (when streaming)</small> | 31 | <small data-i18n="Narrate by paragraphs (when streaming)">Narrate by paragraphs (when streaming)</small> |
| 32 | </label> | 32 | </label> |
| 33 | <label class="checkbox_label" for="tts_narrate_by_paragraphs"> | ||
| 34 | <input type="checkbox" id="tts_narrate_by_paragraphs"> | ||
| 35 | <small data-i18n="Narrate by paragraphs (when not streaming)">Narrate by paragraphs (when not streaming)</small> | ||
| 36 | </label> | ||
| 33 | <label class="checkbox_label" for="tts_narrate_quoted"> | 37 | <label class="checkbox_label" for="tts_narrate_quoted"> |
| 34 | <input type="checkbox" id="tts_narrate_quoted"> | 38 | <input type="checkbox" id="tts_narrate_quoted"> |
| 35 | <small data-i18n="Only narrate quotes">Only narrate "quotes"</small> | 39 | <small data-i18n="Only narrate quotes">Only narrate "quotes"</small> |
| @@ -188,7 +188,7 @@ export async function generateKoboldWithStreaming(generate_data, signal) { | |||
| 188 | if (data?.token) { | 188 | if (data?.token) { |
| 189 | text += data.token; | 189 | text += data.token; |
| 190 | } | 190 | } |
| 191 | yield { text, swipes: [], toolCalls: [] }; | 191 | yield { text, swipes: [], toolCalls: [], state: {} }; |
| 192 | } | 192 | } |
| 193 | }; | 193 | }; |
| 194 | } | 194 | } |
| @@ -27,21 +27,42 @@ export async function hideLoader() { | |||
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | return new Promise((resolve) => { | 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 | $('#loader').remove(); | 51 | $('#loader').remove(); |
| 33 | // Yoink preloader entirely; it only exists to cover up unstyled content while loading JS | 52 | // Yoink preloader entirely; it only exists to cover up unstyled content while loading JS |
| 34 | // If it's present, we remove it once and then it's gone. | 53 | // If it's present, we remove it once and then it's gone. |
| 35 | yoinkPreloader(); | 54 | yoinkPreloader(); |
| 36 | 55 | ||
| 37 | loaderPopup.complete(POPUP_RESULT.AFFIRMATIVE).then(() => { | 56 | loaderPopup.complete(POPUP_RESULT.AFFIRMATIVE) |
| 57 | .catch((err) => console.error('Error completing loaderPopup:', err)) | ||
| 58 | .finally(() => { | ||
| 38 | loaderPopup = null; | 59 | loaderPopup = null; |
| 39 | resolve(); | 60 | resolve(); |
| 40 | }); | 61 | }); |
| 41 | }); | 62 | } |
| 42 | 63 | ||
| 43 | $('#load-spinner') | 64 | // Apply the styles |
| 44 | .css({ | 65 | spinner.css({ |
| 45 | 'filter': 'blur(15px)', | 66 | 'filter': 'blur(15px)', |
| 46 | 'opacity': '0', | 67 | 'opacity': '0', |
| 47 | }); | 68 | }); |
| @@ -746,7 +746,7 @@ export async function generateNovelWithStreaming(generate_data, signal) { | |||
| 746 | text += data.token; | 746 | text += data.token; |
| 747 | } | 747 | } |
| 748 | 748 | ||
| 749 | yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [] }; | 749 | yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [], state: {} }; |
| 750 | } | 750 | } |
| 751 | }; | 751 | }; |
| 752 | } | 752 | } |
| @@ -258,7 +258,7 @@ const default_settings = { | |||
| 258 | ai21_model: 'jamba-1.5-large', | 258 | ai21_model: 'jamba-1.5-large', |
| 259 | mistralai_model: 'mistral-large-latest', | 259 | mistralai_model: 'mistral-large-latest', |
| 260 | cohere_model: 'command-r-plus', | 260 | cohere_model: 'command-r-plus', |
| 261 | perplexity_model: 'llama-3.1-70b-instruct', | 261 | perplexity_model: 'sonar-pro', |
| 262 | groq_model: 'llama-3.1-70b-versatile', | 262 | groq_model: 'llama-3.1-70b-versatile', |
| 263 | nanogpt_model: 'gpt-4o-mini', | 263 | nanogpt_model: 'gpt-4o-mini', |
| 264 | zerooneai_model: 'yi-large', | 264 | zerooneai_model: 'yi-large', |
| @@ -298,7 +298,7 @@ const default_settings = { | |||
| 298 | names_behavior: character_names_behavior.DEFAULT, | 298 | names_behavior: character_names_behavior.DEFAULT, |
| 299 | continue_postfix: continue_postfix_types.SPACE, | 299 | continue_postfix: continue_postfix_types.SPACE, |
| 300 | custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, | 300 | custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, |
| 301 | show_thoughts: false, | 301 | show_thoughts: true, |
| 302 | seed: -1, | 302 | seed: -1, |
| 303 | n: 1, | 303 | n: 1, |
| 304 | }; | 304 | }; |
| @@ -337,7 +337,7 @@ const oai_settings = { | |||
| 337 | ai21_model: 'jamba-1.5-large', | 337 | ai21_model: 'jamba-1.5-large', |
| 338 | mistralai_model: 'mistral-large-latest', | 338 | mistralai_model: 'mistral-large-latest', |
| 339 | cohere_model: 'command-r-plus', | 339 | cohere_model: 'command-r-plus', |
| 340 | perplexity_model: 'llama-3.1-70b-instruct', | 340 | perplexity_model: 'sonar-pro', |
| 341 | groq_model: 'llama-3.1-70b-versatile', | 341 | groq_model: 'llama-3.1-70b-versatile', |
| 342 | nanogpt_model: 'gpt-4o-mini', | 342 | nanogpt_model: 'gpt-4o-mini', |
| 343 | zerooneai_model: 'yi-large', | 343 | zerooneai_model: 'yi-large', |
| @@ -377,7 +377,7 @@ const oai_settings = { | |||
| 377 | names_behavior: character_names_behavior.DEFAULT, | 377 | names_behavior: character_names_behavior.DEFAULT, |
| 378 | continue_postfix: continue_postfix_types.SPACE, | 378 | continue_postfix: continue_postfix_types.SPACE, |
| 379 | custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, | 379 | custom_prompt_post_processing: custom_prompt_post_processing_types.NONE, |
| 380 | show_thoughts: false, | 380 | show_thoughts: true, |
| 381 | seed: -1, | 381 | seed: -1, |
| 382 | n: 1, | 382 | n: 1, |
| 383 | }; | 383 | }; |
| @@ -1096,8 +1096,8 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name | |||
| 1096 | // Unordered prompts without marker | 1096 | // Unordered prompts without marker |
| 1097 | { role: 'system', content: impersonationPrompt, identifier: 'impersonate' }, | 1097 | { role: 'system', content: impersonationPrompt, identifier: 'impersonate' }, |
| 1098 | { role: 'system', content: quietPrompt, identifier: 'quietPrompt' }, | 1098 | { role: 'system', content: quietPrompt, identifier: 'quietPrompt' }, |
| 1099 | { role: 'system', content: bias, identifier: 'bias' }, | ||
| 1100 | { role: 'system', content: groupNudge, identifier: 'groupNudge' }, | 1099 | { role: 'system', content: groupNudge, identifier: 'groupNudge' }, |
| 1100 | { role: 'assistant', content: bias, identifier: 'bias' }, | ||
| 1101 | ]; | 1101 | ]; |
| 1102 | 1102 | ||
| 1103 | // Tavern Extras - Summary | 1103 | // Tavern Extras - Summary |
| @@ -1869,7 +1869,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1869 | const isQuiet = type === 'quiet'; | 1869 | const isQuiet = type === 'quiet'; |
| 1870 | const isImpersonate = type === 'impersonate'; | 1870 | const isImpersonate = type === 'impersonate'; |
| 1871 | const isContinue = type === 'continue'; | 1871 | const isContinue = type === 'continue'; |
| 1872 | const stream = oai_settings.stream_openai && !isQuiet && !isScale && !(isGoogle && oai_settings.google_model.includes('bison')) && !(isOAI && oai_settings.openai_model.startsWith('o1-')); | 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 | const useLogprobs = !!power_user.request_token_probabilities; | 1873 | const useLogprobs = !!power_user.request_token_probabilities; |
| 1874 | const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom); | 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 | 'user_name': name1, | 1913 | 'user_name': name1, |
| 1914 | 'char_name': name2, | 1914 | 'char_name': name2, |
| 1915 | 'group_names': getGroupNames(), | 1915 | 'group_names': getGroupNames(), |
| 1916 | 'show_thoughts': Boolean(oai_settings.show_thoughts), | 1916 | 'include_reasoning': Boolean(oai_settings.show_thoughts), |
| 1917 | }; | 1917 | }; |
| 1918 | 1918 | ||
| 1919 | // Empty array will produce a validation error | 1919 | // Empty array will produce a validation error |
| @@ -2050,7 +2050,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2050 | await ToolManager.registerFunctionToolsOpenAI(generate_data); | 2050 | await ToolManager.registerFunctionToolsOpenAI(generate_data); |
| 2051 | } | 2051 | } |
| 2052 | 2052 | ||
| 2053 | if (isOAI && oai_settings.openai_model.startsWith('o1-')) { | 2053 | if (isOAI && (oai_settings.openai_model.startsWith('o1') || oai_settings.openai_model.startsWith('o3'))) { |
| 2054 | generate_data.messages.forEach((msg) => { | 2054 | generate_data.messages.forEach((msg) => { |
| 2055 | if (msg.role === 'system') { | 2055 | if (msg.role === 'system') { |
| 2056 | msg.role = 'user'; | 2056 | msg.role = 'user'; |
| @@ -2095,7 +2095,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2095 | let text = ''; | 2095 | let text = ''; |
| 2096 | const swipes = []; | 2096 | const swipes = []; |
| 2097 | const toolCalls = []; | 2097 | const toolCalls = []; |
| 2098 | const state = {}; | 2098 | const state = { reasoning: '' }; |
| 2099 | while (true) { | 2099 | while (true) { |
| 2100 | const { done, value } = await reader.read(); | 2100 | const { done, value } = await reader.read(); |
| 2101 | if (done) return; | 2101 | if (done) return; |
| @@ -2113,7 +2113,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2113 | 2113 | ||
| 2114 | ToolManager.parseToolCalls(toolCalls, parsed); | 2114 | ToolManager.parseToolCalls(toolCalls, parsed); |
| 2115 | 2115 | ||
| 2116 | yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls }; | 2116 | yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls, state: state }; |
| 2117 | } | 2117 | } |
| 2118 | }; | 2118 | }; |
| 2119 | } | 2119 | } |
| @@ -2150,16 +2150,22 @@ function getStreamingReply(data, state) { | |||
| 2150 | if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { | 2150 | if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { |
| 2151 | return data?.delta?.text || ''; | 2151 | return data?.delta?.text || ''; |
| 2152 | } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { | 2152 | } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { |
| 2153 | return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || ''; | 2153 | if (oai_settings.show_thoughts) { |
| 2154 | state.reasoning += (data?.candidates?.[0]?.content?.parts?.filter(x => x.thought)?.map(x => x.text)?.[0] || ''); | ||
| 2155 | } | ||
| 2156 | return data?.candidates?.[0]?.content?.parts?.filter(x => !x.thought)?.map(x => x.text)?.[0] || ''; | ||
| 2154 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { | 2157 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { |
| 2155 | return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || ''; | 2158 | return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || ''; |
| 2156 | } else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { | 2159 | } else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { |
| 2157 | const hadThoughts = state.hadThoughts; | 2160 | if (oai_settings.show_thoughts) { |
| 2158 | const thoughts = data.choices?.filter(x => oai_settings.show_thoughts || !x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''; | 2161 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''); |
| 2159 | const content = data.choices?.[0]?.delta?.content || ''; | 2162 | } |
| 2160 | state.hadThoughts = !!thoughts; | 2163 | return data.choices?.[0]?.delta?.content || ''; |
| 2161 | const separator = hadThoughts && !thoughts ? '\n\n' : ''; | 2164 | } else if (oai_settings.chat_completion_source === chat_completion_sources.OPENROUTER) { |
| 2162 | return [thoughts, separator, content].filter(x => x).join('\n\n'); | 2165 | if (oai_settings.show_thoughts) { |
| 2166 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || ''); | ||
| 2167 | } | ||
| 2168 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; | ||
| 2163 | } else { | 2169 | } else { |
| 2164 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; | 2170 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; |
| 2165 | } | 2171 | } |
| @@ -4021,7 +4027,7 @@ function getMaxContextOpenAI(value) { | |||
| 4021 | if (oai_settings.max_context_unlocked) { | 4027 | if (oai_settings.max_context_unlocked) { |
| 4022 | return unlocked_max; | 4028 | return unlocked_max; |
| 4023 | } | 4029 | } |
| 4024 | else if (value.startsWith('o1-')) { | 4030 | else if (value.startsWith('o1') || value.startsWith('o3')) { |
| 4025 | return max_128k; | 4031 | return max_128k; |
| 4026 | } | 4032 | } |
| 4027 | 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')) { | 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')) { |
| @@ -4374,28 +4380,19 @@ async function onModelChange() { | |||
| 4374 | if (oai_settings.max_context_unlocked) { | 4380 | if (oai_settings.max_context_unlocked) { |
| 4375 | $('#openai_max_context').attr('max', unlocked_max); | 4381 | $('#openai_max_context').attr('max', unlocked_max); |
| 4376 | } | 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 | } | ||
| 4377 | else if (oai_settings.perplexity_model.includes('llama-3.1')) { | 4389 | else if (oai_settings.perplexity_model.includes('llama-3.1')) { |
| 4378 | const isOnline = oai_settings.perplexity_model.includes('online'); | 4390 | const isOnline = oai_settings.perplexity_model.includes('online'); |
| 4379 | const contextSize = isOnline ? 128 * 1024 - 4000 : 128 * 1024; | 4391 | const contextSize = isOnline ? 128 * 1024 - 4000 : 128 * 1024; |
| 4380 | $('#openai_max_context').attr('max', contextSize); | 4392 | $('#openai_max_context').attr('max', contextSize); |
| 4381 | } | 4393 | } |
| 4382 | else if (['llama-3-sonar-small-32k-chat', 'llama-3-sonar-large-32k-chat'].includes(oai_settings.perplexity_model)) { | ||
| 4383 | $('#openai_max_context').attr('max', max_32k); | ||
| 4384 | } | ||
| 4385 | else if (['llama-3-sonar-small-32k-online', 'llama-3-sonar-large-32k-online'].includes(oai_settings.perplexity_model)) { | ||
| 4386 | $('#openai_max_context').attr('max', 28000); | ||
| 4387 | } | ||
| 4388 | 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)) { | ||
| 4389 | $('#openai_max_context').attr('max', max_16k); | ||
| 4390 | } | ||
| 4391 | else if (['llama-3-8b-instruct', 'llama-3-70b-instruct'].includes(oai_settings.perplexity_model)) { | ||
| 4392 | $('#openai_max_context').attr('max', max_8k); | ||
| 4393 | } | ||
| 4394 | else if (['sonar-small-online', 'sonar-medium-online'].includes(oai_settings.perplexity_model)) { | ||
| 4395 | $('#openai_max_context').attr('max', 12000); | ||
| 4396 | } | ||
| 4397 | else { | 4394 | else { |
| 4398 | $('#openai_max_context').attr('max', max_4k); | 4395 | $('#openai_max_context').attr('max', max_128k); |
| 4399 | } | 4396 | } |
| 4400 | oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); | 4397 | oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); |
| 4401 | $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); | 4398 | $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); |
| @@ -253,6 +253,15 @@ let power_user = { | |||
| 253 | content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.', | 253 | content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.', |
| 254 | }, | 254 | }, |
| 255 | 255 | ||
| 256 | reasoning: { | ||
| 257 | auto_parse: false, | ||
| 258 | add_to_prompts: false, | ||
| 259 | prefix: '<think>\n', | ||
| 260 | suffix: '\n</think>', | ||
| 261 | separator: '\n\n', | ||
| 262 | max_additions: 1, | ||
| 263 | }, | ||
| 264 | |||
| 256 | personas: {}, | 265 | personas: {}, |
| 257 | default_persona: null, | 266 | default_persona: null, |
| 258 | persona_descriptions: {}, | 267 | persona_descriptions: {}, |
| @@ -2910,6 +2919,46 @@ export function flushEphemeralStoppingStrings() { | |||
| 2910 | } | 2919 | } |
| 2911 | 2920 | ||
| 2912 | /** | 2921 | /** |
| 2922 | * Checks if the generated text should be filtered based on the auto-swipe settings. | ||
| 2923 | * @param {string} text The text to check | ||
| 2924 | * @returns {boolean} If the generated text should be filtered | ||
| 2925 | */ | ||
| 2926 | export function generatedTextFiltered(text) { | ||
| 2927 | /** | ||
| 2928 | * Checks if the given text contains any of the blacklisted words. | ||
| 2929 | * @param {string} text The text to check | ||
| 2930 | * @param {string[]} blacklist The list of blacklisted words | ||
| 2931 | * @param {number} threshold The number of blacklisted words that need to be present to trigger the check | ||
| 2932 | * @returns {boolean} Whether the text contains blacklisted words | ||
| 2933 | */ | ||
| 2934 | function containsBlacklistedWords(text, blacklist, threshold) { | ||
| 2935 | const regex = new RegExp(`\\b(${blacklist.join('|')})\\b`, 'gi'); | ||
| 2936 | const matches = text.match(regex) || []; | ||
| 2937 | return matches.length >= threshold; | ||
| 2938 | } | ||
| 2939 | |||
| 2940 | // Make sure a generated text is non-empty | ||
| 2941 | // Otherwise we might get in a loop with a broken API | ||
| 2942 | text = text.trim(); | ||
| 2943 | if (text.length > 0) { | ||
| 2944 | if (power_user.auto_swipe_minimum_length) { | ||
| 2945 | if (text.length < power_user.auto_swipe_minimum_length) { | ||
| 2946 | console.log('Generated text size too small'); | ||
| 2947 | return true; | ||
| 2948 | } | ||
| 2949 | } | ||
| 2950 | if (power_user.auto_swipe_blacklist.length && power_user.auto_swipe_blacklist_threshold) { | ||
| 2951 | if (containsBlacklistedWords(text, power_user.auto_swipe_blacklist, power_user.auto_swipe_blacklist_threshold)) { | ||
| 2952 | console.log('Generated text has blacklisted words'); | ||
| 2953 | return true; | ||
| 2954 | } | ||
| 2955 | } | ||
| 2956 | } | ||
| 2957 | |||
| 2958 | return false; | ||
| 2959 | } | ||
| 2960 | |||
| 2961 | /** | ||
| 2913 | * Gets the custom stopping strings from the power user settings. | 2962 | * Gets the custom stopping strings from the power user settings. |
| 2914 | * @param {number | undefined} limit Number of strings to return. If 0 or undefined, returns all strings. | 2963 | * @param {number | undefined} limit Number of strings to return. If 0 or undefined, returns all strings. |
| 2915 | * @returns {string[]} An array of custom stopping strings | 2964 | * @returns {string[]} An array of custom stopping strings |
| @@ -3880,9 +3929,9 @@ $(document).ready(() => { | |||
| 3880 | helpString: 'Start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.', | 3929 | helpString: 'Start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.', |
| 3881 | })); | 3930 | })); |
| 3882 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | 3931 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 3883 | name: 'delmode', | 3932 | name: 'del', |
| 3884 | callback: doDelMode, | 3933 | callback: doDelMode, |
| 3885 | aliases: ['del'], | 3934 | aliases: ['delete', 'delmode'], |
| 3886 | unnamedArgumentList: [ | 3935 | unnamedArgumentList: [ |
| 3887 | new SlashCommandArgument( | 3936 | new SlashCommandArgument( |
| 3888 | 'optional number', [ARGUMENT_TYPE.NUMBER], false, | 3937 | 'optional number', [ARGUMENT_TYPE.NUMBER], false, |
| @@ -4065,4 +4114,45 @@ $(document).ready(() => { | |||
| 4065 | ], | 4114 | ], |
| 4066 | helpString: 'activates a movingUI preset by name', | 4115 | helpString: 'activates a movingUI preset by name', |
| 4067 | })); | 4116 | })); |
| 4117 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 4118 | name: 'stop-strings', | ||
| 4119 | aliases: ['stopping-strings', 'custom-stopping-strings', 'custom-stop-strings'], | ||
| 4120 | helpString: ` | ||
| 4121 | <div> | ||
| 4122 | Sets a list of custom stopping strings. Gets the list if no value is provided. | ||
| 4123 | </div> | ||
| 4124 | <div> | ||
| 4125 | <strong>Examples:</strong> | ||
| 4126 | </div> | ||
| 4127 | <ul> | ||
| 4128 | <li>Value must be a JSON-serialized array: <pre><code class="language-stscript">/stop-strings ["goodbye", "farewell"]</code></pre></li> | ||
| 4129 | <li>Pipe characters must be escaped with a backslash: <pre><code class="language-stscript">/stop-strings ["left\\|right"]</code></pre></li> | ||
| 4130 | </ul> | ||
| 4131 | `, | ||
| 4132 | returns: ARGUMENT_TYPE.LIST, | ||
| 4133 | unnamedArgumentList: [ | ||
| 4134 | SlashCommandArgument.fromProps({ | ||
| 4135 | description: 'list of strings', | ||
| 4136 | typeList: [ARGUMENT_TYPE.LIST], | ||
| 4137 | acceptsMultiple: false, | ||
| 4138 | isRequired: false, | ||
| 4139 | }), | ||
| 4140 | ], | ||
| 4141 | callback: (_, value) => { | ||
| 4142 | if (String(value ?? '').trim()) { | ||
| 4143 | const parsedValue = ((x) => { try { return JSON.parse(x.toString()); } catch { return null; } })(value); | ||
| 4144 | if (!parsedValue || !Array.isArray(parsedValue)) { | ||
| 4145 | throw new Error('Invalid list format. The value must be a JSON-serialized array of strings.'); | ||
| 4146 | } | ||
| 4147 | parsedValue.forEach((item, index) => { | ||
| 4148 | parsedValue[index] = String(item); | ||
| 4149 | }); | ||
| 4150 | power_user.custom_stopping_strings = JSON.stringify(parsedValue); | ||
| 4151 | $('#custom_stopping_strings').val(power_user.custom_stopping_strings); | ||
| 4152 | saveSettingsDebounced(); | ||
| 4153 | } | ||
| 4154 | |||
| 4155 | return power_user.custom_stopping_strings; | ||
| 4156 | }, | ||
| 4157 | })); | ||
| 4068 | }); | 4158 | }); |
| @@ -586,6 +586,7 @@ class PresetManager { | |||
| 586 | 'tabby_model', | 586 | 'tabby_model', |
| 587 | 'derived', | 587 | 'derived', |
| 588 | 'generic_model', | 588 | 'generic_model', |
| 589 | 'include_reasoning', | ||
| 589 | ]; | 590 | ]; |
| 590 | const settings = Object.assign({}, getSettingsByApiId(this.apiId)); | 591 | const settings = Object.assign({}, getSettingsByApiId(this.apiId)); |
| 591 | 592 | ||
| @@ -0,0 +1,448 @@ | |||
| 1 | import { chat, closeMessageEditor, event_types, eventSource, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js'; | ||
| 2 | import { getRegexedString, regex_placement } from './extensions/regex/engine.js'; | ||
| 3 | import { t } from './i18n.js'; | ||
| 4 | import { MacrosParser } from './macros.js'; | ||
| 5 | import { Popup } from './popup.js'; | ||
| 6 | import { power_user } from './power-user.js'; | ||
| 7 | import { SlashCommand } from './slash-commands/SlashCommand.js'; | ||
| 8 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; | ||
| 9 | import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | ||
| 10 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | ||
| 11 | import { copyText, escapeRegex, isFalseBoolean } from './utils.js'; | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Gets a message from a jQuery element. | ||
| 15 | * @param {Element} element | ||
| 16 | * @returns {{messageId: number, message: object, messageBlock: JQuery<HTMLElement>}} | ||
| 17 | */ | ||
| 18 | function getMessageFromJquery(element) { | ||
| 19 | const messageBlock = $(element).closest('.mes'); | ||
| 20 | const messageId = Number(messageBlock.attr('mesid')); | ||
| 21 | const message = chat[messageId]; | ||
| 22 | return { messageId: messageId, message, messageBlock }; | ||
| 23 | } | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Helper class for adding reasoning to messages. | ||
| 27 | * Keeps track of the number of reasoning additions. | ||
| 28 | */ | ||
| 29 | export class PromptReasoning { | ||
| 30 | static REASONING_PLACEHOLDER = '\u200B'; | ||
| 31 | static REASONING_PLACEHOLDER_REGEX = new RegExp(`${PromptReasoning.REASONING_PLACEHOLDER}$`); | ||
| 32 | |||
| 33 | constructor() { | ||
| 34 | this.counter = 0; | ||
| 35 | } | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Checks if the limit of reasoning additions has been reached. | ||
| 39 | * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise. | ||
| 40 | */ | ||
| 41 | isLimitReached() { | ||
| 42 | if (!power_user.reasoning.add_to_prompts) { | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | return this.counter >= power_user.reasoning.max_additions; | ||
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Add reasoning to a message according to the power user settings. | ||
| 51 | * @param {string} content Message content | ||
| 52 | * @param {string} reasoning Message reasoning | ||
| 53 | * @param {boolean} isPrefix Whether this is the last message prefix | ||
| 54 | * @returns {string} Message content with reasoning | ||
| 55 | */ | ||
| 56 | addToMessage(content, reasoning, isPrefix) { | ||
| 57 | // Disabled or reached limit of additions | ||
| 58 | if (!isPrefix && (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions)) { | ||
| 59 | return content; | ||
| 60 | } | ||
| 61 | |||
| 62 | // No reasoning provided or a placeholder | ||
| 63 | if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) { | ||
| 64 | return content; | ||
| 65 | } | ||
| 66 | |||
| 67 | // Increment the counter | ||
| 68 | this.counter++; | ||
| 69 | |||
| 70 | // Substitute macros in variable parts | ||
| 71 | const prefix = substituteParams(power_user.reasoning.prefix || ''); | ||
| 72 | const separator = substituteParams(power_user.reasoning.separator || ''); | ||
| 73 | const suffix = substituteParams(power_user.reasoning.suffix || ''); | ||
| 74 | |||
| 75 | // Combine parts with reasoning only | ||
| 76 | if (isPrefix && !content) { | ||
| 77 | return `${prefix}${reasoning}`; | ||
| 78 | } | ||
| 79 | |||
| 80 | // Combine parts with reasoning and content | ||
| 81 | return `${prefix}${reasoning}${suffix}${separator}${content}`; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | function loadReasoningSettings() { | ||
| 86 | $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts); | ||
| 87 | $('#reasoning_add_to_prompts').on('change', function () { | ||
| 88 | power_user.reasoning.add_to_prompts = !!$(this).prop('checked'); | ||
| 89 | saveSettingsDebounced(); | ||
| 90 | }); | ||
| 91 | |||
| 92 | $('#reasoning_prefix').val(power_user.reasoning.prefix); | ||
| 93 | $('#reasoning_prefix').on('input', function () { | ||
| 94 | power_user.reasoning.prefix = String($(this).val()); | ||
| 95 | saveSettingsDebounced(); | ||
| 96 | }); | ||
| 97 | |||
| 98 | $('#reasoning_suffix').val(power_user.reasoning.suffix); | ||
| 99 | $('#reasoning_suffix').on('input', function () { | ||
| 100 | power_user.reasoning.suffix = String($(this).val()); | ||
| 101 | saveSettingsDebounced(); | ||
| 102 | }); | ||
| 103 | |||
| 104 | $('#reasoning_separator').val(power_user.reasoning.separator); | ||
| 105 | $('#reasoning_separator').on('input', function () { | ||
| 106 | power_user.reasoning.separator = String($(this).val()); | ||
| 107 | saveSettingsDebounced(); | ||
| 108 | }); | ||
| 109 | |||
| 110 | $('#reasoning_max_additions').val(power_user.reasoning.max_additions); | ||
| 111 | $('#reasoning_max_additions').on('input', function () { | ||
| 112 | power_user.reasoning.max_additions = Number($(this).val()); | ||
| 113 | saveSettingsDebounced(); | ||
| 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 | }); | ||
| 121 | } | ||
| 122 | |||
| 123 | function registerReasoningSlashCommands() { | ||
| 124 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 125 | name: 'reasoning-get', | ||
| 126 | aliases: ['get-reasoning'], | ||
| 127 | returns: ARGUMENT_TYPE.STRING, | ||
| 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.`, | ||
| 129 | unnamedArgumentList: [ | ||
| 130 | SlashCommandArgument.fromProps({ | ||
| 131 | description: 'Message ID. If not provided, the message ID of the last message is used.', | ||
| 132 | typeList: ARGUMENT_TYPE.NUMBER, | ||
| 133 | enumProvider: commonEnumProviders.messages(), | ||
| 134 | }), | ||
| 135 | ], | ||
| 136 | callback: (_args, value) => { | ||
| 137 | const messageId = !isNaN(Number(value)) ? Number(value) : chat.length - 1; | ||
| 138 | const message = chat[messageId]; | ||
| 139 | const reasoning = String(message?.extra?.reasoning ?? ''); | ||
| 140 | return reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, ''); | ||
| 141 | }, | ||
| 142 | })); | ||
| 143 | |||
| 144 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 145 | name: 'reasoning-set', | ||
| 146 | aliases: ['set-reasoning'], | ||
| 147 | returns: ARGUMENT_TYPE.STRING, | ||
| 148 | helpString: t`Set the reasoning block of a message. Returns the reasoning block content.`, | ||
| 149 | namedArgumentList: [ | ||
| 150 | SlashCommandNamedArgument.fromProps({ | ||
| 151 | name: 'at', | ||
| 152 | description: 'Message ID. If not provided, the message ID of the last message is used.', | ||
| 153 | typeList: ARGUMENT_TYPE.NUMBER, | ||
| 154 | enumProvider: commonEnumProviders.messages(), | ||
| 155 | }), | ||
| 156 | ], | ||
| 157 | unnamedArgumentList: [ | ||
| 158 | SlashCommandArgument.fromProps({ | ||
| 159 | description: 'Reasoning block content.', | ||
| 160 | typeList: ARGUMENT_TYPE.STRING, | ||
| 161 | }), | ||
| 162 | ], | ||
| 163 | callback: async (args, value) => { | ||
| 164 | const messageId = !isNaN(Number(args.at)) ? Number(args.at) : chat.length - 1; | ||
| 165 | const message = chat[messageId]; | ||
| 166 | if (!message?.extra) { | ||
| 167 | return ''; | ||
| 168 | } | ||
| 169 | |||
| 170 | message.extra.reasoning = String(value ?? ''); | ||
| 171 | await saveChatConditional(); | ||
| 172 | |||
| 173 | closeMessageEditor('reasoning'); | ||
| 174 | updateMessageBlock(messageId, message); | ||
| 175 | return message.extra.reasoning; | ||
| 176 | }, | ||
| 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 | })); | ||
| 222 | } | ||
| 223 | |||
| 224 | function registerReasoningMacros() { | ||
| 225 | MacrosParser.registerMacro('reasoningPrefix', () => power_user.reasoning.prefix, t`Reasoning Prefix`); | ||
| 226 | MacrosParser.registerMacro('reasoningSuffix', () => power_user.reasoning.suffix, t`Reasoning Suffix`); | ||
| 227 | MacrosParser.registerMacro('reasoningSeparator', () => power_user.reasoning.separator, t`Reasoning Separator`); | ||
| 228 | } | ||
| 229 | |||
| 230 | function setReasoningEventHandlers() { | ||
| 231 | $(document).on('click', '.mes_reasoning_copy', (e) => { | ||
| 232 | e.stopPropagation(); | ||
| 233 | e.preventDefault(); | ||
| 234 | }); | ||
| 235 | |||
| 236 | $(document).on('click', '.mes_reasoning_edit', function (e) { | ||
| 237 | e.stopPropagation(); | ||
| 238 | e.preventDefault(); | ||
| 239 | const { message, messageBlock } = getMessageFromJquery(this); | ||
| 240 | if (!message?.extra) { | ||
| 241 | return; | ||
| 242 | } | ||
| 243 | |||
| 244 | const reasoning = String(message?.extra?.reasoning ?? ''); | ||
| 245 | const chatElement = document.getElementById('chat'); | ||
| 246 | const textarea = document.createElement('textarea'); | ||
| 247 | const reasoningBlock = messageBlock.find('.mes_reasoning'); | ||
| 248 | textarea.classList.add('reasoning_edit_textarea'); | ||
| 249 | textarea.value = reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, ''); | ||
| 250 | $(textarea).insertBefore(reasoningBlock); | ||
| 251 | |||
| 252 | if (!CSS.supports('field-sizing', 'content')) { | ||
| 253 | const resetHeight = function () { | ||
| 254 | const scrollTop = chatElement.scrollTop; | ||
| 255 | textarea.style.height = '0px'; | ||
| 256 | textarea.style.height = `${textarea.scrollHeight}px`; | ||
| 257 | chatElement.scrollTop = scrollTop; | ||
| 258 | }; | ||
| 259 | |||
| 260 | textarea.addEventListener('input', resetHeight); | ||
| 261 | resetHeight(); | ||
| 262 | } | ||
| 263 | |||
| 264 | textarea.focus(); | ||
| 265 | textarea.setSelectionRange(textarea.value.length, textarea.value.length); | ||
| 266 | |||
| 267 | const textareaRect = textarea.getBoundingClientRect(); | ||
| 268 | const chatRect = chatElement.getBoundingClientRect(); | ||
| 269 | |||
| 270 | // Scroll if textarea bottom is below visible area | ||
| 271 | if (textareaRect.bottom > chatRect.bottom) { | ||
| 272 | const scrollOffset = textareaRect.bottom - chatRect.bottom; | ||
| 273 | chatElement.scrollTop += scrollOffset; | ||
| 274 | } | ||
| 275 | }); | ||
| 276 | |||
| 277 | $(document).on('click', '.mes_reasoning_edit_done', async function (e) { | ||
| 278 | e.stopPropagation(); | ||
| 279 | e.preventDefault(); | ||
| 280 | const { message, messageId, messageBlock } = getMessageFromJquery(this); | ||
| 281 | if (!message?.extra) { | ||
| 282 | return; | ||
| 283 | } | ||
| 284 | |||
| 285 | const textarea = messageBlock.find('.reasoning_edit_textarea'); | ||
| 286 | const reasoning = getRegexedString(String(textarea.val()), regex_placement.REASONING, { isEdit: true }); | ||
| 287 | message.extra.reasoning = reasoning; | ||
| 288 | await saveChatConditional(); | ||
| 289 | updateMessageBlock(messageId, message); | ||
| 290 | textarea.remove(); | ||
| 291 | }); | ||
| 292 | |||
| 293 | $(document).on('click', '.mes_reasoning_edit_cancel', function (e) { | ||
| 294 | e.stopPropagation(); | ||
| 295 | e.preventDefault(); | ||
| 296 | |||
| 297 | const { messageBlock } = getMessageFromJquery(this); | ||
| 298 | const textarea = messageBlock.find('.reasoning_edit_textarea'); | ||
| 299 | textarea.remove(); | ||
| 300 | }); | ||
| 301 | |||
| 302 | $(document).on('click', '.mes_edit_add_reasoning', async function () { | ||
| 303 | const { message, messageId } = getMessageFromJquery(this); | ||
| 304 | if (!message?.extra) { | ||
| 305 | return; | ||
| 306 | } | ||
| 307 | |||
| 308 | if (message.extra.reasoning) { | ||
| 309 | toastr.info(t`Reasoning already exists.`, t`Edit Message`); | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | |||
| 313 | message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER; | ||
| 314 | await saveChatConditional(); | ||
| 315 | closeMessageEditor(); | ||
| 316 | updateMessageBlock(messageId, message); | ||
| 317 | }); | ||
| 318 | |||
| 319 | $(document).on('click', '.mes_reasoning_delete', async function (e) { | ||
| 320 | e.stopPropagation(); | ||
| 321 | e.preventDefault(); | ||
| 322 | |||
| 323 | const confirm = await Popup.show.confirm(t`Are you sure you want to clear the reasoning?`, t`Visible message contents will stay intact.`); | ||
| 324 | |||
| 325 | if (!confirm) { | ||
| 326 | return; | ||
| 327 | } | ||
| 328 | |||
| 329 | const { message, messageId } = getMessageFromJquery(this); | ||
| 330 | if (!message?.extra) { | ||
| 331 | return; | ||
| 332 | } | ||
| 333 | message.extra.reasoning = ''; | ||
| 334 | await saveChatConditional(); | ||
| 335 | updateMessageBlock(messageId, message); | ||
| 336 | }); | ||
| 337 | |||
| 338 | $(document).on('pointerup', '.mes_reasoning_copy', async function () { | ||
| 339 | const { message } = getMessageFromJquery(this); | ||
| 340 | const reasoning = String(message?.extra?.reasoning ?? '').replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, ''); | ||
| 341 | |||
| 342 | if (!reasoning) { | ||
| 343 | return; | ||
| 344 | } | ||
| 345 | |||
| 346 | await copyText(reasoning); | ||
| 347 | toastr.info(t`Copied!`, '', { timeOut: 2000 }); | ||
| 348 | }); | ||
| 349 | } | ||
| 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 | |||
| 442 | export function initReasoning() { | ||
| 443 | loadReasoningSettings(); | ||
| 444 | setReasoningEventHandlers(); | ||
| 445 | registerReasoningSlashCommands(); | ||
| 446 | registerReasoningMacros(); | ||
| 447 | registerReasoningAppEvents(); | ||
| 448 | } | ||
| @@ -42,6 +42,7 @@ import { | |||
| 42 | showMoreMessages, | 42 | showMoreMessages, |
| 43 | stopGeneration, | 43 | stopGeneration, |
| 44 | substituteParams, | 44 | substituteParams, |
| 45 | syncCurrentSwipeInfoExtras, | ||
| 45 | system_avatar, | 46 | system_avatar, |
| 46 | system_message_types, | 47 | system_message_types, |
| 47 | this_chid, | 48 | this_chid, |
| @@ -58,7 +59,7 @@ import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, setPersonaLockStat | |||
| 58 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; | 59 | import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js'; |
| 59 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; | 60 | import { SERVER_INPUTS, textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; |
| 60 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; | 61 | import { decodeTextTokens, getAvailableTokenizers, getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, selectTokenizer } from './tokenizers.js'; |
| 61 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; | 62 | import { debounce, delay, equalsIgnoreCaseAndAccents, findChar, getCharIndex, isFalseBoolean, isTrueBoolean, onlyUnique, regexFromString, showFontAwesomePicker, stringToRange, trimToEndSentence, trimToStartSentence, waitUntilCondition } from './utils.js'; |
| 62 | import { registerVariableCommands, resolveVariable } from './variables.js'; | 63 | import { registerVariableCommands, resolveVariable } from './variables.js'; |
| 63 | import { background_settings } from './backgrounds.js'; | 64 | import { background_settings } from './backgrounds.js'; |
| 64 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; | 65 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| @@ -1902,7 +1903,7 @@ export function initDefaultSlashCommands() { | |||
| 1902 | returns: 'uppercase string', | 1903 | returns: 'uppercase string', |
| 1903 | unnamedArgumentList: [ | 1904 | unnamedArgumentList: [ |
| 1904 | new SlashCommandArgument( | 1905 | new SlashCommandArgument( |
| 1905 | 'string', [ARGUMENT_TYPE.STRING], true, false, | 1906 | 'text to affect', [ARGUMENT_TYPE.STRING], true, false, |
| 1906 | ), | 1907 | ), |
| 1907 | ], | 1908 | ], |
| 1908 | helpString: 'Converts the provided string to uppercase.', | 1909 | helpString: 'Converts the provided string to uppercase.', |
| @@ -1914,7 +1915,7 @@ export function initDefaultSlashCommands() { | |||
| 1914 | returns: 'lowercase string', | 1915 | returns: 'lowercase string', |
| 1915 | unnamedArgumentList: [ | 1916 | unnamedArgumentList: [ |
| 1916 | new SlashCommandArgument( | 1917 | new SlashCommandArgument( |
| 1917 | 'string', [ARGUMENT_TYPE.STRING], true, false, | 1918 | 'text to affect', [ARGUMENT_TYPE.STRING], true, false, |
| 1918 | ), | 1919 | ), |
| 1919 | ], | 1920 | ], |
| 1920 | helpString: 'Converts the provided string to lowercase.', | 1921 | helpString: 'Converts the provided string to lowercase.', |
| @@ -1934,7 +1935,7 @@ export function initDefaultSlashCommands() { | |||
| 1934 | ], | 1935 | ], |
| 1935 | unnamedArgumentList: [ | 1936 | unnamedArgumentList: [ |
| 1936 | new SlashCommandArgument( | 1937 | new SlashCommandArgument( |
| 1937 | 'string', [ARGUMENT_TYPE.STRING], true, false, | 1938 | 'text to affect', [ARGUMENT_TYPE.STRING], true, false, |
| 1938 | ), | 1939 | ), |
| 1939 | ], | 1940 | ], |
| 1940 | helpString: ` | 1941 | helpString: ` |
| @@ -1998,6 +1999,62 @@ export function initDefaultSlashCommands() { | |||
| 1998 | return ''; | 1999 | return ''; |
| 1999 | }, | 2000 | }, |
| 2000 | })); | 2001 | })); |
| 2002 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | ||
| 2003 | name: 'replace', | ||
| 2004 | aliases: ['re'], | ||
| 2005 | callback: (async ({ mode = 'literal', pattern, replacer = '' }, text) => { | ||
| 2006 | if (pattern === '') | ||
| 2007 | throw new Error('Argument of \'pattern=\' cannot be empty'); | ||
| 2008 | switch (mode) { | ||
| 2009 | case 'literal': | ||
| 2010 | return text.replaceAll(pattern, replacer); | ||
| 2011 | case 'regex': | ||
| 2012 | return text.replace(regexFromString(pattern), replacer); | ||
| 2013 | default: | ||
| 2014 | throw new Error('Invalid \'/replace mode=\' argument specified!'); | ||
| 2015 | } | ||
| 2016 | }), | ||
| 2017 | returns: 'replaced text', | ||
| 2018 | namedArgumentList: [ | ||
| 2019 | SlashCommandNamedArgument.fromProps({ | ||
| 2020 | name: 'mode', | ||
| 2021 | description: 'Replaces occurrence(s) of a pattern', | ||
| 2022 | typeList: [ARGUMENT_TYPE.STRING], | ||
| 2023 | defaultValue: 'literal', | ||
| 2024 | enumList: ['literal', 'regex'], | ||
| 2025 | }), | ||
| 2026 | new SlashCommandNamedArgument( | ||
| 2027 | 'pattern', 'pattern to search with', [ARGUMENT_TYPE.STRING], true, false, | ||
| 2028 | ), | ||
| 2029 | new SlashCommandNamedArgument( | ||
| 2030 | 'replacer', 'replacement text for matches', [ARGUMENT_TYPE.STRING], false, false, '', | ||
| 2031 | ), | ||
| 2032 | ], | ||
| 2033 | unnamedArgumentList: [ | ||
| 2034 | new SlashCommandArgument( | ||
| 2035 | 'text to affect', [ARGUMENT_TYPE.STRING], true, false, | ||
| 2036 | ), | ||
| 2037 | ], | ||
| 2038 | helpString: ` | ||
| 2039 | <div> | ||
| 2040 | Replaces text within the provided string based on the pattern. | ||
| 2041 | </div> | ||
| 2042 | <div> | ||
| 2043 | If <code>mode</code> is <code>literal</code> (or omitted), <code>pattern</code> is a literal search string (case-sensitive).<br /> | ||
| 2044 | If <code>mode</code> is <code>regex</code>, <code>pattern</code> is parsed as an ECMAScript Regular Expression.<br /> | ||
| 2045 | The <code>replacer</code> replaces based on the <code>pattern</code> in the input text.<br /> | ||
| 2046 | If <code>replacer</code> is omitted, the replacement(s) will be an empty string.<br /> | ||
| 2047 | </div> | ||
| 2048 | <div> | ||
| 2049 | <strong>Example:</strong> | ||
| 2050 | <pre>/let x Blue house and blue car || </pre> | ||
| 2051 | <pre>/replace pattern="blue" {{var::x}} | /echo |/# Blue house and car ||</pre> | ||
| 2052 | <pre>/replace pattern="blue" replacer="red" {{var::x}} | /echo |/# Blue house and red car ||</pre> | ||
| 2053 | <pre>/replace mode=regex pattern="/blue/i" replacer="red" {{var::x}} | /echo |/# red house and blue car ||</pre> | ||
| 2054 | <pre>/replace mode=regex pattern="/blue/gi" replacer="red" {{var::x}} | /echo |/# red house and red car ||</pre> | ||
| 2055 | </div> | ||
| 2056 | `, | ||
| 2057 | })); | ||
| 2001 | 2058 | ||
| 2002 | registerVariableCommands(); | 2059 | registerVariableCommands(); |
| 2003 | } | 2060 | } |
| @@ -2814,8 +2871,11 @@ async function addSwipeCallback(args, value) { | |||
| 2814 | const newSwipeId = lastMessage.swipes.length - 1; | 2871 | const newSwipeId = lastMessage.swipes.length - 1; |
| 2815 | 2872 | ||
| 2816 | if (isTrueBoolean(args.switch)) { | 2873 | if (isTrueBoolean(args.switch)) { |
| 2874 | // Make sure ad-hoc changes to extras are saved before swiping away | ||
| 2875 | syncCurrentSwipeInfoExtras(); | ||
| 2817 | lastMessage.swipe_id = newSwipeId; | 2876 | lastMessage.swipe_id = newSwipeId; |
| 2818 | lastMessage.mes = lastMessage.swipes[newSwipeId]; | 2877 | lastMessage.mes = lastMessage.swipes[newSwipeId]; |
| 2878 | lastMessage.extra = structuredClone(lastMessage.swipe_info?.[newSwipeId]?.extra ?? lastMessage.extra ?? {}); | ||
| 2819 | } | 2879 | } |
| 2820 | 2880 | ||
| 2821 | await saveChatConditional(); | 2881 | await saveChatConditional(); |
| @@ -3081,11 +3141,6 @@ async function removeGroupMemberCallback(_, arg) { | |||
| 3081 | return ''; | 3141 | return ''; |
| 3082 | } | 3142 | } |
| 3083 | 3143 | ||
| 3084 | if (is_group_generating) { | ||
| 3085 | toastr.warning('Cannot run /memberremove command while the group reply is generating.'); | ||
| 3086 | return ''; | ||
| 3087 | } | ||
| 3088 | |||
| 3089 | const chid = findGroupMemberId(arg); | 3144 | const chid = findGroupMemberId(arg); |
| 3090 | 3145 | ||
| 3091 | if (chid === undefined) { | 3146 | if (chid === undefined) { |
| @@ -3190,12 +3245,7 @@ function findPersonaByName(name) { | |||
| 3190 | } | 3245 | } |
| 3191 | 3246 | ||
| 3192 | async function sendUserMessageCallback(args, text) { | 3247 | async function sendUserMessageCallback(args, text) { |
| 3193 | if (!text) { | 3248 | text = String(text ?? '').trim(); |
| 3194 | toastr.warning('You must specify text to send'); | ||
| 3195 | return; | ||
| 3196 | } | ||
| 3197 | |||
| 3198 | text = text.trim(); | ||
| 3199 | const compact = isTrueBoolean(args?.compact); | 3249 | const compact = isTrueBoolean(args?.compact); |
| 3200 | const bias = extractMessageBias(text); | 3250 | const bias = extractMessageBias(text); |
| 3201 | 3251 | ||
| @@ -3504,13 +3554,7 @@ export function getNameAndAvatarForMessage(character, name = null) { | |||
| 3504 | } | 3554 | } |
| 3505 | 3555 | ||
| 3506 | export async function sendMessageAs(args, text) { | 3556 | export async function sendMessageAs(args, text) { |
| 3507 | if (!text) { | ||
| 3508 | toastr.warning('You must specify text to send as'); | ||
| 3509 | return ''; | ||
| 3510 | } | ||
| 3511 | |||
| 3512 | let name = args.name?.trim(); | 3557 | let name = args.name?.trim(); |
| 3513 | let mesText; | ||
| 3514 | 3558 | ||
| 3515 | if (!name) { | 3559 | if (!name) { |
| 3516 | const namelessWarningKey = 'sendAsNamelessWarningShown'; | 3560 | const namelessWarningKey = 'sendAsNamelessWarningShown'; |
| @@ -3521,7 +3565,7 @@ export async function sendMessageAs(args, text) { | |||
| 3521 | name = name2; | 3565 | name = name2; |
| 3522 | } | 3566 | } |
| 3523 | 3567 | ||
| 3524 | mesText = text.trim(); | 3568 | let mesText = String(text ?? '').trim(); |
| 3525 | 3569 | ||
| 3526 | // Requires a regex check after the slash command is pushed to output | 3570 | // Requires a regex check after the slash command is pushed to output |
| 3527 | mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND, { characterOverride: name }); | 3571 | mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND, { characterOverride: name }); |
| @@ -3599,11 +3643,7 @@ export async function sendMessageAs(args, text) { | |||
| 3599 | } | 3643 | } |
| 3600 | 3644 | ||
| 3601 | export async function sendNarratorMessage(args, text) { | 3645 | export async function sendNarratorMessage(args, text) { |
| 3602 | if (!text) { | 3646 | text = String(text ?? ''); |
| 3603 | toastr.warning('You must specify text to send'); | ||
| 3604 | return ''; | ||
| 3605 | } | ||
| 3606 | |||
| 3607 | const name = chat_metadata[NARRATOR_NAME_KEY] || NARRATOR_NAME_DEFAULT; | 3647 | const name = chat_metadata[NARRATOR_NAME_KEY] || NARRATOR_NAME_DEFAULT; |
| 3608 | // Messages that do nothing but set bias will be hidden from the context | 3648 | // Messages that do nothing but set bias will be hidden from the context |
| 3609 | const bias = extractMessageBias(text); | 3649 | const bias = extractMessageBias(text); |
| @@ -3694,18 +3734,13 @@ export async function promptQuietForLoudResponse(who, text) { | |||
| 3694 | } | 3734 | } |
| 3695 | 3735 | ||
| 3696 | async function sendCommentMessage(args, text) { | 3736 | async function sendCommentMessage(args, text) { |
| 3697 | if (!text) { | ||
| 3698 | toastr.warning('You must specify text to send'); | ||
| 3699 | return ''; | ||
| 3700 | } | ||
| 3701 | |||
| 3702 | const compact = isTrueBoolean(args?.compact); | 3737 | const compact = isTrueBoolean(args?.compact); |
| 3703 | const message = { | 3738 | const message = { |
| 3704 | name: COMMENT_NAME_DEFAULT, | 3739 | name: COMMENT_NAME_DEFAULT, |
| 3705 | is_user: false, | 3740 | is_user: false, |
| 3706 | is_system: true, | 3741 | is_system: true, |
| 3707 | send_date: getMessageTimeStamp(), | 3742 | send_date: getMessageTimeStamp(), |
| 3708 | mes: substituteParams(text.trim()), | 3743 | mes: substituteParams(String(text ?? '').trim()), |
| 3709 | force_avatar: comment_avatar, | 3744 | force_avatar: comment_avatar, |
| 3710 | extra: { | 3745 | extra: { |
| 3711 | type: system_message_types.COMMENT, | 3746 | type: system_message_types.COMMENT, |
| @@ -235,6 +235,21 @@ async function* parseStreamData(json) { | |||
| 235 | } | 235 | } |
| 236 | return; | 236 | return; |
| 237 | } | 237 | } |
| 238 | else if (typeof json.choices[0].delta.reasoning === 'string' && json.choices[0].delta.reasoning.length > 0) { | ||
| 239 | for (let j = 0; j < json.choices[0].delta.reasoning.length; j++) { | ||
| 240 | const str = json.choices[0].delta.reasoning[j]; | ||
| 241 | const isLastSymbol = j === json.choices[0].delta.reasoning.length - 1; | ||
| 242 | const choiceClone = structuredClone(json.choices[0]); | ||
| 243 | choiceClone.delta.reasoning = str; | ||
| 244 | choiceClone.delta.content = isLastSymbol ? choiceClone.delta.content : ''; | ||
| 245 | const choices = [choiceClone]; | ||
| 246 | yield { | ||
| 247 | data: { ...json, choices }, | ||
| 248 | chunk: str, | ||
| 249 | }; | ||
| 250 | } | ||
| 251 | return; | ||
| 252 | } | ||
| 238 | else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) { | 253 | else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) { |
| 239 | for (let j = 0; j < json.choices[0].delta.content.length; j++) { | 254 | for (let j = 0; j < json.choices[0].delta.content.length; j++) { |
| 240 | const str = json.choices[0].delta.content[j]; | 255 | const str = json.choices[0].delta.content[j]; |
| @@ -1,6 +1,7 @@ | |||
| 1 | import { | 1 | import { |
| 2 | activateSendButtons, | 2 | activateSendButtons, |
| 3 | addOneMessage, | 3 | addOneMessage, |
| 4 | appendMediaToMessage, | ||
| 4 | callPopup, | 5 | callPopup, |
| 5 | characters, | 6 | characters, |
| 6 | chat, | 7 | chat, |
| @@ -41,6 +42,7 @@ import { | |||
| 41 | substituteParamsExtended, | 42 | substituteParamsExtended, |
| 42 | this_chid, | 43 | this_chid, |
| 43 | updateChatMetadata, | 44 | updateChatMetadata, |
| 45 | updateMessageBlock, | ||
| 44 | } from '../script.js'; | 46 | } from '../script.js'; |
| 45 | import { | 47 | import { |
| 46 | extension_settings, | 48 | extension_settings, |
| @@ -67,6 +69,7 @@ import { textgenerationwebui_settings } from './textgen-settings.js'; | |||
| 67 | import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js'; | 69 | import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js'; |
| 68 | import { ToolManager } from './tool-calling.js'; | 70 | import { ToolManager } from './tool-calling.js'; |
| 69 | import { timestampToMoment, uuidv4 } from './utils.js'; | 71 | import { timestampToMoment, uuidv4 } from './utils.js'; |
| 72 | import { getGlobalVariable, getLocalVariable, setGlobalVariable, setLocalVariable } from './variables.js'; | ||
| 70 | 73 | ||
| 71 | export function getContext() { | 74 | export function getContext() { |
| 72 | return { | 75 | return { |
| @@ -171,6 +174,18 @@ export function getContext() { | |||
| 171 | getCharacters, | 174 | getCharacters, |
| 172 | uuidv4, | 175 | uuidv4, |
| 173 | humanizedDateTime, | 176 | humanizedDateTime, |
| 177 | updateMessageBlock, | ||
| 178 | appendMediaToMessage, | ||
| 179 | variables: { | ||
| 180 | local: { | ||
| 181 | get: getLocalVariable, | ||
| 182 | set: setLocalVariable, | ||
| 183 | }, | ||
| 184 | global: { | ||
| 185 | get: getGlobalVariable, | ||
| 186 | set: setGlobalVariable, | ||
| 187 | }, | ||
| 188 | }, | ||
| 174 | }; | 189 | }; |
| 175 | } | 190 | } |
| 176 | 191 | ||
| @@ -54,6 +54,12 @@ const OPENROUTER_PROVIDERS = [ | |||
| 54 | 'xAI', | 54 | 'xAI', |
| 55 | 'Cloudflare', | 55 | 'Cloudflare', |
| 56 | 'SF Compute', | 56 | 'SF Compute', |
| 57 | 'Minimax', | ||
| 58 | 'Nineteen', | ||
| 59 | 'Liquid', | ||
| 60 | 'Nebius', | ||
| 61 | 'Chutes', | ||
| 62 | 'Kluster', | ||
| 57 | '01.AI', | 63 | '01.AI', |
| 58 | 'HuggingFace', | 64 | 'HuggingFace', |
| 59 | 'Mancer', | 65 | 'Mancer', |
| @@ -172,6 +172,7 @@ const settings = { | |||
| 172 | //truncation_length: 2048, | 172 | //truncation_length: 2048, |
| 173 | ban_eos_token: false, | 173 | ban_eos_token: false, |
| 174 | skip_special_tokens: true, | 174 | skip_special_tokens: true, |
| 175 | include_reasoning: true, | ||
| 175 | streaming: false, | 176 | streaming: false, |
| 176 | mirostat_mode: 0, | 177 | mirostat_mode: 0, |
| 177 | mirostat_tau: 5, | 178 | mirostat_tau: 5, |
| @@ -263,6 +264,7 @@ export const setting_names = [ | |||
| 263 | 'add_bos_token', | 264 | 'add_bos_token', |
| 264 | 'ban_eos_token', | 265 | 'ban_eos_token', |
| 265 | 'skip_special_tokens', | 266 | 'skip_special_tokens', |
| 267 | 'include_reasoning', | ||
| 266 | 'streaming', | 268 | 'streaming', |
| 267 | 'mirostat_mode', | 269 | 'mirostat_mode', |
| 268 | 'mirostat_tau', | 270 | 'mirostat_tau', |
| @@ -501,7 +503,7 @@ export function loadTextGenSettings(data, loadedSettings) { | |||
| 501 | for (const [type, selector] of Object.entries(SERVER_INPUTS)) { | 503 | for (const [type, selector] of Object.entries(SERVER_INPUTS)) { |
| 502 | const control = $(selector); | 504 | const control = $(selector); |
| 503 | control.val(settings.server_urls[type] ?? '').on('input', function () { | 505 | control.val(settings.server_urls[type] ?? '').on('input', function () { |
| 504 | settings.server_urls[type] = String($(this).val()); | 506 | settings.server_urls[type] = String($(this).val()).trim(); |
| 505 | saveSettingsDebounced(); | 507 | saveSettingsDebounced(); |
| 506 | }); | 508 | }); |
| 507 | } | 509 | } |
| @@ -740,6 +742,7 @@ jQuery(function () { | |||
| 740 | 'add_bos_token_textgenerationwebui': true, | 742 | 'add_bos_token_textgenerationwebui': true, |
| 741 | 'temperature_last_textgenerationwebui': true, | 743 | 'temperature_last_textgenerationwebui': true, |
| 742 | 'skip_special_tokens_textgenerationwebui': true, | 744 | 'skip_special_tokens_textgenerationwebui': true, |
| 745 | 'include_reasoning_textgenerationwebui': true, | ||
| 743 | 'top_a_textgenerationwebui': 0, | 746 | 'top_a_textgenerationwebui': 0, |
| 744 | 'top_a_counter_textgenerationwebui': 0, | 747 | 'top_a_counter_textgenerationwebui': 0, |
| 745 | 'mirostat_mode_textgenerationwebui': 0, | 748 | 'mirostat_mode_textgenerationwebui': 0, |
| @@ -986,6 +989,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | |||
| 986 | let logprobs = null; | 989 | let logprobs = null; |
| 987 | const swipes = []; | 990 | const swipes = []; |
| 988 | const toolCalls = []; | 991 | const toolCalls = []; |
| 992 | const state = { reasoning: '' }; | ||
| 989 | while (true) { | 993 | while (true) { |
| 990 | const { done, value } = await reader.read(); | 994 | const { done, value } = await reader.read(); |
| 991 | if (done) return; | 995 | if (done) return; |
| @@ -1002,9 +1006,10 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | |||
| 1002 | const newText = data?.choices?.[0]?.text || data?.content || ''; | 1006 | const newText = data?.choices?.[0]?.text || data?.content || ''; |
| 1003 | text += newText; | 1007 | text += newText; |
| 1004 | logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities); | 1008 | logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities); |
| 1009 | state.reasoning += data?.choices?.[0]?.reasoning ?? ''; | ||
| 1005 | } | 1010 | } |
| 1006 | 1011 | ||
| 1007 | yield { text, swipes, logprobs, toolCalls }; | 1012 | yield { text, swipes, logprobs, toolCalls, state }; |
| 1008 | } | 1013 | } |
| 1009 | }; | 1014 | }; |
| 1010 | } | 1015 | } |
| @@ -1265,6 +1270,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, | |||
| 1265 | 'truncation_length': max_context, | 1270 | 'truncation_length': max_context, |
| 1266 | 'ban_eos_token': settings.ban_eos_token, | 1271 | 'ban_eos_token': settings.ban_eos_token, |
| 1267 | 'skip_special_tokens': settings.skip_special_tokens, | 1272 | 'skip_special_tokens': settings.skip_special_tokens, |
| 1273 | 'include_reasoning': settings.include_reasoning, | ||
| 1268 | 'top_a': settings.top_a, | 1274 | 'top_a': settings.top_a, |
| 1269 | 'tfs': settings.tfs, | 1275 | 'tfs': settings.tfs, |
| 1270 | 'epsilon_cutoff': [OOBA, MANCER].includes(settings.type) ? settings.epsilon_cutoff : undefined, | 1276 | 'epsilon_cutoff': [OOBA, MANCER].includes(settings.type) ? settings.epsilon_cutoff : undefined, |
| @@ -679,6 +679,9 @@ export function getTokenizerModel() { | |||
| 679 | } | 679 | } |
| 680 | 680 | ||
| 681 | if (oai_settings.chat_completion_source === chat_completion_sources.PERPLEXITY) { | 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 | if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) { | 685 | if (oai_settings.perplexity_model.includes('llama-3') || oai_settings.perplexity_model.includes('llama3')) { |
| 683 | return llama3Tokenizer; | 686 | return llama3Tokenizer; |
| 684 | } | 687 | } |
| @@ -1733,17 +1733,17 @@ export function hasAnimation(control) { | |||
| 1733 | 1733 | ||
| 1734 | /** | 1734 | /** |
| 1735 | * Run an action once an animation on a control ends. If the control has no animation, the action will be executed immediately. | 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 | * @param {HTMLElement} control - The control element to listen for animation end event | 1737 | * @param {HTMLElement} control - The control element to listen for animation end event |
| 1738 | * @param {(control:*?) => void} callback - The callback function to be executed when the animation ends | 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 | export function runAfterAnimation(control, callback) { | 1741 | export function runAfterAnimation(control, callback, timeout = 500) { |
| 1741 | if (hasAnimation(control)) { | 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 | } else { | 1747 | } else { |
| 1748 | callback(control); | 1748 | callback(control); |
| 1749 | } | 1749 | } |
| @@ -19,7 +19,7 @@ import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; | |||
| 19 | 19 | ||
| 20 | const MAX_LOOPS = 100; | 20 | const MAX_LOOPS = 100; |
| 21 | 21 | ||
| 22 | function getLocalVariable(name, args = {}) { | 22 | export function getLocalVariable(name, args = {}) { |
| 23 | if (!chat_metadata.variables) { | 23 | if (!chat_metadata.variables) { |
| 24 | chat_metadata.variables = {}; | 24 | chat_metadata.variables = {}; |
| 25 | } | 25 | } |
| @@ -45,7 +45,7 @@ function getLocalVariable(name, args = {}) { | |||
| 45 | return (localVariable?.trim?.() === '' || isNaN(Number(localVariable))) ? (localVariable || '') : Number(localVariable); | 45 | return (localVariable?.trim?.() === '' || isNaN(Number(localVariable))) ? (localVariable || '') : Number(localVariable); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | function setLocalVariable(name, value, args = {}) { | 48 | export function setLocalVariable(name, value, args = {}) { |
| 49 | if (!name) { | 49 | if (!name) { |
| 50 | throw new Error('Variable name cannot be empty or undefined.'); | 50 | throw new Error('Variable name cannot be empty or undefined.'); |
| 51 | } | 51 | } |
| @@ -80,7 +80,7 @@ function setLocalVariable(name, value, args = {}) { | |||
| 80 | return value; | 80 | return value; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | function getGlobalVariable(name, args = {}) { | 83 | export function getGlobalVariable(name, args = {}) { |
| 84 | let globalVariable = extension_settings.variables.global[args.key ?? name]; | 84 | let globalVariable = extension_settings.variables.global[args.key ?? name]; |
| 85 | if (args.index !== undefined) { | 85 | if (args.index !== undefined) { |
| 86 | try { | 86 | try { |
| @@ -102,7 +102,7 @@ function getGlobalVariable(name, args = {}) { | |||
| 102 | return (globalVariable?.trim?.() === '' || isNaN(Number(globalVariable))) ? (globalVariable || '') : Number(globalVariable); | 102 | return (globalVariable?.trim?.() === '' || isNaN(Number(globalVariable))) ? (globalVariable || '') : Number(globalVariable); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | function setGlobalVariable(name, value, args = {}) { | 105 | export function setGlobalVariable(name, value, args = {}) { |
| 106 | if (!name) { | 106 | if (!name) { |
| 107 | throw new Error('Variable name cannot be empty or undefined.'); | 107 | throw new Error('Variable name cannot be empty or undefined.'); |
| 108 | } | 108 | } |
| @@ -292,36 +292,44 @@ input[type='checkbox']:focus-visible { | |||
| 292 | filter: grayscale(25%); | 292 | filter: grayscale(25%); |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | .mes_text table { | 295 | .mes_text table, |
| 296 | .mes_reasoning table { | ||
| 296 | border-spacing: 0; | 297 | border-spacing: 0; |
| 297 | border-collapse: collapse; | 298 | border-collapse: collapse; |
| 298 | margin-bottom: 10px; | 299 | margin-bottom: 10px; |
| 299 | } | 300 | } |
| 300 | 301 | ||
| 301 | .mes_text td, | 302 | .mes_text td, |
| 302 | .mes_text th { | 303 | .mes_text th, |
| 304 | .mes_reasoning td, | ||
| 305 | .mes_reasoning th { | ||
| 303 | border: 1px solid; | 306 | border: 1px solid; |
| 304 | border-collapse: collapse; | 307 | border-collapse: collapse; |
| 305 | padding: 0.25em; | 308 | padding: 0.25em; |
| 306 | } | 309 | } |
| 307 | 310 | ||
| 308 | .mes_text p { | 311 | .mes_text p, |
| 312 | .mes_reasoning p { | ||
| 309 | margin-top: 0; | 313 | margin-top: 0; |
| 310 | margin-bottom: 10px; | 314 | margin-bottom: 10px; |
| 311 | } | 315 | } |
| 312 | 316 | ||
| 313 | .mes_text li tt { | 317 | .mes_text li tt, |
| 318 | .mes_reasoning li tt { | ||
| 314 | display: inline-block; | 319 | display: inline-block; |
| 315 | } | 320 | } |
| 316 | 321 | ||
| 317 | .mes_text ol, | 322 | .mes_text ol, |
| 318 | .mes_text ul { | 323 | .mes_text ul, |
| 324 | .mes_reasoning ol, | ||
| 325 | .mes_reasoning ul { | ||
| 319 | margin-top: 5px; | 326 | margin-top: 5px; |
| 320 | margin-bottom: 5px; | 327 | margin-bottom: 5px; |
| 321 | } | 328 | } |
| 322 | 329 | ||
| 323 | .mes_text br, | 330 | .mes_text br, |
| 324 | .mes_bias br { | 331 | .mes_bias br, |
| 332 | .mes_reasoning br { | ||
| 325 | content: ' '; | 333 | content: ' '; |
| 326 | } | 334 | } |
| 327 | 335 | ||
| @@ -332,25 +340,88 @@ input[type='checkbox']:focus-visible { | |||
| 332 | color: var(--SmartThemeQuoteColor); | 340 | color: var(--SmartThemeQuoteColor); |
| 333 | } | 341 | } |
| 334 | 342 | ||
| 343 | .mes_reasoning { | ||
| 344 | display: block; | ||
| 345 | border: 1px solid var(--SmartThemeBorderColor); | ||
| 346 | background-color: var(--black30a); | ||
| 347 | border-radius: 5px; | ||
| 348 | padding: 5px; | ||
| 349 | margin: 5px 0; | ||
| 350 | overflow-y: auto; | ||
| 351 | } | ||
| 352 | |||
| 353 | .mes_reasoning_summary { | ||
| 354 | cursor: pointer; | ||
| 355 | position: relative; | ||
| 356 | margin: 2px; | ||
| 357 | } | ||
| 358 | |||
| 359 | @supports not selector(:has(*)) { | ||
| 360 | .mes_reasoning_details { | ||
| 361 | display: none !important; | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | .mes_bias:empty, | ||
| 366 | .mes_reasoning:empty, | ||
| 367 | .mes_reasoning_details:has(.mes_reasoning:empty), | ||
| 368 | .mes_block:has(.edit_textarea) .mes_reasoning_details, | ||
| 369 | .mes_reasoning_details:not([open]) .mes_reasoning_actions, | ||
| 370 | .mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning, | ||
| 371 | .mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_done, | ||
| 372 | .mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_cancel, | ||
| 373 | .mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_actions .mes_button:not(.mes_reasoning_edit_done, .mes_reasoning_edit_cancel) { | ||
| 374 | display: none; | ||
| 375 | } | ||
| 376 | |||
| 377 | .mes_reasoning_actions { | ||
| 378 | position: absolute; | ||
| 379 | right: 0; | ||
| 380 | top: 0; | ||
| 381 | |||
| 382 | display: flex; | ||
| 383 | gap: 4px; | ||
| 384 | flex-wrap: nowrap; | ||
| 385 | justify-content: flex-end; | ||
| 386 | transition: all 200ms; | ||
| 387 | overflow-x: hidden; | ||
| 388 | padding: 1px; | ||
| 389 | } | ||
| 390 | |||
| 391 | .mes_reasoning_summary>span { | ||
| 392 | margin-left: 0.5em; | ||
| 393 | } | ||
| 394 | |||
| 335 | .mes_text i, | 395 | .mes_text i, |
| 336 | .mes_text em { | 396 | .mes_text em, |
| 397 | .mes_reasoning i, | ||
| 398 | .mes_reasoning em { | ||
| 337 | color: var(--SmartThemeEmColor); | 399 | color: var(--SmartThemeEmColor); |
| 338 | } | 400 | } |
| 339 | 401 | ||
| 340 | .mes_text u { | 402 | .mes_text q i, |
| 403 | .mes_text q em { | ||
| 404 | color: inherit; | ||
| 405 | } | ||
| 406 | |||
| 407 | .mes_text u, | ||
| 408 | .mes_reasoning u { | ||
| 341 | color: var(--SmartThemeUnderlineColor); | 409 | color: var(--SmartThemeUnderlineColor); |
| 342 | } | 410 | } |
| 343 | 411 | ||
| 344 | .mes_text q { | 412 | .mes_text q, |
| 413 | .mes_reasoning q { | ||
| 345 | color: var(--SmartThemeQuoteColor); | 414 | color: var(--SmartThemeQuoteColor); |
| 346 | } | 415 | } |
| 347 | 416 | ||
| 348 | .mes_text font[color] em, | 417 | .mes_text font[color] em, |
| 349 | .mes_text font[color] i { | 418 | .mes_text font[color] i, |
| 350 | color: inherit; | 419 | .mes_text font[color] u, |
| 351 | } | 420 | .mes_text font[color] q, |
| 352 | 421 | .mes_reasoning font[color] em, | |
| 353 | .mes_text font[color] q { | 422 | .mes_reasoning font[color] i, |
| 423 | .mes_reasoning font[color] u, | ||
| 424 | .mes_reasoning font[color] q { | ||
| 354 | color: inherit; | 425 | color: inherit; |
| 355 | } | 426 | } |
| 356 | 427 | ||
| @@ -358,7 +429,8 @@ input[type='checkbox']:focus-visible { | |||
| 358 | display: block; | 429 | display: block; |
| 359 | } | 430 | } |
| 360 | 431 | ||
| 361 | .mes_text blockquote { | 432 | .mes_text blockquote, |
| 433 | .mes_reasoning blockquote { | ||
| 362 | border-left: 3px solid var(--SmartThemeQuoteColor); | 434 | border-left: 3px solid var(--SmartThemeQuoteColor); |
| 363 | padding-left: 10px; | 435 | padding-left: 10px; |
| 364 | background-color: var(--black30a); | 436 | background-color: var(--black30a); |
| @@ -368,18 +440,24 @@ input[type='checkbox']:focus-visible { | |||
| 368 | .mes_text strong em, | 440 | .mes_text strong em, |
| 369 | .mes_text strong, | 441 | .mes_text strong, |
| 370 | .mes_text h2, | 442 | .mes_text h2, |
| 371 | .mes_text h1 { | 443 | .mes_text h1, |
| 444 | .mes_reasoning strong em, | ||
| 445 | .mes_reasoning strong, | ||
| 446 | .mes_reasoning h2, | ||
| 447 | .mes_reasoning h1 { | ||
| 372 | font-weight: bold; | 448 | font-weight: bold; |
| 373 | } | 449 | } |
| 374 | 450 | ||
| 375 | .mes_text pre code { | 451 | .mes_text pre code, |
| 452 | .mes_reasoning pre code { | ||
| 376 | position: relative; | 453 | position: relative; |
| 377 | display: block; | 454 | display: block; |
| 378 | overflow-x: auto; | 455 | overflow-x: auto; |
| 379 | padding: 1em; | 456 | padding: 1em; |
| 380 | } | 457 | } |
| 381 | 458 | ||
| 382 | .mes_text img:not(.mes_img) { | 459 | .mes_text img:not(.mes_img), |
| 460 | .mes_reasoning img:not(.mes_img) { | ||
| 383 | max-width: 100%; | 461 | max-width: 100%; |
| 384 | max-height: var(--doc-height); | 462 | max-height: var(--doc-height); |
| 385 | } | 463 | } |
| @@ -1022,6 +1100,11 @@ body .panelControlBar { | |||
| 1022 | /*only affects bubblechat to make it sit nicely at the bottom*/ | 1100 | /*only affects bubblechat to make it sit nicely at the bottom*/ |
| 1023 | } | 1101 | } |
| 1024 | 1102 | ||
| 1103 | .last_mes:has(.mes_text:empty):has(.mes_reasoning_details[open]) .mes_reasoning:not(:empty) { | ||
| 1104 | margin-bottom: 30px; | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | .last_mes .mes_reasoning, | ||
| 1025 | .last_mes .mes_text { | 1108 | .last_mes .mes_text { |
| 1026 | padding-right: 30px; | 1109 | padding-right: 30px; |
| 1027 | } | 1110 | } |
| @@ -1235,14 +1318,18 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter { | |||
| 1235 | overflow-y: clip; | 1318 | overflow-y: clip; |
| 1236 | } | 1319 | } |
| 1237 | 1320 | ||
| 1238 | .mes_text { | 1321 | .mes_text, |
| 1322 | .mes_reasoning { | ||
| 1239 | font-weight: 500; | 1323 | font-weight: 500; |
| 1240 | line-height: calc(var(--mainFontSize) + .5rem); | 1324 | line-height: calc(var(--mainFontSize) + .5rem); |
| 1325 | max-width: 100%; | ||
| 1326 | overflow-wrap: anywhere; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | .mes_text { | ||
| 1241 | padding-left: 0; | 1330 | padding-left: 0; |
| 1242 | padding-top: 5px; | 1331 | padding-top: 5px; |
| 1243 | padding-bottom: 5px; | 1332 | padding-bottom: 5px; |
| 1244 | max-width: 100%; | ||
| 1245 | overflow-wrap: anywhere; | ||
| 1246 | } | 1333 | } |
| 1247 | 1334 | ||
| 1248 | br { | 1335 | br { |
| @@ -4150,10 +4237,12 @@ input[type="range"]::-webkit-slider-thumb { | |||
| 4150 | align-items: center; | 4237 | align-items: center; |
| 4151 | } | 4238 | } |
| 4152 | 4239 | ||
| 4240 | .mes_reasoning_edit_cancel, | ||
| 4153 | .mes_edit_cancel.menu_button { | 4241 | .mes_edit_cancel.menu_button { |
| 4154 | background-color: var(--crimson70a); | 4242 | background-color: var(--crimson70a); |
| 4155 | } | 4243 | } |
| 4156 | 4244 | ||
| 4245 | .mes_reasoning_edit_done, | ||
| 4157 | .mes_edit_done.menu_button { | 4246 | .mes_edit_done.menu_button { |
| 4158 | background-color: var(--okGreen70a); | 4247 | background-color: var(--okGreen70a); |
| 4159 | } | 4248 | } |
| @@ -4162,6 +4251,7 @@ input[type="range"]::-webkit-slider-thumb { | |||
| 4162 | opacity: 1; | 4251 | opacity: 1; |
| 4163 | } | 4252 | } |
| 4164 | 4253 | ||
| 4254 | .reasoning_edit_textarea, | ||
| 4165 | .edit_textarea { | 4255 | .edit_textarea { |
| 4166 | padding: 5px; | 4256 | padding: 5px; |
| 4167 | margin: 0; | 4257 | margin: 0; |
| @@ -369,6 +369,7 @@ export const OPENROUTER_KEYS = [ | |||
| 369 | 'prompt', | 369 | 'prompt', |
| 370 | 'stop', | 370 | 'stop', |
| 371 | 'provider', | 371 | 'provider', |
| 372 | 'include_reasoning', | ||
| 372 | ]; | 373 | ]; |
| 373 | 374 | ||
| 374 | // https://github.com/vllm-project/vllm/blob/0f8a91401c89ac0a8018def3756829611b57727f/vllm/entrypoints/openai/protocol.py#L220 | 375 | // https://github.com/vllm-project/vllm/blob/0f8a91401c89ac0a8018def3756829611b57727f/vllm/entrypoints/openai/protocol.py#L220 |
| @@ -288,7 +288,8 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 288 | 288 | ||
| 289 | const model = String(request.body.model); | 289 | const model = String(request.body.model); |
| 290 | const stream = Boolean(request.body.stream); | 290 | const stream = Boolean(request.body.stream); |
| 291 | const showThoughts = Boolean(request.body.show_thoughts); | 291 | const showThoughts = Boolean(request.body.include_reasoning); |
| 292 | const isThinking = model.includes('thinking'); | ||
| 292 | 293 | ||
| 293 | const generationConfig = { | 294 | const generationConfig = { |
| 294 | stopSequences: request.body.stop, | 295 | stopSequences: request.body.stop, |
| @@ -329,6 +330,12 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 329 | body.systemInstruction = prompt.system_instruction; | 330 | body.systemInstruction = prompt.system_instruction; |
| 330 | } | 331 | } |
| 331 | 332 | ||
| 333 | if (isThinking && showThoughts) { | ||
| 334 | generationConfig.thinkingConfig = { | ||
| 335 | includeThoughts: true, | ||
| 336 | }; | ||
| 337 | } | ||
| 338 | |||
| 332 | return body; | 339 | return body; |
| 333 | } | 340 | } |
| 334 | 341 | ||
| @@ -342,7 +349,6 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 342 | controller.abort(); | 349 | controller.abort(); |
| 343 | }); | 350 | }); |
| 344 | 351 | ||
| 345 | const isThinking = model.includes('thinking'); | ||
| 346 | const apiVersion = isThinking ? 'v1alpha' : 'v1beta'; | 352 | const apiVersion = isThinking ? 'v1alpha' : 'v1beta'; |
| 347 | const responseType = (stream ? 'streamGenerateContent' : 'generateContent'); | 353 | const responseType = (stream ? 'streamGenerateContent' : 'generateContent'); |
| 348 | 354 | ||
| @@ -387,11 +393,7 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 387 | const responseContent = candidates[0].content ?? candidates[0].output; | 393 | const responseContent = candidates[0].content ?? candidates[0].output; |
| 388 | console.log('Google AI Studio response:', responseContent); | 394 | console.log('Google AI Studio response:', responseContent); |
| 389 | 395 | ||
| 390 | if (Array.isArray(responseContent?.parts) && isThinking && !showThoughts) { | 396 | const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n'); |
| 391 | responseContent.parts = responseContent.parts.filter(part => !part.thought); | ||
| 392 | } | ||
| 393 | |||
| 394 | const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join('\n\n'); | ||
| 395 | if (!responseText) { | 397 | if (!responseText) { |
| 396 | let message = 'Google AI Studio Candidate text empty'; | 398 | let message = 'Google AI Studio Candidate text empty'; |
| 397 | console.log(message, generateResponseJson); | 399 | console.log(message, generateResponseJson); |
| @@ -399,7 +401,7 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 399 | } | 401 | } |
| 400 | 402 | ||
| 401 | // Wrap it back to OAI format | 403 | // Wrap it back to OAI format |
| 402 | const reply = { choices: [{ 'message': { 'content': responseText } }] }; | 404 | const reply = { choices: [{ 'message': { 'content': responseText } }], responseContent }; |
| 403 | return response.send(reply); | 405 | return response.send(reply); |
| 404 | } | 406 | } |
| 405 | } catch (error) { | 407 | } catch (error) { |
| @@ -996,6 +998,10 @@ router.post('/generate', jsonParser, function (request, response) { | |||
| 996 | bodyParams['route'] = 'fallback'; | 998 | bodyParams['route'] = 'fallback'; |
| 997 | } | 999 | } |
| 998 | 1000 | ||
| 1001 | if (request.body.include_reasoning) { | ||
| 1002 | bodyParams['include_reasoning'] = true; | ||
| 1003 | } | ||
| 1004 | |||
| 999 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); | 1005 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); |
| 1000 | if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) { | 1006 | if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) { |
| 1001 | cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth); | 1007 | cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth); |
| @@ -162,7 +162,7 @@ router.post('/transcript', jsonParser, async (request, response) => { | |||
| 162 | 162 | ||
| 163 | router.post('/searxng', jsonParser, async (request, response) => { | 163 | router.post('/searxng', jsonParser, async (request, response) => { |
| 164 | try { | 164 | try { |
| 165 | const { baseUrl, query, preferences } = request.body; | 165 | const { baseUrl, query, preferences, categories } = request.body; |
| 166 | 166 | ||
| 167 | if (!baseUrl || !query) { | 167 | if (!baseUrl || !query) { |
| 168 | console.log('Missing required parameters for /searxng'); | 168 | console.log('Missing required parameters for /searxng'); |
| @@ -193,6 +193,9 @@ router.post('/searxng', jsonParser, async (request, response) => { | |||
| 193 | if (preferences) { | 193 | if (preferences) { |
| 194 | searchParams.append('preferences', preferences); | 194 | searchParams.append('preferences', preferences); |
| 195 | } | 195 | } |
| 196 | if (categories) { | ||
| 197 | searchParams.append('categories', categories); | ||
| 198 | } | ||
| 196 | searchUrl.search = searchParams.toString(); | 199 | searchUrl.search = searchParams.toString(); |
| 197 | 200 | ||
| 198 | const searchResult = await fetch(searchUrl, { headers: visitHeaders }); | 201 | const searchResult = await fetch(searchUrl, { headers: visitHeaders }); |
| @@ -220,7 +223,7 @@ router.post('/tavily', jsonParser, async (request, response) => { | |||
| 220 | return response.sendStatus(400); | 223 | return response.sendStatus(400); |
| 221 | } | 224 | } |
| 222 | 225 | ||
| 223 | const { query } = request.body; | 226 | const { query, include_images } = request.body; |
| 224 | 227 | ||
| 225 | const body = { | 228 | const body = { |
| 226 | query: query, | 229 | query: query, |
| @@ -229,7 +232,7 @@ router.post('/tavily', jsonParser, async (request, response) => { | |||
| 229 | topic: 'general', | 232 | topic: 'general', |
| 230 | include_answer: true, | 233 | include_answer: true, |
| 231 | include_raw_content: false, | 234 | include_raw_content: false, |
| 232 | include_images: false, | 235 | include_images: !!include_images, |
| 233 | include_image_descriptions: false, | 236 | include_image_descriptions: false, |
| 234 | include_domains: [], | 237 | include_domains: [], |
| 235 | max_results: 10, | 238 | max_results: 10, |
| @@ -297,6 +300,7 @@ router.post('/koboldcpp', jsonParser, async (request, response) => { | |||
| 297 | router.post('/visit', jsonParser, async (request, response) => { | 300 | router.post('/visit', jsonParser, async (request, response) => { |
| 298 | try { | 301 | try { |
| 299 | const url = request.body.url; | 302 | const url = request.body.url; |
| 303 | const html = Boolean(request.body.html ?? true); | ||
| 300 | 304 | ||
| 301 | if (!url) { | 305 | if (!url) { |
| 302 | console.log('No url provided for /visit'); | 306 | console.log('No url provided for /visit'); |
| @@ -340,6 +344,8 @@ router.post('/visit', jsonParser, async (request, response) => { | |||
| 340 | } | 344 | } |
| 341 | 345 | ||
| 342 | const contentType = String(result.headers.get('content-type')); | 346 | const contentType = String(result.headers.get('content-type')); |
| 347 | |||
| 348 | if (html) { | ||
| 343 | if (!contentType.includes('text/html')) { | 349 | if (!contentType.includes('text/html')) { |
| 344 | console.log(`Visit failed, content-type is ${contentType}, expected text/html`); | 350 | console.log(`Visit failed, content-type is ${contentType}, expected text/html`); |
| 345 | return response.sendStatus(500); | 351 | return response.sendStatus(500); |
| @@ -347,6 +353,11 @@ router.post('/visit', jsonParser, async (request, response) => { | |||
| 347 | 353 | ||
| 348 | const text = await result.text(); | 354 | const text = await result.text(); |
| 349 | return response.send(text); | 355 | return response.send(text); |
| 356 | } | ||
| 357 | |||
| 358 | response.setHeader('Content-Type', contentType); | ||
| 359 | const buffer = await result.arrayBuffer(); | ||
| 360 | return response.send(Buffer.from(buffer)); | ||
| 350 | } catch (error) { | 361 | } catch (error) { |
| 351 | console.log(error); | 362 | console.log(error); |
| 352 | return response.sendStatus(500); | 363 | return response.sendStatus(500); |
| @@ -533,6 +533,7 @@ comfy.post('/delete-workflow', jsonParser, async (request, response) => { | |||
| 533 | 533 | ||
| 534 | comfy.post('/generate', jsonParser, async (request, response) => { | 534 | comfy.post('/generate', jsonParser, async (request, response) => { |
| 535 | try { | 535 | try { |
| 536 | let item; | ||
| 536 | const url = new URL(urlJoin(request.body.url, '/prompt')); | 537 | const url = new URL(urlJoin(request.body.url, '/prompt')); |
| 537 | 538 | ||
| 538 | const controller = new AbortController(); | 539 | const controller = new AbortController(); |
| @@ -557,7 +558,6 @@ comfy.post('/generate', jsonParser, async (request, response) => { | |||
| 557 | /** @type {any} */ | 558 | /** @type {any} */ |
| 558 | const data = await promptResult.json(); | 559 | const data = await promptResult.json(); |
| 559 | const id = data.prompt_id; | 560 | const id = data.prompt_id; |
| 560 | let item; | ||
| 561 | const historyUrl = new URL(urlJoin(request.body.url, '/history')); | 561 | const historyUrl = new URL(urlJoin(request.body.url, '/history')); |
| 562 | while (true) { | 562 | while (true) { |
| 563 | const result = await fetch(historyUrl); | 563 | const result = await fetch(historyUrl); |
| @@ -398,7 +398,7 @@ function getWebTokenizersChunks(tokenizer, ids) { | |||
| 398 | * @returns {string} Tokenizer model to use | 398 | * @returns {string} Tokenizer model to use |
| 399 | */ | 399 | */ |
| 400 | export function getTokenizerModel(requestModel) { | 400 | export function getTokenizerModel(requestModel) { |
| 401 | if (requestModel.includes('o1-preview') || requestModel.includes('o1-mini')) { | 401 | if (requestModel.includes('o1-preview') || requestModel.includes('o1-mini') || requestModel.includes('o3-mini')) { |
| 402 | return 'gpt-4o'; | 402 | return 'gpt-4o'; |
| 403 | } | 403 | } |
| 404 | 404 | ||