mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge pull request #3442 from SillyTavern/more-reasoning-ui
Smaller improvements and fixes to the reasoning UI
This commit is contained in:
@@ -225,7 +225,7 @@ import {
|
|||||||
instruct_presets,
|
instruct_presets,
|
||||||
selectContextPreset,
|
selectContextPreset,
|
||||||
} from './scripts/instruct-mode.js';
|
} 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 { getFriendlyTokenizerName, getTokenCount, getTokenCountAsync, initTokenizers, saveTokenCache, TOKENIZER_SUPPORTED_KEY } from './scripts/tokenizers.js';
|
||||||
import {
|
import {
|
||||||
user_avatar,
|
user_avatar,
|
||||||
@@ -269,7 +269,7 @@ import { initSettingsSearch } from './scripts/setting-search.js';
|
|||||||
import { initBulkEdit } from './scripts/bulk-edit.js';
|
import { initBulkEdit } from './scripts/bulk-edit.js';
|
||||||
import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
|
import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
|
||||||
import { getContext } from './scripts/st-context.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
|
// API OBJECT FOR EXTERNAL WIRING
|
||||||
globalThis.SillyTavern = {
|
globalThis.SillyTavern = {
|
||||||
@@ -2275,11 +2275,15 @@ function getMessageFromTemplate({
|
|||||||
* Re-renders a message block with updated content.
|
* Re-renders a message block with updated content.
|
||||||
* @param {number} messageId Message ID
|
* @param {number} messageId Message ID
|
||||||
* @param {object} message Message object
|
* @param {object} message Message object
|
||||||
|
* @param {object} [options={}] Optional arguments
|
||||||
|
* @param {boolean} [options.rerenderMessage=true] Whether to re-render the message content (inside <c>.mes_text</c>)
|
||||||
*/
|
*/
|
||||||
export function updateMessageBlock(messageId, message) {
|
export function updateMessageBlock(messageId, message, { rerenderMessage = true } = {}) {
|
||||||
const messageElement = $(`#chat [mesid="${messageId}"]`);
|
const messageElement = $(`#chat [mesid="${messageId}"]`);
|
||||||
|
if (rerenderMessage) {
|
||||||
const text = message?.extra?.display_text ?? message.mes;
|
const text = message?.extra?.display_text ?? message.mes;
|
||||||
messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId, {}, false));
|
messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId, {}, false));
|
||||||
|
}
|
||||||
messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, messageId, {}, true));
|
messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, messageId, {}, true));
|
||||||
messageElement.toggleClass('reasoning', !!message.extra?.reasoning);
|
messageElement.toggleClass('reasoning', !!message.extra?.reasoning);
|
||||||
addCopyToCodeBlocks(messageElement);
|
addCopyToCodeBlocks(messageElement);
|
||||||
@@ -5763,56 +5767,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.
|
* Extracts multiswipe swipes from the response data.
|
||||||
* @param {Object} data Response data
|
* @param {Object} data Response data
|
||||||
@@ -10872,18 +10826,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 () {
|
$(document).on('input', '#curEditTextarea', function () {
|
||||||
if (power_user.auto_save_msg_edits === true) {
|
if (power_user.auto_save_msg_edits === true) {
|
||||||
messageEditAuto($(this));
|
messageEditAuto($(this));
|
||||||
@@ -11438,17 +11380,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) {
|
$(document).keyup(function (e) {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;
|
const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;
|
||||||
|
@@ -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 { 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 { MacrosParser } from './macros.js';
|
||||||
|
import { chat_completion_sources, oai_settings } from './openai.js';
|
||||||
import { Popup } from './popup.js';
|
import { Popup } from './popup.js';
|
||||||
import { power_user } from './power-user.js';
|
import { power_user } from './power-user.js';
|
||||||
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
|
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
|
||||||
import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||||
|
import { textgen_types, textgenerationwebui_settings } from './textgen-settings.js';
|
||||||
import { copyText, escapeRegex, isFalseBoolean } from './utils.js';
|
import { copyText, escapeRegex, isFalseBoolean } from './utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,6 +39,56 @@ 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: 3 });
|
||||||
|
const secondsStr = moment.duration(duration).asSeconds();
|
||||||
|
element.innerHTML = t`Thought for <span title="${secondsStr} seconds">${durationStr}</span>`;
|
||||||
|
} else if (forceEnd) {
|
||||||
|
element.textContent = t`Thought for some time`;
|
||||||
|
} else {
|
||||||
|
element.textContent = t`Thinking...`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for adding reasoning to messages.
|
* Helper class for adding reasoning to messages.
|
||||||
* Keeps track of the number of reasoning additions.
|
* Keeps track of the number of reasoning additions.
|
||||||
@@ -247,6 +302,24 @@ function registerReasoningMacros() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setReasoningEventHandlers() {
|
function setReasoningEventHandlers() {
|
||||||
|
$(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) => {
|
$(document).on('click', '.mes_reasoning_copy', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -323,7 +396,7 @@ function setReasoningEventHandlers() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.mes_edit_add_reasoning', async function () {
|
$(document).on('click', '.mes_edit_add_reasoning', async function () {
|
||||||
const { message, messageId } = getMessageFromJquery(this);
|
const { message, messageId, messageBlock } = getMessageFromJquery(this);
|
||||||
if (!message?.extra) {
|
if (!message?.extra) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -334,7 +407,8 @@ function setReasoningEventHandlers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
|
message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
|
||||||
updateMessageBlock(messageId, message);
|
updateMessageBlock(messageId, message, { rerenderMessage: false });
|
||||||
|
messageBlock.find('.mes_reasoning_edit').trigger('click');
|
||||||
await saveChatConditional();
|
await saveChatConditional();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -348,13 +422,15 @@ function setReasoningEventHandlers() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { message, messageId } = getMessageFromJquery(this);
|
const { message, messageId, messageBlock } = getMessageFromJquery(this);
|
||||||
if (!message?.extra) {
|
if (!message?.extra) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.extra.reasoning = '';
|
message.extra.reasoning = '';
|
||||||
await saveChatConditional();
|
await saveChatConditional();
|
||||||
updateMessageBlock(messageId, message);
|
updateMessageBlock(messageId, message);
|
||||||
|
const textarea = messageBlock.find('.reasoning_edit_textarea');
|
||||||
|
textarea.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('pointerup', '.mes_reasoning_copy', async function () {
|
$(document).on('pointerup', '.mes_reasoning_copy', async function () {
|
||||||
|
@@ -388,7 +388,7 @@ input[type='checkbox']:focus-visible {
|
|||||||
margin: 0.5em 2px;
|
margin: 0.5em 2px;
|
||||||
padding: 7px 14px;
|
padding: 7px 14px;
|
||||||
padding-right: calc(0.7em + 14px);
|
padding-right: calc(0.7em + 14px);
|
||||||
border-radius: 10px;
|
border-radius: 5px;
|
||||||
background-color: var(--grey30);
|
background-color: var(--grey30);
|
||||||
font-size: calc(var(--mainFontSize) * 0.9);
|
font-size: calc(var(--mainFontSize) * 0.9);
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
Reference in New Issue
Block a user