diff --git a/public/script.js b/public/script.js index a8a3cabf7..e3f5434c5 100644 --- a/public/script.js +++ b/public/script.js @@ -225,7 +225,7 @@ import { instruct_presets, selectContextPreset, } from './scripts/instruct-mode.js'; -import { getCurrentLocale, initLocales, t } from './scripts/i18n.js'; +import { initLocales, t } from './scripts/i18n.js'; import { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js'; import { user_avatar, @@ -269,7 +269,7 @@ import { initSettingsSearch } from './scripts/setting-search.js'; import { initBulkEdit } from './scripts/bulk-edit.js'; import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; import { getContext } from './scripts/st-context.js'; -import { initReasoning, PromptReasoning } from './scripts/reasoning.js'; +import { extractReasoningFromData, initReasoning, PromptReasoning, updateReasoningTimeUI } from './scripts/reasoning.js'; // API OBJECT FOR EXTERNAL WIRING globalThis.SillyTavern = { @@ -5763,56 +5763,6 @@ function extractMessageFromData(data) { } } -/** - * Extracts the reasoning from the response data. - * @param {object} data Response data - * @returns {string} Extracted reasoning - */ -function extractReasoningFromData(data) { - switch (main_api) { - case 'textgenerationwebui': - switch (textgen_settings.type) { - case textgen_types.OPENROUTER: - return data?.choices?.[0]?.reasoning ?? ''; - } - break; - - case 'openai': - if (!oai_settings.show_thoughts) break; - - switch (oai_settings.chat_completion_source) { - case chat_completion_sources.DEEPSEEK: - return data?.choices?.[0]?.message?.reasoning_content ?? ''; - case chat_completion_sources.OPENROUTER: - return data?.choices?.[0]?.message?.reasoning ?? ''; - case chat_completion_sources.MAKERSUITE: - return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; - } - break; - } - - return ''; -} - -/** - * Updates the Reasoning controls - * @param {HTMLElement} element The element to update - * @param {number?} duration The duration of the reasoning in milliseconds - * @param {object} [options={}] Options for the function - * @param {boolean} [options.forceEnd=false] If true, there will be no "Thinking..." when no duration exists - */ -function updateReasoningTimeUI(element, duration, { forceEnd = false } = {}) { - if (duration) { - const durationStr = moment.duration(duration).locale(getCurrentLocale()).humanize({ s: 50, ss: 9 }); - element.textContent = t`Thought for ${durationStr}`; - } else if (forceEnd) { - element.textContent = t`Thought for some time`; - } else { - element.textContent = t`Thinking...`; - } -} - - /** * Extracts multiswipe swipes from the response data. * @param {Object} data Response data @@ -10872,18 +10822,6 @@ jQuery(async function () { } }); - $(document).on('click', '.mes_reasoning_header', function () { - // If we are in message edit mode and reasoning area is closed, a click opens and edits it - const mes = $(this).closest('.mes'); - const mesEditArea = mes.find('#curEditTextarea'); - if (mesEditArea.length) { - const summary = $(mes).find('.mes_reasoning_summary'); - if (!summary.attr('open')) { - summary.find('.mes_reasoning_edit').trigger('click'); - } - } - }); - $(document).on('input', '#curEditTextarea', function () { if (power_user.auto_save_msg_edits === true) { messageEditAuto($(this)); @@ -11438,17 +11376,6 @@ jQuery(async function () { } }); - $(document).on('click', '.mes_reasoning_summary', function () { - // If you toggle summary header while editing reasoning, yup - we just cancel it - $(this).closest('.mes').find('.mes_reasoning_edit_cancel:visible').trigger('click'); - }); - - $(document).on('click', '.mes_reasoning_details', function (e) { - if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) { - e.preventDefault(); - } - }); - $(document).keyup(function (e) { if (e.key === 'Escape') { const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0; diff --git a/public/scripts/reasoning.js b/public/scripts/reasoning.js index 6b9344313..34922313d 100644 --- a/public/scripts/reasoning.js +++ b/public/scripts/reasoning.js @@ -1,13 +1,18 @@ -import { chat, closeMessageEditor, event_types, eventSource, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js'; +import { + moment, +} from '../lib.js'; +import { chat, closeMessageEditor, event_types, eventSource, main_api, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js'; import { getRegexedString, regex_placement } from './extensions/regex/engine.js'; -import { t } from './i18n.js'; +import { getCurrentLocale, t } from './i18n.js'; import { MacrosParser } from './macros.js'; +import { chat_completion_sources, oai_settings } from './openai.js'; import { Popup } from './popup.js'; import { power_user } from './power-user.js'; import { SlashCommand } from './slash-commands/SlashCommand.js'; import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js'; import { copyText, escapeRegex, isFalseBoolean } from './utils.js'; /** @@ -34,6 +39,55 @@ function toggleReasoningAutoExpand() { }); } +/** + * Extracts the reasoning from the response data. + * @param {object} data Response data + * @returns {string} Extracted reasoning + */ +export function extractReasoningFromData(data) { + switch (main_api) { + case 'textgenerationwebui': + switch (textgenerationwebui_settings.type) { + case textgen_types.OPENROUTER: + return data?.choices?.[0]?.reasoning ?? ''; + } + break; + + case 'openai': + if (!oai_settings.show_thoughts) break; + + switch (oai_settings.chat_completion_source) { + case chat_completion_sources.DEEPSEEK: + return data?.choices?.[0]?.message?.reasoning_content ?? ''; + case chat_completion_sources.OPENROUTER: + return data?.choices?.[0]?.message?.reasoning ?? ''; + case chat_completion_sources.MAKERSUITE: + return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; + } + break; + } + + return ''; +} + +/** + * Updates the Reasoning controls + * @param {HTMLElement} element The element to update + * @param {number?} duration The duration of the reasoning in milliseconds + * @param {object} [options={}] Options for the function + * @param {boolean} [options.forceEnd=false] If true, there will be no "Thinking..." when no duration exists + */ +export function updateReasoningTimeUI(element, duration, { forceEnd = false } = {}) { + if (duration) { + const durationStr = moment.duration(duration).locale(getCurrentLocale()).humanize({ s: 50, ss: 9 }); + element.textContent = t`Thought for ${durationStr}`; + } else if (forceEnd) { + element.textContent = t`Thought for some time`; + } else { + element.textContent = t`Thinking...`; + } +} + /** * Helper class for adding reasoning to messages. * Keeps track of the number of reasoning additions. @@ -247,6 +301,29 @@ function registerReasoningMacros() { } function setReasoningEventHandlers() { + $(document).on('click', '.mes_reasoning_summary', function () { + // If you toggle summary header while editing reasoning, yup - we just cancel it + $(this).closest('.mes').find('.mes_reasoning_edit_cancel:visible').trigger('click'); + }); + + $(document).on('click', '.mes_reasoning_details', function (e) { + if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) { + e.preventDefault(); + } + }); + + $(document).on('click', '.mes_reasoning_header', function () { + // If we are in message edit mode and reasoning area is closed, a click opens and edits it + const mes = $(this).closest('.mes'); + const mesEditArea = mes.find('#curEditTextarea'); + if (mesEditArea.length) { + const summary = $(mes).find('.mes_reasoning_summary'); + if (!summary.attr('open')) { + summary.find('.mes_reasoning_edit').trigger('click'); + } + } + }); + $(document).on('click', '.mes_reasoning_copy', (e) => { e.stopPropagation(); e.preventDefault();