Formatting setting to send reasoning back to the model Supersedes #3352

94ed5483533147a782127925e010dd74e85ebe1a

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

7 files changed, +150 -10Showing whitespace changes
public/index.html+37 -5
@@ -1981,7 +1981,7 @@
1981 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">1981 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
1982 <input id="openai_show_thoughts" type="checkbox" />1982 <input id="openai_show_thoughts" type="checkbox" />
1983 <span>1983 <span>
1984 <span data-i18n="Show model thoughts">Show model thoughts</span>1984 <span data-i18n="Show model reasoning">Show model reasoning</span>
1985 <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i>1985 <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i>
1986 </span>1986 </span>
1987 </label>1987 </label>
@@ -3800,6 +3800,39 @@
3800 </div>3800 </div>
3801 </div>3801 </div>
3802 <div>3802 <div>
3803 <h4 class="standoutHeader">
3804 <span data-i18n="Reasoning">Reasoning</span>
3805 </h4>
3806 <div>
3807 <label class="checkbox_label" for="reasoning_add_to_prompts" title="Add existing reasoning blocks to prompts. To add a new reasoning block, use the message edit menu." data-i18n="[title]reasoning_add_to_prompts">
3808 <input id="reasoning_add_to_prompts" type="checkbox" />
3809 <small data-i18n="Add Reasoning to Prompts">
3810 Add Reasoning to Prompts
3811 </small>
3812 </label>
3813 <div class="flex-container">
3814 <div class="flex1" title="Inserted before the reasoning content." data-i18n="[title]reasoning_prefix">
3815 <small data-i18n="Prefix">Prefix</small>
3816 <textarea id="reasoning_prefix" class="text_pole textarea_compact autoSetHeight"></textarea>
3817 </div>
3818 <div class="flex1" title="Inserted after the reasoning content." data-i18n="[title]reasoning_suffix">
3819 <small data-i18n="Suffix">Suffix</small>
3820 <textarea id="reasoning_suffix" class="text_pole textarea_compact autoSetHeight"></textarea>
3821 </div>
3822 </div>
3823 <div class="flex-container">
3824 <div class="flex1" title="Inserted between the reasoning and the message content." data-i18n="[title]reasoning_separator">
3825 <small data-i18n="Separator">Separator</small>
3826 <textarea id="reasoning_separator" class="text_pole textarea_compact autoSetHeight"></textarea>
3827 </div>
3828 <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">
3829 <small data-i18n="Max Additions">Max Additions</small>
3830 <input id="reasoning_max_additions" class="text_pole textarea_compact" type="number" min="0" max="999"></textarea>
3831 </div>
3832 </div>
3833 </div>
3834 </div>
3835 <div>
3803 <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4>3836 <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4>
3804 <div>3837 <div>
3805 <small>3838 <small>
@@ -6221,11 +6254,10 @@
6221 <div class="mes_edit_buttons">6254 <div class="mes_edit_buttons">
6222 <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div>6255 <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>6256 <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">6257 <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>6258 <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>6259 <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">6260 <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>6261 <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div>
6230 </div>6262 </div>
6231 </div>6263 </div>
public/locales/fr-fr.json+1 -1
@@ -1385,7 +1385,7 @@
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 "Show model reasoning": "Afficher 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 "Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.",
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",
public/locales/zh-cn.json+1 -1
@@ -266,7 +266,7 @@
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 "Show model reasoning": "展示思维链",
270 "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。",270 "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。",
271 "Assistant Prefill": "AI预填",271 "Assistant Prefill": "AI预填",
272 "Expand the editor": "展开编辑器",272 "Expand the editor": "展开编辑器",
public/locales/zh-tw.json+1 -1
@@ -2357,7 +2357,7 @@
2357 "Forbid": "禁止",2357 "Forbid": "禁止",
2358 "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. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。",
2359 "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。",2359 "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。",
2360 "Show model thoughts": "顯示模型思維鏈",2360 "Show model reasoning": "顯示模型思維鏈",
2361 "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。",2361 "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。",
2362 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]",2362 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]",
2363 "Model ID (optional)": "模型 ID(可選)",2363 "Model ID (optional)": "模型 ID(可選)",
public/script.js+7 -2
@@ -238,7 +238,7 @@ import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_set
238import { hideLoader, showLoader } from './scripts/loader.js';238import { hideLoader, showLoader } from './scripts/loader.js';
239import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';239import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
240import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';240import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';
241import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js';241import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat, PromptReasoning } from './scripts/chats.js';
242import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';242import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
243import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';243import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
244import { currentUser, setUserControls } from './scripts/user.js';244import { currentUser, setUserControls } from './scripts/user.js';
@@ -3844,6 +3844,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3844 coreChat.pop();3844 coreChat.pop();
3845 }3845 }
38463846
3847 const reasoning = new PromptReasoning();
3848 for (let i = coreChat.length - 1; i >= 0; i--) {
3849 coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) };
3850 }
3851
3847 coreChat = await Promise.all(coreChat.map(async (chatItem, index) => {3852 coreChat = await Promise.all(coreChat.map(async (chatItem, index) => {
3848 let message = chatItem.mes;3853 let message = chatItem.mes;
3849 let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT;3854 let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT;
@@ -8034,7 +8039,7 @@ function updateEditArrowClasses() {
8034 }8039 }
8035}8040}
80368041
8037function closeMessageEditor() {8042export function closeMessageEditor() {
8038 if (this_edit_mes_id) {8043 if (this_edit_mes_id) {
8039 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();8044 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();
8040 }8045 }
public/scripts/chats.js+62 -0
@@ -24,6 +24,8 @@ import {
24 updateChatMetadata,24 updateChatMetadata,
25 system_message_types,25 system_message_types,
26 updateMessageBlock,26 updateMessageBlock,
27 closeMessageEditor,
28 substituteParams,
27} from '../script.js';29} from '../script.js';
28import { selected_group } from './group-chats.js';30import { selected_group } from './group-chats.js';
29import { power_user } from './power-user.js';31import { power_user } from './power-user.js';
@@ -1418,6 +1420,47 @@ export function registerFileConverter(mimeType, converter) {
1418 converters[mimeType] = converter;1420 converters[mimeType] = converter;
1419}1421}
14201422
1423/**
1424 * Helper class for adding reasoning to messages.
1425 * Keeps track of the number of reasoning additions.
1426 */
1427export class PromptReasoning {
1428 static REASONING_PLACEHOLDER = '\u200B';
1429
1430 constructor() {
1431 this.counter = 0;
1432 }
1433
1434 /**
1435 * Add reasoning to a message according to the power user settings.
1436 * @param {string} content Message content
1437 * @param {string} reasoning Message reasoning
1438 * @returns {string} Message content with reasoning
1439 */
1440 addToMessage(content, reasoning) {
1441 // Disabled or reached limit of additions
1442 if (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions) {
1443 return content;
1444 }
1445
1446 // No reasoning provided or a placeholder
1447 if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) {
1448 return content;
1449 }
1450
1451 // Increment the counter
1452 this.counter++;
1453
1454 // Substitute macros in variable parts
1455 const prefix = substituteParams(power_user.reasoning.prefix || '');
1456 const separator = substituteParams(power_user.reasoning.separator || '');
1457 const suffix = substituteParams(power_user.reasoning.suffix || '');
1458
1459 // Combine parts with reasoning and content
1460 return `${prefix}${reasoning}${suffix}${separator}${content}`;
1461 }
1462}
1463
1421jQuery(function () {1464jQuery(function () {
1422 $(document).on('click', '.mes_hide', async function () {1465 $(document).on('click', '.mes_hide', async function () {
1423 const messageBlock = $(this).closest('.mes');1466 const messageBlock = $(this).closest('.mes');
@@ -1574,6 +1617,25 @@ jQuery(function () {
1574 e.preventDefault();1617 e.preventDefault();
1575 });1618 });
15761619
1620 $(document).on('click', '.mes_edit_add_reasoning', async function () {
1621 const mesBlock = $(this).closest('.mes');
1622 const mesId = Number(mesBlock.attr('mesid'));
1623 const message = chat[mesId];
1624 if (!message?.extra){
1625 return;
1626 }
1627
1628 if (message.extra.reasoning) {
1629 toastr.info(t`Reasoning already exists.`, t`Edit Message`);
1630 return;
1631 }
1632
1633 message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
1634 await saveChatConditional();
1635 closeMessageEditor();
1636 updateMessageBlock(mesId, message);
1637 });
1638
1577 $(document).on('click', '.mes_reasoning_delete', async function (e) {1639 $(document).on('click', '.mes_reasoning_delete', async function (e) {
1578 e.stopPropagation();1640 e.stopPropagation();
1579 e.preventDefault();1641 e.preventDefault();
public/scripts/power-user.js+41 -0
@@ -253,6 +253,14 @@ 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 },
255255
256 reasoning: {
257 add_to_prompts: false,
258 prefix: '<think>\n',
259 suffix: '\n</think>',
260 separator: '\n',
261 max_additions: 1,
262 },
263
256 personas: {},264 personas: {},
257 default_persona: null,265 default_persona: null,
258 persona_descriptions: {},266 persona_descriptions: {},
@@ -1613,6 +1621,7 @@ async function loadPowerUserSettings(settings, data) {
1613 loadMovingUIState();1621 loadMovingUIState();
1614 loadCharListState();1622 loadCharListState();
1615 toggleMDHotkeyIconDisplay();1623 toggleMDHotkeyIconDisplay();
1624 loadReasoningSettings();
1616}1625}
16171626
1618function toggleMDHotkeyIconDisplay() {1627function toggleMDHotkeyIconDisplay() {
@@ -1629,6 +1638,38 @@ function loadCharListState() {
1629 document.body.classList.toggle('charListGrid', power_user.charListGrid);1638 document.body.classList.toggle('charListGrid', power_user.charListGrid);
1630}1639}
16311640
1641function loadReasoningSettings() {
1642 $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts);
1643 $('#reasoning_add_to_prompts').on('change', function () {
1644 power_user.reasoning.add_to_prompts = !!$(this).prop('checked');
1645 saveSettingsDebounced();
1646 });
1647
1648 $('#reasoning_prefix').val(power_user.reasoning.prefix);
1649 $('#reasoning_prefix').on('input', function () {
1650 power_user.reasoning.prefix = String($(this).val());
1651 saveSettingsDebounced();
1652 });
1653
1654 $('#reasoning_suffix').val(power_user.reasoning.suffix);
1655 $('#reasoning_suffix').on('input', function () {
1656 power_user.reasoning.suffix = String($(this).val());
1657 saveSettingsDebounced();
1658 });
1659
1660 $('#reasoning_separator').val(power_user.reasoning.separator);
1661 $('#reasoning_separator').on('input', function () {
1662 power_user.reasoning.separator = String($(this).val());
1663 saveSettingsDebounced();
1664 });
1665
1666 $('#reasoning_max_additions').val(power_user.reasoning.max_additions);
1667 $('#reasoning_max_additions').on('input', function () {
1668 power_user.reasoning.max_additions = Number($(this).val());
1669 saveSettingsDebounced();
1670 });
1671}
1672
1632function loadMovingUIState() {1673function loadMovingUIState() {
1633 if (!isMobile()1674 if (!isMobile()
1634 && power_user.movingUIState1675 && power_user.movingUIState