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 @@
19811981 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
19821982 <input id="openai_show_thoughts" type="checkbox" />
19831983 <span>
19841984 <span data-i18n="Show model thoughtsreasoning">Show model thoughtsreasoning</span>
19851985 <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i>
19861986 </span>
19871987 </label>
@@ -3800,6 +3800,39 @@
38003800 </div>
38013801 </div>
38023802 <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>
38033836 <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4>
38043837 <div>
38053838 <small>
@@ -6221,11 +6254,10 @@
62216254 <div class="mes_edit_buttons">
62226255 <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div>
62236256 <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div>
62246257 <div class="mes_edit_deletemes_edit_add_reasoning menu_button fa-solid fa-trash-canlightbulb" title="DeleteAdd thisa messagereasoning block" data-i18n="[title]DeleteAdd thisa messagereasoning 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>
62266259 <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div>
62276260 <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>
62296261 <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div>
62306262 </div>
62316263 </div>
public/locales/fr-fr.json+1 -1
@@ -1385,7 +1385,7 @@
13851385 "enable_functions_desc_1": "Autorise l'utilisation",
13861386 "enable_functions_desc_2": "outils de fonction",
13871387 "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.",
13881388 "Show model thoughtsreasoning": "Afficher les pensées du modèle",
13891389 "Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.",
13901390 "Confirm token parsing with": "Confirmer l'analyse des tokens avec",
13911391 "openai_logit_bias_no_items": "Aucun élément",
public/locales/zh-cn.json+1 -1
@@ -266,7 +266,7 @@
266266 "Use system prompt": "使用系统提示词",
267267 "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",
268268 "Merges_all_system_messages_desc_2": "字段发送。",
269269 "Show model thoughtsreasoning": "展示思维链",
270270 "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。",
271271 "Assistant Prefill": "AI预填",
272272 "Expand the editor": "展开编辑器",
public/locales/zh-tw.json+1 -1
@@ -2357,7 +2357,7 @@
23572357 "Forbid": "禁止",
23582358 "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。",
23592359 "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。",
23602360 "Show model thoughtsreasoning": "顯示模型思維鏈",
23612361 "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。",
23622362 "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]",
23632363 "Model ID (optional)": "模型 ID(可選)",
public/script.js+7 -2
@@ -238,7 +238,7 @@ import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_set
238238import { hideLoader, showLoader } from './scripts/loader.js';
239239import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
240240import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';
241241import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat, PromptReasoning } from './scripts/chats.js';
242242import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
243243import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
244244import { currentUser, setUserControls } from './scripts/user.js';
@@ -3844,6 +3844,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
38443844 coreChat.pop();
38453845 }
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+
38473852 coreChat = await Promise.all(coreChat.map(async (chatItem, index) => {
38483853 let message = chatItem.mes;
38493854 let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT;
@@ -8034,7 +8039,7 @@ function updateEditArrowClasses() {
80348039 }
80358040}
80368041
80378042export function closeMessageEditor() {
80388043 if (this_edit_mes_id) {
80398044 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();
80408045 }
public/scripts/chats.js+62 -0
@@ -24,6 +24,8 @@ import {
2424 updateChatMetadata,
2525 system_message_types,
2626 updateMessageBlock,
27+ closeMessageEditor,
28+ substituteParams,
2729} from '../script.js';
2830import { selected_group } from './group-chats.js';
2931import { power_user } from './power-user.js';
@@ -1418,6 +1420,47 @@ export function registerFileConverter(mimeType, converter) {
14181420 converters[mimeType] = converter;
14191421}
14201422
1423+/**
1424+ * Helper class for adding reasoning to messages.
1425+ * Keeps track of the number of reasoning additions.
1426+ */
1427+export 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+
14211464jQuery(function () {
14221465 $(document).on('click', '.mes_hide', async function () {
14231466 const messageBlock = $(this).closest('.mes');
@@ -1574,6 +1617,25 @@ jQuery(function () {
15741617 e.preventDefault();
15751618 });
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+
15771639 $(document).on('click', '.mes_reasoning_delete', async function (e) {
15781640 e.stopPropagation();
15791641 e.preventDefault();
public/scripts/power-user.js+41 -0
@@ -253,6 +253,14 @@ let power_user = {
253253 content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.',
254254 },
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+
256264 personas: {},
257265 default_persona: null,
258266 persona_descriptions: {},
@@ -1613,6 +1621,7 @@ async function loadPowerUserSettings(settings, data) {
16131621 loadMovingUIState();
16141622 loadCharListState();
16151623 toggleMDHotkeyIconDisplay();
1624+ loadReasoningSettings();
16161625}
16171626
16181627function toggleMDHotkeyIconDisplay() {
@@ -1629,6 +1638,38 @@ function loadCharListState() {
16291638 document.body.classList.toggle('charListGrid', power_user.charListGrid);
16301639}
16311640
1641+function 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+
16321673function loadMovingUIState() {
16331674 if (!isMobile()
16341675 && power_user.movingUIState