Move reasoning stuff to reasoning.js

06303bb62fc895446264cbf004e2dc042a09e49a

Wolfsblvt <wolfsblvt@gmail.com>

2 files changed, +81 -77Ignore whitespace
public/script.js+2 -75
@@ -225,7 +225,7 @@ import {
225 instruct_presets,225 instruct_presets,
226 selectContextPreset,226 selectContextPreset,
227} from './scripts/instruct-mode.js';227} from './scripts/instruct-mode.js';
228import { getCurrentLocale, initLocales, t } from './scripts/i18n.js';228import { initLocales, t } from './scripts/i18n.js';
229import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';229import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
230import {230import {
231 user_avatar,231 user_avatar,
@@ -269,7 +269,7 @@ import { initSettingsSearch } from './scripts/setting-search.js';
269import { initBulkEdit } from './scripts/bulk-edit.js';269import { initBulkEdit } from './scripts/bulk-edit.js';
270import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';270import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
271import { getContext } from './scripts/st-context.js';271import { getContext } from './scripts/st-context.js';
272import { initReasoning, PromptReasoning } from './scripts/reasoning.js';272import { extractReasoningFromData, initReasoning, PromptReasoning, updateReasoningTimeUI } from './scripts/reasoning.js';
273273
274// API OBJECT FOR EXTERNAL WIRING274// API OBJECT FOR EXTERNAL WIRING
275globalThis.SillyTavern = {275globalThis.SillyTavern = {
@@ -5764,56 +5764,6 @@ function extractMessageFromData(data) {
5764}5764}
57655765
5766/**5766/**
5767 * Extracts the reasoning from the response data.
5768 * @param {object} data Response data
5769 * @returns {string} Extracted reasoning
5770 */
5771function extractReasoningFromData(data) {
5772 switch (main_api) {
5773 case 'textgenerationwebui':
5774 switch (textgen_settings.type) {
5775 case textgen_types.OPENROUTER:
5776 return data?.choices?.[0]?.reasoning ?? '';
5777 }
5778 break;
5779
5780 case 'openai':
5781 if (!oai_settings.show_thoughts) break;
5782
5783 switch (oai_settings.chat_completion_source) {
5784 case chat_completion_sources.DEEPSEEK:
5785 return data?.choices?.[0]?.message?.reasoning_content ?? '';
5786 case chat_completion_sources.OPENROUTER:
5787 return data?.choices?.[0]?.message?.reasoning ?? '';
5788 case chat_completion_sources.MAKERSUITE:
5789 return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
5790 }
5791 break;
5792 }
5793
5794 return '';
5795}
5796
5797/**
5798 * Updates the Reasoning controls
5799 * @param {HTMLElement} element The element to update
5800 * @param {number?} duration The duration of the reasoning in milliseconds
5801 * @param {object} [options={}] Options for the function
5802 * @param {boolean} [options.forceEnd=false] If true, there will be no "Thinking..." when no duration exists
5803 */
5804function updateReasoningTimeUI(element, duration, { forceEnd = false } = {}) {
5805 if (duration) {
5806 const durationStr = moment.duration(duration).locale(getCurrentLocale()).humanize({ s: 50, ss: 9 });
5807 element.textContent = t`Thought for ${durationStr}`;
5808 } else if (forceEnd) {
5809 element.textContent = t`Thought for some time`;
5810 } else {
5811 element.textContent = t`Thinking...`;
5812 }
5813}
5814
5815
5816/**
5817 * Extracts multiswipe swipes from the response data.5767 * Extracts multiswipe swipes from the response data.
5818 * @param {Object} data Response data5768 * @param {Object} data Response data
5819 * @param {string} type Type of generation5769 * @param {string} type Type of generation
@@ -10872,18 +10822,6 @@ jQuery(async function () {
10872 }10822 }
10873 });10823 });
1087410824
10875 $(document).on('click', '.mes_reasoning_header', function () {
10876 // If we are in message edit mode and reasoning area is closed, a click opens and edits it
10877 const mes = $(this).closest('.mes');
10878 const mesEditArea = mes.find('#curEditTextarea');
10879 if (mesEditArea.length) {
10880 const summary = $(mes).find('.mes_reasoning_summary');
10881 if (!summary.attr('open')) {
10882 summary.find('.mes_reasoning_edit').trigger('click');
10883 }
10884 }
10885 });
10886
10887 $(document).on('input', '#curEditTextarea', function () {10825 $(document).on('input', '#curEditTextarea', function () {
10888 if (power_user.auto_save_msg_edits === true) {10826 if (power_user.auto_save_msg_edits === true) {
10889 messageEditAuto($(this));10827 messageEditAuto($(this));
@@ -11438,17 +11376,6 @@ jQuery(async function () {
11438 }11376 }
11439 });11377 });
1144011378
11441 $(document).on('click', '.mes_reasoning_summary', function () {
11442 // If you toggle summary header while editing reasoning, yup - we just cancel it
11443 $(this).closest('.mes').find('.mes_reasoning_edit_cancel:visible').trigger('click');
11444 });
11445
11446 $(document).on('click', '.mes_reasoning_details', function (e) {
11447 if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) {
11448 e.preventDefault();
11449 }
11450 });
11451
11452 $(document).keyup(function (e) {11379 $(document).keyup(function (e) {
11453 if (e.key === 'Escape') {11380 if (e.key === 'Escape') {
11454 const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;11381 const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;
public/scripts/reasoning.js+79 -2
@@ -1,13 +1,18 @@
1import { chat, closeMessageEditor, event_types, eventSource, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js';1import {
2 moment,
3} from '../lib.js';
4import { chat, closeMessageEditor, event_types, eventSource, main_api, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js';
2import { getRegexedString, regex_placement } from './extensions/regex/engine.js';5import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
3import { t } from './i18n.js';6import { getCurrentLocale, t } from './i18n.js';
4import { MacrosParser } from './macros.js';7import { MacrosParser } from './macros.js';
8import { chat_completion_sources, oai_settings } from './openai.js';
5import { Popup } from './popup.js';9import { Popup } from './popup.js';
6import { power_user } from './power-user.js';10import { power_user } from './power-user.js';
7import { SlashCommand } from './slash-commands/SlashCommand.js';11import { SlashCommand } from './slash-commands/SlashCommand.js';
8import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';12import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
9import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';13import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
10import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';14import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
15import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
11import { copyText, escapeRegex, isFalseBoolean } from './utils.js';16import { copyText, escapeRegex, isFalseBoolean } from './utils.js';
1217
13/**18/**
@@ -35,6 +40,55 @@ function toggleReasoningAutoExpand() {
35}40}
3641
37/**42/**
43 * Extracts the reasoning from the response data.
44 * @param {object} data Response data
45 * @returns {string} Extracted reasoning
46 */
47export function extractReasoningFromData(data) {
48 switch (main_api) {
49 case 'textgenerationwebui':
50 switch (textgenerationwebui_settings.type) {
51 case textgen_types.OPENROUTER:
52 return data?.choices?.[0]?.reasoning ?? '';
53 }
54 break;
55
56 case 'openai':
57 if (!oai_settings.show_thoughts) break;
58
59 switch (oai_settings.chat_completion_source) {
60 case chat_completion_sources.DEEPSEEK:
61 return data?.choices?.[0]?.message?.reasoning_content ?? '';
62 case chat_completion_sources.OPENROUTER:
63 return data?.choices?.[0]?.message?.reasoning ?? '';
64 case chat_completion_sources.MAKERSUITE:
65 return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
66 }
67 break;
68 }
69
70 return '';
71}
72
73/**
74 * Updates the Reasoning controls
75 * @param {HTMLElement} element The element to update
76 * @param {number?} duration The duration of the reasoning in milliseconds
77 * @param {object} [options={}] Options for the function
78 * @param {boolean} [options.forceEnd=false] If true, there will be no "Thinking..." when no duration exists
79 */
80export function updateReasoningTimeUI(element, duration, { forceEnd = false } = {}) {
81 if (duration) {
82 const durationStr = moment.duration(duration).locale(getCurrentLocale()).humanize({ s: 50, ss: 9 });
83 element.textContent = t`Thought for ${durationStr}`;
84 } else if (forceEnd) {
85 element.textContent = t`Thought for some time`;
86 } else {
87 element.textContent = t`Thinking...`;
88 }
89}
90
91/**
38 * Helper class for adding reasoning to messages.92 * Helper class for adding reasoning to messages.
39 * Keeps track of the number of reasoning additions.93 * Keeps track of the number of reasoning additions.
40 */94 */
@@ -247,6 +301,29 @@ function registerReasoningMacros() {
247}301}
248302
249function setReasoningEventHandlers() {303function setReasoningEventHandlers() {
304 $(document).on('click', '.mes_reasoning_summary', function () {
305 // If you toggle summary header while editing reasoning, yup - we just cancel it
306 $(this).closest('.mes').find('.mes_reasoning_edit_cancel:visible').trigger('click');
307 });
308
309 $(document).on('click', '.mes_reasoning_details', function (e) {
310 if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) {
311 e.preventDefault();
312 }
313 });
314
315 $(document).on('click', '.mes_reasoning_header', function () {
316 // If we are in message edit mode and reasoning area is closed, a click opens and edits it
317 const mes = $(this).closest('.mes');
318 const mesEditArea = mes.find('#curEditTextarea');
319 if (mesEditArea.length) {
320 const summary = $(mes).find('.mes_reasoning_summary');
321 if (!summary.attr('open')) {
322 summary.find('.mes_reasoning_edit').trigger('click');
323 }
324 }
325 });
326
250 $(document).on('click', '.mes_reasoning_copy', (e) => {327 $(document).on('click', '.mes_reasoning_copy', (e) => {
251 e.stopPropagation();328 e.stopPropagation();
252 e.preventDefault();329 e.preventDefault();