From afae8d02be1e837b1dc5abc013fa011ab4c3d067 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Thu, 23 Jan 2025 02:52:52 +0200
Subject: [PATCH 01/83] The THONKening
---
public/index.html | 1 +
public/script.js | 111 +++++++++++++++------
public/scripts/constants.js | 5 +
public/scripts/kai-settings.js | 2 +-
public/scripts/nai-settings.js | 2 +-
public/scripts/openai.js | 19 ++--
public/scripts/textgen-settings.js | 3 +-
public/style.css | 28 +++++-
src/constants.js | 5 +
src/endpoints/backends/chat-completions.js | 3 +-
10 files changed, 130 insertions(+), 49 deletions(-)
diff --git a/public/index.html b/public/index.html
index 3984afa5e..3fe2162fb 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6229,6 +6229,7 @@
+
diff --git a/public/script.js b/public/script.js
index 3ba3aacbc..b641e161c 100644
--- a/public/script.js
+++ b/public/script.js
@@ -170,7 +170,7 @@ import {
isElementInViewport,
copyText,
} from './scripts/utils.js';
-import { debounce_timeout } from './scripts/constants.js';
+import { debounce_timeout, THINK_BREAK } from './scripts/constants.js';
import { doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js';
@@ -2199,6 +2199,7 @@ function getMessageFromTemplate({
isUser,
avatarImg,
bias,
+ reasoning,
isSystem,
title,
timerValue,
@@ -2223,6 +2224,7 @@ function getMessageFromTemplate({
mes.find('.avatar img').attr('src', avatarImg);
mes.find('.ch_name .name_text').text(characterName);
mes.find('.mes_bias').html(bias);
+ mes.find('.mes_reasoning').html(reasoning);
mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`);
mes.find('.mesIDDisplay').text(`#${mesId}`);
tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`);
@@ -2241,6 +2243,7 @@ export function updateMessageBlock(messageId, message) {
const messageElement = $(`#chat [mesid="${messageId}"]`);
const text = message?.extra?.display_text ?? message.mes;
messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId));
+ messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, -1));
addCopyToCodeBlocks(messageElement);
appendMediaToMessage(message, messageElement);
}
@@ -2399,6 +2402,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
sanitizerOverrides,
);
const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1);
+ const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, -1);
let bookmarkLink = mes?.extra?.bookmark_link ?? '';
let params = {
@@ -2408,6 +2412,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
isUser: mes.is_user,
avatarImg: avatarImg,
bias: bias,
+ reasoning: reasoning,
isSystem: isSystem,
title: title,
bookmarkLink: bookmarkLink,
@@ -2467,6 +2472,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`);
swipeMessage.attr('swipeid', params.swipeId);
swipeMessage.find('.mes_text').html(messageText).attr('title', title);
+ swipeMessage.find('.mes_reasoning').html(reasoning);
swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);
appendMediaToMessage(mes, swipeMessage);
if (power_user.timestamp_model_icon && params.extra?.api) {
@@ -3077,6 +3083,7 @@ class StreamingProcessor {
this.messageTextDom = null;
this.messageTimerDom = null;
this.messageTokenCounterDom = null;
+ this.messageReasoningDom = null;
/** @type {HTMLTextAreaElement} */
this.sendTextarea = document.querySelector('#send_textarea');
this.type = type;
@@ -3092,6 +3099,7 @@ class StreamingProcessor {
/** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */
this.messageLogprobs = [];
this.toolCalls = [];
+ this.reasoning = '';
}
#checkDomElements(messageId) {
@@ -3100,6 +3108,7 @@ class StreamingProcessor {
this.messageTextDom = this.messageDom?.querySelector('.mes_text');
this.messageTimerDom = this.messageDom?.querySelector('.mes_timer');
this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay');
+ this.messageReasoningDom = this.messageDom?.querySelector('.mes_reasoning');
}
}
@@ -3184,11 +3193,17 @@ class StreamingProcessor {
chat[messageId]['gen_started'] = this.timeStarted;
chat[messageId]['gen_finished'] = currentTime;
- if (currentTokenCount) {
- if (!chat[messageId]['extra']) {
- chat[messageId]['extra'] = {};
- }
+ if (!chat[messageId]['extra']) {
+ chat[messageId]['extra'] = {};
+ }
+ if (this.reasoning && this.messageReasoningDom instanceof HTMLElement) {
+ chat[messageId]['extra']['reasoning'] = this.reasoning;
+ const formattedReasoning = messageFormatting(this.reasoning, '', false, false, -1);
+ this.messageReasoningDom.innerHTML = formattedReasoning;
+ }
+
+ if (currentTokenCount) {
chat[messageId]['extra']['token_count'] = currentTokenCount;
if (this.messageTokenCounterDom instanceof HTMLElement) {
this.messageTokenCounterDom.textContent = `${currentTokenCount}t`;
@@ -3320,7 +3335,7 @@ class StreamingProcessor {
}
/**
- * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[] }, void, void>}
+ * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>}
*/
*nullStreamingGeneration() {
throw new Error('Generation function for streaming is not hooked up');
@@ -3342,7 +3357,7 @@ class StreamingProcessor {
try {
const sw = new Stopwatch(1000 / power_user.streaming_fps);
const timestamps = [];
- for await (const { text, swipes, logprobs, toolCalls } of this.generator()) {
+ for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) {
timestamps.push(Date.now());
if (this.isStopped) {
return;
@@ -3354,6 +3369,7 @@ class StreamingProcessor {
if (logprobs) {
this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));
}
+ this.reasoning = state?.reasoning ?? '';
await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text);
await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text));
}
@@ -4741,6 +4757,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
//const getData = await response.json();
let getMessage = extractMessageFromData(data);
let title = extractTitleFromData(data);
+ let reasoning = extractReasoningFromData(data);
kobold_horde_model = title;
const swipes = extractMultiSwipes(data, type);
@@ -4767,10 +4784,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
else {
// Without streaming we'll be having a full message on continuation. Treat it as a last chunk.
if (originalType !== 'continue') {
- ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes));
+ ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes, reasoning));
}
else {
- ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes));
+ ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes, reasoning));
}
// This relies on `saveReply` having been called to add the message to the chat, so it must be last.
@@ -5649,42 +5666,65 @@ function parseAndSaveLogprobs(data, continueFrom) {
}
/**
- * Extracts the message from the response data.
- * @param {object} data Response data
- * @returns {string} Extracted message
+ * Gets the text context from the response data.
+ * @param {object} data Response JSON data
+ * @returns {string} Extracted text
*/
-function extractMessageFromData(data) {
+function getTextContextFromData(data) {
if (typeof data === 'string') {
return data;
}
- function getTextContext() {
- switch (main_api) {
- case 'kobold':
- return data.results[0].text;
- case 'koboldhorde':
- return data.text;
- case 'textgenerationwebui':
- return data.choices?.[0]?.text ?? data.content ?? data.response ?? '';
- case 'novel':
- return data.output;
- case 'openai':
- return data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? data?.message?.content?.[0]?.text ?? data?.message?.tool_plan ?? '';
- default:
- return '';
- }
+ switch (main_api) {
+ case 'kobold':
+ return data.results[0].text;
+ case 'koboldhorde':
+ return data.text;
+ case 'textgenerationwebui':
+ return data.choices?.[0]?.text ?? data.content ?? data.response ?? '';
+ case 'novel':
+ return data.output;
+ case 'openai':
+ return data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? data?.message?.content?.[0]?.text ?? data?.message?.tool_plan ?? '';
+ default:
+ return '';
}
+}
- const content = getTextContext();
+/**
+ * Extracts the message from the response data.
+ * @param {object} data Response data
+ * @returns {string} Extracted message
+ */
+function extractMessageFromData(data){
+ const content = String(getTextContextFromData(data) ?? '');
- if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) {
- const thoughts = data?.choices?.[0]?.message?.reasoning_content ?? '';
- return [thoughts, content].filter(x => x).join('\n\n');
+ if (content.includes(THINK_BREAK)) {
+ return content.split(THINK_BREAK)[1];
}
return content;
}
+/**
+ * Extracts the reasoning from the response data.
+ * @param {object} data Response data
+ * @returns {string} Extracted reasoning
+ */
+function extractReasoningFromData(data) {
+ const content = String(getTextContextFromData(data) ?? '');
+
+ if (content.includes(THINK_BREAK)) {
+ return content.split(THINK_BREAK)[0];
+ }
+
+ if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) {
+ return data?.choices?.[0]?.message?.reasoning_content ?? '';
+ }
+
+ return '';
+}
+
/**
* Extracts multiswipe swipes from the response data.
* @param {Object} data Response data
@@ -5865,7 +5905,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc
return getMessage;
}
-export async function saveReply(type, getMessage, fromStreaming, title, swipes) {
+export async function saveReply(type, getMessage, fromStreaming, title, swipes, reasoning) {
if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined ||
chat[chat.length - 1]['is_user'])) {
type = 'normal';
@@ -5890,6 +5930,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
+ chat[chat.length - 1]['extra']['reasoning'] = reasoning;
if (power_user.message_token_count_enabled) {
chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);
}
@@ -5910,6 +5951,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
+ chat[chat.length - 1]['extra']['reasoning'] += reasoning;
if (power_user.message_token_count_enabled) {
chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);
}
@@ -5927,6 +5969,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
+ chat[chat.length - 1]['extra']['reasoning'] += reasoning;
if (power_user.message_token_count_enabled) {
chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);
}
@@ -5944,6 +5987,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes)
chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
+ chat[chat.length - 1]['extra']['reasoning'] = reasoning;
if (power_user.trim_spaces) {
getMessage = getMessage.trim();
}
@@ -8646,6 +8690,7 @@ const swipe_right = () => {
// resets the timer
swipeMessage.find('.mes_timer').html('');
swipeMessage.find('.tokenCounterDisplay').text('');
+ swipeMessage.find('.mes_reasoning').html('');
} else {
//console.log('showing previously generated swipe candidate, or "..."');
//console.log('onclick right swipe calling addOneMessage');
diff --git a/public/scripts/constants.js b/public/scripts/constants.js
index f95a8e146..935a74219 100644
--- a/public/scripts/constants.js
+++ b/public/scripts/constants.js
@@ -14,3 +14,8 @@ export const debounce_timeout = {
/** [5 sec] For delayed tasks, like auto-saving or completing batch operations that need a significant pause. */
extended: 5000,
};
+
+/**
+ * Custom boundary for splitting the text between the model's reasoning and the actual response.
+ */
+export const THINK_BREAK = '##�THINK_BREAK�##';
diff --git a/public/scripts/kai-settings.js b/public/scripts/kai-settings.js
index 6efadce87..65d47fc4b 100644
--- a/public/scripts/kai-settings.js
+++ b/public/scripts/kai-settings.js
@@ -188,7 +188,7 @@ export async function generateKoboldWithStreaming(generate_data, signal) {
if (data?.token) {
text += data.token;
}
- yield { text, swipes: [], toolCalls: [] };
+ yield { text, swipes: [], toolCalls: [], state: {} };
}
};
}
diff --git a/public/scripts/nai-settings.js b/public/scripts/nai-settings.js
index f95e7d9f6..91ff09ef6 100644
--- a/public/scripts/nai-settings.js
+++ b/public/scripts/nai-settings.js
@@ -746,7 +746,7 @@ export async function generateNovelWithStreaming(generate_data, signal) {
text += data.token;
}
- yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [] };
+ yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [], state: {} };
}
};
}
diff --git a/public/scripts/openai.js b/public/scripts/openai.js
index 54e9f6125..5585f82ef 100644
--- a/public/scripts/openai.js
+++ b/public/scripts/openai.js
@@ -2095,7 +2095,7 @@ async function sendOpenAIRequest(type, messages, signal) {
let text = '';
const swipes = [];
const toolCalls = [];
- const state = {};
+ const state = { reasoning: '' };
while (true) {
const { done, value } = await reader.read();
if (done) return;
@@ -2113,7 +2113,7 @@ async function sendOpenAIRequest(type, messages, signal) {
ToolManager.parseToolCalls(toolCalls, parsed);
- yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls };
+ yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls, state: state };
}
};
}
@@ -2150,16 +2150,17 @@ function getStreamingReply(data, state) {
if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
return data?.delta?.text || '';
} else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
- return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || '';
+ if (oai_settings.show_thoughts) {
+ state.reasoning += (data?.candidates?.[0]?.content?.parts?.filter(x => x.thought)?.map(x => x.text)?.[0] || '');
+ }
+ return data?.candidates?.[0]?.content?.parts?.filter(x => !x.thought)?.map(x => x.text)?.[0] || '';
} else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || '';
} else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) {
- const hadThoughts = state.hadThoughts;
- const thoughts = data.choices?.filter(x => oai_settings.show_thoughts || !x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '';
- const content = data.choices?.[0]?.delta?.content || '';
- state.hadThoughts = !!thoughts;
- const separator = hadThoughts && !thoughts ? '\n\n' : '';
- return [thoughts, separator, content].filter(x => x).join('\n\n');
+ if (oai_settings.show_thoughts) {
+ state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '');
+ }
+ return data.choices?.[0]?.delta?.content || '';
} else {
return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
}
diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js
index cd1004991..19b729374 100644
--- a/public/scripts/textgen-settings.js
+++ b/public/scripts/textgen-settings.js
@@ -986,6 +986,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) {
let logprobs = null;
const swipes = [];
const toolCalls = [];
+ const state = {};
while (true) {
const { done, value } = await reader.read();
if (done) return;
@@ -1004,7 +1005,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) {
logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities);
}
- yield { text, swipes, logprobs, toolCalls };
+ yield { text, swipes, logprobs, toolCalls, state };
}
};
}
diff --git a/public/style.css b/public/style.css
index cbfc96185..ee7cf9094 100644
--- a/public/style.css
+++ b/public/style.css
@@ -332,6 +332,23 @@ input[type='checkbox']:focus-visible {
color: var(--SmartThemeQuoteColor);
}
+.mes_reasoning {
+ display: block;
+ border: 1px solid var(--SmartThemeBorderColor);
+ background-color: var(--black30a);
+ border-radius: 5px;
+ padding: 5px;
+ margin: 5px 0;
+ overflow-y: auto;
+ max-height: 100px;
+}
+
+.mes_block:has(.edit_textarea) .mes_reasoning,
+.mes_bias:empty,
+.mes_reasoning:empty {
+ display: none;
+}
+
.mes_text i,
.mes_text em {
color: var(--SmartThemeEmColor);
@@ -1022,6 +1039,7 @@ body .panelControlBar {
/*only affects bubblechat to make it sit nicely at the bottom*/
}
+.last_mes .mes_reasoning,
.last_mes .mes_text {
padding-right: 30px;
}
@@ -1235,14 +1253,18 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
overflow-y: clip;
}
-.mes_text {
+.mes_text,
+.mes_reasoning {
font-weight: 500;
line-height: calc(var(--mainFontSize) + .5rem);
+ max-width: 100%;
+ overflow-wrap: anywhere;
+}
+
+.mes_text {
padding-left: 0;
padding-top: 5px;
padding-bottom: 5px;
- max-width: 100%;
- overflow-wrap: anywhere;
}
br {
diff --git a/src/constants.js b/src/constants.js
index 35118a04b..faddaaf81 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -413,3 +413,8 @@ export const VLLM_KEYS = [
'guided_decoding_backend',
'guided_whitespace_pattern',
];
+
+/**
+ * Custom boundary for splitting the text between the model's reasoning and the actual response.
+ */
+export const THINK_BREAK = '##�THINK_BREAK�##';
diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js
index d67f309da..5e81b2280 100644
--- a/src/endpoints/backends/chat-completions.js
+++ b/src/endpoints/backends/chat-completions.js
@@ -7,6 +7,7 @@ import {
CHAT_COMPLETION_SOURCES,
GEMINI_SAFETY,
OPENROUTER_HEADERS,
+ THINK_BREAK,
} from '../../constants.js';
import {
forwardFetchResponse,
@@ -389,7 +390,7 @@ async function sendMakerSuiteRequest(request, response) {
responseContent.parts = responseContent.parts.filter(part => !part.thought);
}
- const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join('\n\n');
+ const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join(THINK_BREAK);
if (!responseText) {
let message = 'Google AI Studio Candidate text empty';
console.log(message, generateResponseJson);
From 823b9db6f6d6403354433584ecd02f14c603de68 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Thu, 23 Jan 2025 22:41:39 +0200
Subject: [PATCH 02/83] Gemini: Fix requesting thought blocks
---
src/endpoints/backends/chat-completions.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js
index 5e81b2280..f50a35240 100644
--- a/src/endpoints/backends/chat-completions.js
+++ b/src/endpoints/backends/chat-completions.js
@@ -288,6 +288,7 @@ async function sendMakerSuiteRequest(request, response) {
const model = String(request.body.model);
const stream = Boolean(request.body.stream);
const showThoughts = Boolean(request.body.show_thoughts);
+ const isThinking = model.includes('thinking');
const generationConfig = {
stopSequences: request.body.stop,
@@ -328,6 +329,12 @@ async function sendMakerSuiteRequest(request, response) {
body.systemInstruction = prompt.system_instruction;
}
+ if (isThinking && showThoughts) {
+ generationConfig.thinkingConfig = {
+ includeThoughts: true,
+ };
+ }
+
return body;
}
@@ -341,7 +348,6 @@ async function sendMakerSuiteRequest(request, response) {
controller.abort();
});
- const isThinking = model.includes('thinking');
const apiVersion = isThinking ? 'v1alpha' : 'v1beta';
const responseType = (stream ? 'streamGenerateContent' : 'generateContent');
From 144277bdcc5ac110d888510b79d0ad6a8d92873a Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Thu, 23 Jan 2025 22:43:04 +0200
Subject: [PATCH 03/83] Wrap thonk into collapsible
---
public/index.html | 7 ++++++-
public/style.css | 13 +++++++++++--
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/public/index.html b/public/index.html
index 3fe2162fb..4255bcae5 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6229,7 +6229,12 @@
-
Miscellaneous
@@ -6221,11 +6254,10 @@
-
+
+
-
+
diff --git a/public/locales/fr-fr.json b/public/locales/fr-fr.json
index 96124e043..342624c8a 100644
--- a/public/locales/fr-fr.json
+++ b/public/locales/fr-fr.json
@@ -1385,7 +1385,7 @@
"enable_functions_desc_1": "Autorise l'utilisation",
"enable_functions_desc_2": "outils de fonction",
"enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.",
- "Show model thoughts": "Afficher les pensées du modèle",
+ "Show model reasoning": "Afficher les pensées du modèle",
"Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.",
"Confirm token parsing with": "Confirmer l'analyse des tokens avec",
"openai_logit_bias_no_items": "Aucun élément",
diff --git a/public/locales/zh-cn.json b/public/locales/zh-cn.json
index 04129749c..4df2fd60a 100644
--- a/public/locales/zh-cn.json
+++ b/public/locales/zh-cn.json
@@ -266,7 +266,7 @@
"Use system prompt": "使用系统提示词",
"Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过",
"Merges_all_system_messages_desc_2": "字段发送。",
- "Show model thoughts": "展示思维链",
+ "Show model reasoning": "展示思维链",
"Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。",
"Assistant Prefill": "AI预填",
"Expand the editor": "展开编辑器",
diff --git a/public/locales/zh-tw.json b/public/locales/zh-tw.json
index 5d8313c1d..f1656afe7 100644
--- a/public/locales/zh-tw.json
+++ b/public/locales/zh-tw.json
@@ -2357,7 +2357,7 @@
"Forbid": "禁止",
"Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。",
"Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。",
- "Show model thoughts": "顯示模型思維鏈",
+ "Show model reasoning": "顯示模型思維鏈",
"Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。",
"Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]",
"Model ID (optional)": "模型 ID(可選)",
diff --git a/public/script.js b/public/script.js
index c18d047b1..f91070709 100644
--- a/public/script.js
+++ b/public/script.js
@@ -238,7 +238,7 @@ import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_set
import { hideLoader, showLoader } from './scripts/loader.js';
import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';
-import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js';
+import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat, PromptReasoning } from './scripts/chats.js';
import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
import { currentUser, setUserControls } from './scripts/user.js';
@@ -3844,6 +3844,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
coreChat.pop();
}
+ const reasoning = new PromptReasoning();
+ for (let i = coreChat.length - 1; i >= 0; i--) {
+ coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) };
+ }
+
coreChat = await Promise.all(coreChat.map(async (chatItem, index) => {
let message = chatItem.mes;
let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT;
@@ -8034,7 +8039,7 @@ function updateEditArrowClasses() {
}
}
-function closeMessageEditor() {
+export function closeMessageEditor() {
if (this_edit_mes_id) {
$(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();
}
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
index 43dbb9b0c..f18f5c883 100644
--- a/public/scripts/chats.js
+++ b/public/scripts/chats.js
@@ -24,6 +24,8 @@ import {
updateChatMetadata,
system_message_types,
updateMessageBlock,
+ closeMessageEditor,
+ substituteParams,
} from '../script.js';
import { selected_group } from './group-chats.js';
import { power_user } from './power-user.js';
@@ -1418,6 +1420,47 @@ export function registerFileConverter(mimeType, converter) {
converters[mimeType] = converter;
}
+/**
+ * Helper class for adding reasoning to messages.
+ * Keeps track of the number of reasoning additions.
+ */
+export class PromptReasoning {
+ static REASONING_PLACEHOLDER = '\u200B';
+
+ constructor() {
+ this.counter = 0;
+ }
+
+ /**
+ * Add reasoning to a message according to the power user settings.
+ * @param {string} content Message content
+ * @param {string} reasoning Message reasoning
+ * @returns {string} Message content with reasoning
+ */
+ addToMessage(content, reasoning) {
+ // Disabled or reached limit of additions
+ if (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions) {
+ return content;
+ }
+
+ // No reasoning provided or a placeholder
+ if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) {
+ return content;
+ }
+
+ // Increment the counter
+ this.counter++;
+
+ // Substitute macros in variable parts
+ const prefix = substituteParams(power_user.reasoning.prefix || '');
+ const separator = substituteParams(power_user.reasoning.separator || '');
+ const suffix = substituteParams(power_user.reasoning.suffix || '');
+
+ // Combine parts with reasoning and content
+ return `${prefix}${reasoning}${suffix}${separator}${content}`;
+ }
+}
+
jQuery(function () {
$(document).on('click', '.mes_hide', async function () {
const messageBlock = $(this).closest('.mes');
@@ -1574,6 +1617,25 @@ jQuery(function () {
e.preventDefault();
});
+ $(document).on('click', '.mes_edit_add_reasoning', async function () {
+ const mesBlock = $(this).closest('.mes');
+ const mesId = Number(mesBlock.attr('mesid'));
+ const message = chat[mesId];
+ if (!message?.extra){
+ return;
+ }
+
+ if (message.extra.reasoning) {
+ toastr.info(t`Reasoning already exists.`, t`Edit Message`);
+ return;
+ }
+
+ message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
+ await saveChatConditional();
+ closeMessageEditor();
+ updateMessageBlock(mesId, message);
+ });
+
$(document).on('click', '.mes_reasoning_delete', async function (e) {
e.stopPropagation();
e.preventDefault();
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index bfe8eca2e..4859c0c71 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -253,6 +253,14 @@ let power_user = {
content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.',
},
+ reasoning: {
+ add_to_prompts: false,
+ prefix: '
\n',
+ suffix: '\n',
+ separator: '\n',
+ max_additions: 1,
+ },
+
personas: {},
default_persona: null,
persona_descriptions: {},
@@ -1613,6 +1621,7 @@ async function loadPowerUserSettings(settings, data) {
loadMovingUIState();
loadCharListState();
toggleMDHotkeyIconDisplay();
+ loadReasoningSettings();
}
function toggleMDHotkeyIconDisplay() {
@@ -1629,6 +1638,38 @@ function loadCharListState() {
document.body.classList.toggle('charListGrid', power_user.charListGrid);
}
+function loadReasoningSettings() {
+ $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts);
+ $('#reasoning_add_to_prompts').on('change', function () {
+ power_user.reasoning.add_to_prompts = !!$(this).prop('checked');
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_prefix').val(power_user.reasoning.prefix);
+ $('#reasoning_prefix').on('input', function () {
+ power_user.reasoning.prefix = String($(this).val());
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_suffix').val(power_user.reasoning.suffix);
+ $('#reasoning_suffix').on('input', function () {
+ power_user.reasoning.suffix = String($(this).val());
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_separator').val(power_user.reasoning.separator);
+ $('#reasoning_separator').on('input', function () {
+ power_user.reasoning.separator = String($(this).val());
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_max_additions').val(power_user.reasoning.max_additions);
+ $('#reasoning_max_additions').on('input', function () {
+ power_user.reasoning.max_additions = Number($(this).val());
+ saveSettingsDebounced();
+ });
+}
+
function loadMovingUIState() {
if (!isMobile()
&& power_user.movingUIState
From 8fc880b69b88667f8120266669369dad2921e73e Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 26 Jan 2025 02:07:50 +0200
Subject: [PATCH 14/83] Early stopping if prompt reasoning limit reached
---
public/script.js | 3 +++
public/scripts/chats.js | 12 ++++++++++++
2 files changed, 15 insertions(+)
diff --git a/public/script.js b/public/script.js
index f91070709..b0ed60704 100644
--- a/public/script.js
+++ b/public/script.js
@@ -3846,6 +3846,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
const reasoning = new PromptReasoning();
for (let i = coreChat.length - 1; i >= 0; i--) {
+ if (reasoning.isLimitReached()) {
+ break;
+ }
coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) };
}
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
index f18f5c883..ef1db0732 100644
--- a/public/scripts/chats.js
+++ b/public/scripts/chats.js
@@ -1431,6 +1431,18 @@ export class PromptReasoning {
this.counter = 0;
}
+ /**
+ * Checks if the limit of reasoning additions has been reached.
+ * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise.
+ */
+ isLimitReached() {
+ if (!power_user.reasoning.add_to_prompts) {
+ return true;
+ }
+
+ return this.counter >= power_user.reasoning.max_additions;
+ }
+
/**
* Add reasoning to a message according to the power user settings.
* @param {string} content Message content
From 45d4d1bb3ef2f544f0d652f598d590067dd32348 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 26 Jan 2025 02:49:10 +0200
Subject: [PATCH 15/83] [wip] Open reasoning editor
---
public/index.html | 3 +++
public/script.js | 2 +-
public/scripts/chats.js | 48 ++++++++++++++++++++++++++++++-----------
public/style.css | 27 +++++++++++++----------
4 files changed, 55 insertions(+), 25 deletions(-)
diff --git a/public/index.html b/public/index.html
index 7bb533dc7..c7cc4c617 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6265,6 +6265,9 @@
Reasoning
diff --git a/public/script.js b/public/script.js
index b0ed60704..b4fd28f2f 100644
--- a/public/script.js
+++ b/public/script.js
@@ -11274,7 +11274,7 @@ jQuery(async function () {
$(document).keyup(function (e) {
if (e.key === 'Escape') {
- const isEditVisible = $('#curEditTextarea').is(':visible');
+ const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length;
if (isEditVisible && power_user.auto_save_msg_edits === false) {
closeMessageEditor();
$('#send_textarea').focus();
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
index ef1db0732..5b2db51c5 100644
--- a/public/scripts/chats.js
+++ b/public/scripts/chats.js
@@ -1474,6 +1474,18 @@ export class PromptReasoning {
}
jQuery(function () {
+ /**
+ * Gets a message from a jQuery element.
+ * @param {Element} element
+ * @returns {{messageId: number, message: object, messageBlock: JQuery}}
+ */
+ const getMessageFromJquery = function (element) {
+ const messageBlock = $(element).closest('.mes');
+ const messageId = Number(messageBlock.attr('mesid'));
+ const message = chat[messageId];
+ return { messageId: messageId, message, messageBlock };
+ };
+
$(document).on('click', '.mes_hide', async function () {
const messageBlock = $(this).closest('.mes');
const messageId = Number(messageBlock.attr('mesid'));
@@ -1629,11 +1641,25 @@ jQuery(function () {
e.preventDefault();
});
+ $(document).on('click', '.mes_reasoning_edit', function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ const { message, messageBlock } = getMessageFromJquery(this);
+ if (!message?.extra) {
+ return;
+ }
+
+ const reasoning = message?.extra?.reasoning;
+ const textarea = document.createElement('textarea');
+ const reasoningBlock = messageBlock.find('.mes_reasoning');
+ textarea.classList.add('reasoning_edit_textarea');
+ textarea.value = reasoning === PromptReasoning.REASONING_PLACEHOLDER ? '' : reasoning;
+ $(textarea).insertBefore(reasoningBlock);
+ });
+
$(document).on('click', '.mes_edit_add_reasoning', async function () {
- const mesBlock = $(this).closest('.mes');
- const mesId = Number(mesBlock.attr('mesid'));
- const message = chat[mesId];
- if (!message?.extra){
+ const { message, messageId } = getMessageFromJquery(this);
+ if (!message?.extra) {
return;
}
@@ -1645,7 +1671,7 @@ jQuery(function () {
message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
await saveChatConditional();
closeMessageEditor();
- updateMessageBlock(mesId, message);
+ updateMessageBlock(messageId, message);
});
$(document).on('click', '.mes_reasoning_delete', async function (e) {
@@ -1658,21 +1684,17 @@ jQuery(function () {
return;
}
- const mesBlock = $(this).closest('.mes');
- const mesId = Number(mesBlock.attr('mesid'));
- const message = chat[mesId];
- if (!message?.extra){
+ const { message, messageId } = getMessageFromJquery(this);
+ if (!message?.extra) {
return;
}
message.extra.reasoning = '';
await saveChatConditional();
- updateMessageBlock(mesId, message);
+ updateMessageBlock(messageId, message);
});
$(document).on('pointerup', '.mes_reasoning_copy', async function () {
- const mesBlock = $(this).closest('.mes');
- const mesId = Number(mesBlock.attr('mesid'));
- const message = chat[mesId];
+ const { message } = getMessageFromJquery(this);
const reasoning = message?.extra?.reasoning;
if (!reasoning) {
diff --git a/public/style.css b/public/style.css
index 2b7ce0a01..bd31aba7f 100644
--- a/public/style.css
+++ b/public/style.css
@@ -353,9 +353,18 @@ input[type='checkbox']:focus-visible {
.mes_reasoning_summary {
cursor: pointer;
position: relative;
+ margin: 2px;
}
-.mes_reasoning_details:not([open]) .mes_reasoning_actions {
+.mes_bias:empty,
+.mes_reasoning:empty,
+.mes_reasoning_details:has(.mes_reasoning:empty),
+.mes_block:has(.edit_textarea) .mes_reasoning_details,
+.mes_reasoning_details:not([open]) .mes_reasoning_actions,
+.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning,
+.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_done,
+.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_cancel,
+.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_actions .mes_button:not(.mes_reasoning_edit_done, .mes_reasoning_edit_cancel) {
display: none;
}
@@ -373,21 +382,14 @@ input[type='checkbox']:focus-visible {
padding: 1px;
}
-.mes_reasoning_summary > span {
+.mes_reasoning_summary>span {
margin-left: 0.5em;
}
-.mes_reasoning_details:has(.mes_reasoning:empty),
-.mes_block:has(.edit_textarea) .mes_reasoning_details,
-.mes_bias:empty,
-.mes_reasoning:empty {
- display: none;
-}
-
.mes_text i,
.mes_text em,
.mes_reasoning i,
-.mes_reasoning em {
+.mes_reasoning em {
color: var(--SmartThemeEmColor);
}
@@ -408,7 +410,7 @@ input[type='checkbox']:focus-visible {
.mes_reasoning font[color] em,
.mes_reasoning font[color] i,
.mes_reasoning font[color] u,
-.mes_reasoning font[color] q {
+.mes_reasoning font[color] q {
color: inherit;
}
@@ -4213,10 +4215,12 @@ input[type="range"]::-webkit-slider-thumb {
align-items: center;
}
+.mes_reasoning_edit_cancel,
.mes_edit_cancel.menu_button {
background-color: var(--crimson70a);
}
+.mes_reasoning_edit_done,
.mes_edit_done.menu_button {
background-color: var(--okGreen70a);
}
@@ -4225,6 +4229,7 @@ input[type="range"]::-webkit-slider-thumb {
opacity: 1;
}
+.reasoning_edit_textarea,
.edit_textarea {
padding: 5px;
margin: 0;
From 17d4175b47bb274048f2503ecb8e423836265308 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 26 Jan 2025 05:14:17 +0200
Subject: [PATCH 16/83] Functional reasoning edit
---
public/script.js | 27 +++++++++++++++++-----
public/scripts/chats.js | 50 +++++++++++++++++++++++++++++++++++++++++
public/style.css | 10 +++++++++
3 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/public/script.js b/public/script.js
index b4fd28f2f..037ff7bc3 100644
--- a/public/script.js
+++ b/public/script.js
@@ -5686,7 +5686,7 @@ function parseAndSaveLogprobs(data, continueFrom) {
* @param {object} data Response data
* @returns {string} Extracted message
*/
-function extractMessageFromData(data){
+function extractMessageFromData(data) {
if (typeof data === 'string') {
return data;
}
@@ -8042,9 +8042,23 @@ function updateEditArrowClasses() {
}
}
-export function closeMessageEditor() {
- if (this_edit_mes_id) {
- $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();
+/**
+ * Closes the message editor.
+ * @param {'message'|'reasoning'|'all'} what What to close. Default is 'all'.
+ */
+export function closeMessageEditor(what = 'all') {
+ if (what === 'message' || what === 'all') {
+ if (this_edit_mes_id) {
+ $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click();
+ }
+ }
+ if (what === 'reasoning' || what === 'all') {
+ document.querySelectorAll('.reasoning_edit_textarea').forEach((el) => {
+ const cancelButton = el.closest('.mes')?.querySelector('.mes_reasoning_edit_cancel');
+ if (cancelButton instanceof HTMLElement) {
+ cancelButton.click();
+ }
+ });
}
}
@@ -11274,14 +11288,15 @@ jQuery(async function () {
$(document).keyup(function (e) {
if (e.key === 'Escape') {
- const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length;
+ const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;
if (isEditVisible && power_user.auto_save_msg_edits === false) {
- closeMessageEditor();
+ closeMessageEditor('all');
$('#send_textarea').focus();
return;
}
if (isEditVisible && power_user.auto_save_msg_edits === true) {
$(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click();
+ closeMessageEditor('reasoning');
$('#send_textarea').focus();
return;
}
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
index 5b2db51c5..8d6ec6040 100644
--- a/public/scripts/chats.js
+++ b/public/scripts/chats.js
@@ -1650,11 +1650,61 @@ jQuery(function () {
}
const reasoning = message?.extra?.reasoning;
+ const chatElement = document.getElementById('chat');
const textarea = document.createElement('textarea');
const reasoningBlock = messageBlock.find('.mes_reasoning');
textarea.classList.add('reasoning_edit_textarea');
textarea.value = reasoning === PromptReasoning.REASONING_PLACEHOLDER ? '' : reasoning;
$(textarea).insertBefore(reasoningBlock);
+
+ if (!CSS.supports('field-sizing', 'content')) {
+ const resetHeight = function () {
+ const scrollTop = chatElement.scrollTop;
+ textarea.style.height = '0px';
+ textarea.style.height = `${textarea.scrollHeight}px`;
+ chatElement.scrollTop = scrollTop;
+ };
+
+ textarea.addEventListener('input', resetHeight);
+ resetHeight();
+ }
+
+ textarea.focus();
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length);
+
+ const textareaRect = textarea.getBoundingClientRect();
+ const chatRect = chatElement.getBoundingClientRect();
+
+ // Scroll if textarea bottom is below visible area
+ if (textareaRect.bottom > chatRect.bottom) {
+ const scrollOffset = textareaRect.bottom - chatRect.bottom;
+ chatElement.scrollTop += scrollOffset;
+ }
+ });
+
+ $(document).on('click', '.mes_reasoning_edit_done', async function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ const { message, messageId, messageBlock } = getMessageFromJquery(this);
+ if (!message?.extra) {
+ return;
+ }
+
+ const textarea = messageBlock.find('.reasoning_edit_textarea');
+ const reasoning = String(textarea.val());
+ message.extra.reasoning = reasoning;
+ await saveChatConditional();
+ updateMessageBlock(messageId, message);
+ textarea.remove();
+ });
+
+ $(document).on('click', '.mes_reasoning_edit_cancel', function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ const { messageBlock } = getMessageFromJquery(this);
+ const textarea = messageBlock.find('.reasoning_edit_textarea');
+ textarea.remove();
});
$(document).on('click', '.mes_edit_add_reasoning', async function () {
diff --git a/public/style.css b/public/style.css
index bd31aba7f..0318e6d7d 100644
--- a/public/style.css
+++ b/public/style.css
@@ -356,6 +356,12 @@ input[type='checkbox']:focus-visible {
margin: 2px;
}
+@supports not selector(:has(*)) {
+ .mes_reasoning_details {
+ display: none !important;
+ }
+}
+
.mes_bias:empty,
.mes_reasoning:empty,
.mes_reasoning_details:has(.mes_reasoning:empty),
@@ -1089,6 +1095,10 @@ body .panelControlBar {
/*only affects bubblechat to make it sit nicely at the bottom*/
}
+.last_mes:has(.mes_text:empty):has(.mes_reasoning_details[open]) .mes_reasoning:not(:empty) {
+ margin-bottom: 30px;
+}
+
.last_mes .mes_reasoning,
.last_mes .mes_text {
padding-right: 30px;
From eb798fa4f191892bffe07d40bd255b69dddf2d4f Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 26 Jan 2025 16:47:13 +0200
Subject: [PATCH 17/83] Move reasoning-specific code into its own module
---
public/script.js | 4 +-
public/scripts/chats.js | 188 -----------------------------
public/scripts/power-user.js | 33 -----
public/scripts/reasoning.js | 228 +++++++++++++++++++++++++++++++++++
4 files changed, 231 insertions(+), 222 deletions(-)
create mode 100644 public/scripts/reasoning.js
diff --git a/public/script.js b/public/script.js
index 037ff7bc3..43245e7a3 100644
--- a/public/script.js
+++ b/public/script.js
@@ -238,7 +238,7 @@ import { getBackgrounds, initBackgrounds, loadBackgroundSettings, background_set
import { hideLoader, showLoader } from './scripts/loader.js';
import { BulkEditOverlay, CharacterContextMenu } from './scripts/BulkEditOverlay.js';
import { loadFeatherlessModels, loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermaticAIModels, loadOpenRouterModels, loadVllmModels, loadAphroditeModels, loadDreamGenModels, initTextGenModels, loadTabbyModels, loadGenericModels } from './scripts/textgen-models.js';
-import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat, PromptReasoning } from './scripts/chats.js';
+import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId, preserveNeutralChat, restoreNeutralChat } from './scripts/chats.js';
import { getPresetManager, initPresetManager } from './scripts/preset-manager.js';
import { evaluateMacros, getLastMessageId, initMacros } from './scripts/macros.js';
import { currentUser, setUserControls } from './scripts/user.js';
@@ -267,6 +267,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';
// API OBJECT FOR EXTERNAL WIRING
globalThis.SillyTavern = {
@@ -982,6 +983,7 @@ async function firstLoadInit() {
initServerHistory();
initSettingsSearch();
initBulkEdit();
+ initReasoning();
await initScrapers();
doDailyExtensionUpdatesCheck();
await hideLoader();
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
index 8d6ec6040..94ad882bb 100644
--- a/public/scripts/chats.js
+++ b/public/scripts/chats.js
@@ -23,9 +23,6 @@ import {
neutralCharacterName,
updateChatMetadata,
system_message_types,
- updateMessageBlock,
- closeMessageEditor,
- substituteParams,
} from '../script.js';
import { selected_group } from './group-chats.js';
import { power_user } from './power-user.js';
@@ -40,7 +37,6 @@ import {
saveBase64AsFile,
extractTextFromOffice,
download,
- copyText,
} from './utils.js';
import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js';
import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
@@ -1420,72 +1416,7 @@ export function registerFileConverter(mimeType, converter) {
converters[mimeType] = converter;
}
-/**
- * Helper class for adding reasoning to messages.
- * Keeps track of the number of reasoning additions.
- */
-export class PromptReasoning {
- static REASONING_PLACEHOLDER = '\u200B';
-
- constructor() {
- this.counter = 0;
- }
-
- /**
- * Checks if the limit of reasoning additions has been reached.
- * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise.
- */
- isLimitReached() {
- if (!power_user.reasoning.add_to_prompts) {
- return true;
- }
-
- return this.counter >= power_user.reasoning.max_additions;
- }
-
- /**
- * Add reasoning to a message according to the power user settings.
- * @param {string} content Message content
- * @param {string} reasoning Message reasoning
- * @returns {string} Message content with reasoning
- */
- addToMessage(content, reasoning) {
- // Disabled or reached limit of additions
- if (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions) {
- return content;
- }
-
- // No reasoning provided or a placeholder
- if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) {
- return content;
- }
-
- // Increment the counter
- this.counter++;
-
- // Substitute macros in variable parts
- const prefix = substituteParams(power_user.reasoning.prefix || '');
- const separator = substituteParams(power_user.reasoning.separator || '');
- const suffix = substituteParams(power_user.reasoning.suffix || '');
-
- // Combine parts with reasoning and content
- return `${prefix}${reasoning}${suffix}${separator}${content}`;
- }
-}
-
jQuery(function () {
- /**
- * Gets a message from a jQuery element.
- * @param {Element} element
- * @returns {{messageId: number, message: object, messageBlock: JQuery}}
- */
- const getMessageFromJquery = function (element) {
- const messageBlock = $(element).closest('.mes');
- const messageId = Number(messageBlock.attr('mesid'));
- const message = chat[messageId];
- return { messageId: messageId, message, messageBlock };
- };
-
$(document).on('click', '.mes_hide', async function () {
const messageBlock = $(this).closest('.mes');
const messageId = Number(messageBlock.attr('mesid'));
@@ -1636,125 +1567,6 @@ jQuery(function () {
$(document).on('click', '.mes_img_enlarge', enlargeMessageImage);
$(document).on('click', '.mes_img_delete', deleteMessageImage);
- $(document).on('click', '.mes_reasoning_copy', (e) => {
- e.stopPropagation();
- e.preventDefault();
- });
-
- $(document).on('click', '.mes_reasoning_edit', function (e) {
- e.stopPropagation();
- e.preventDefault();
- const { message, messageBlock } = getMessageFromJquery(this);
- if (!message?.extra) {
- return;
- }
-
- const reasoning = message?.extra?.reasoning;
- const chatElement = document.getElementById('chat');
- const textarea = document.createElement('textarea');
- const reasoningBlock = messageBlock.find('.mes_reasoning');
- textarea.classList.add('reasoning_edit_textarea');
- textarea.value = reasoning === PromptReasoning.REASONING_PLACEHOLDER ? '' : reasoning;
- $(textarea).insertBefore(reasoningBlock);
-
- if (!CSS.supports('field-sizing', 'content')) {
- const resetHeight = function () {
- const scrollTop = chatElement.scrollTop;
- textarea.style.height = '0px';
- textarea.style.height = `${textarea.scrollHeight}px`;
- chatElement.scrollTop = scrollTop;
- };
-
- textarea.addEventListener('input', resetHeight);
- resetHeight();
- }
-
- textarea.focus();
- textarea.setSelectionRange(textarea.value.length, textarea.value.length);
-
- const textareaRect = textarea.getBoundingClientRect();
- const chatRect = chatElement.getBoundingClientRect();
-
- // Scroll if textarea bottom is below visible area
- if (textareaRect.bottom > chatRect.bottom) {
- const scrollOffset = textareaRect.bottom - chatRect.bottom;
- chatElement.scrollTop += scrollOffset;
- }
- });
-
- $(document).on('click', '.mes_reasoning_edit_done', async function (e) {
- e.stopPropagation();
- e.preventDefault();
- const { message, messageId, messageBlock } = getMessageFromJquery(this);
- if (!message?.extra) {
- return;
- }
-
- const textarea = messageBlock.find('.reasoning_edit_textarea');
- const reasoning = String(textarea.val());
- message.extra.reasoning = reasoning;
- await saveChatConditional();
- updateMessageBlock(messageId, message);
- textarea.remove();
- });
-
- $(document).on('click', '.mes_reasoning_edit_cancel', function (e) {
- e.stopPropagation();
- e.preventDefault();
-
- const { messageBlock } = getMessageFromJquery(this);
- const textarea = messageBlock.find('.reasoning_edit_textarea');
- textarea.remove();
- });
-
- $(document).on('click', '.mes_edit_add_reasoning', async function () {
- const { message, messageId } = getMessageFromJquery(this);
- if (!message?.extra) {
- return;
- }
-
- if (message.extra.reasoning) {
- toastr.info(t`Reasoning already exists.`, t`Edit Message`);
- return;
- }
-
- message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
- await saveChatConditional();
- closeMessageEditor();
- updateMessageBlock(messageId, message);
- });
-
- $(document).on('click', '.mes_reasoning_delete', async function (e) {
- e.stopPropagation();
- e.preventDefault();
-
- const confirm = await Popup.show.confirm(t`Are you sure you want to clear the reasoning?`, t`Visible message contents will stay intact.`);
-
- if (!confirm) {
- return;
- }
-
- const { message, messageId } = getMessageFromJquery(this);
- if (!message?.extra) {
- return;
- }
- message.extra.reasoning = '';
- await saveChatConditional();
- updateMessageBlock(messageId, message);
- });
-
- $(document).on('pointerup', '.mes_reasoning_copy', async function () {
- const { message } = getMessageFromJquery(this);
- const reasoning = message?.extra?.reasoning;
-
- if (!reasoning) {
- return;
- }
-
- await copyText(reasoning);
- toastr.info(t`Copied!`, '', { timeOut: 2000 });
- });
-
$('#file_form_input').on('change', async () => {
const fileInput = document.getElementById('file_form_input');
if (!(fileInput instanceof HTMLInputElement)) return;
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index 4859c0c71..08840c7bc 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -1621,7 +1621,6 @@ async function loadPowerUserSettings(settings, data) {
loadMovingUIState();
loadCharListState();
toggleMDHotkeyIconDisplay();
- loadReasoningSettings();
}
function toggleMDHotkeyIconDisplay() {
@@ -1638,38 +1637,6 @@ function loadCharListState() {
document.body.classList.toggle('charListGrid', power_user.charListGrid);
}
-function loadReasoningSettings() {
- $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts);
- $('#reasoning_add_to_prompts').on('change', function () {
- power_user.reasoning.add_to_prompts = !!$(this).prop('checked');
- saveSettingsDebounced();
- });
-
- $('#reasoning_prefix').val(power_user.reasoning.prefix);
- $('#reasoning_prefix').on('input', function () {
- power_user.reasoning.prefix = String($(this).val());
- saveSettingsDebounced();
- });
-
- $('#reasoning_suffix').val(power_user.reasoning.suffix);
- $('#reasoning_suffix').on('input', function () {
- power_user.reasoning.suffix = String($(this).val());
- saveSettingsDebounced();
- });
-
- $('#reasoning_separator').val(power_user.reasoning.separator);
- $('#reasoning_separator').on('input', function () {
- power_user.reasoning.separator = String($(this).val());
- saveSettingsDebounced();
- });
-
- $('#reasoning_max_additions').val(power_user.reasoning.max_additions);
- $('#reasoning_max_additions').on('input', function () {
- power_user.reasoning.max_additions = Number($(this).val());
- saveSettingsDebounced();
- });
-}
-
function loadMovingUIState() {
if (!isMobile()
&& power_user.movingUIState
diff --git a/public/scripts/reasoning.js b/public/scripts/reasoning.js
new file mode 100644
index 000000000..0d2011b12
--- /dev/null
+++ b/public/scripts/reasoning.js
@@ -0,0 +1,228 @@
+import { chat, closeMessageEditor, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js';
+import { t } from './i18n.js';
+import { Popup } from './popup.js';
+import { power_user } from './power-user.js';
+import { copyText } from './utils.js';
+
+/**
+ * Gets a message from a jQuery element.
+ * @param {Element} element
+ * @returns {{messageId: number, message: object, messageBlock: JQuery}}
+ */
+function getMessageFromJquery(element) {
+ const messageBlock = $(element).closest('.mes');
+ const messageId = Number(messageBlock.attr('mesid'));
+ const message = chat[messageId];
+ return { messageId: messageId, message, messageBlock };
+}
+
+/**
+ * Helper class for adding reasoning to messages.
+ * Keeps track of the number of reasoning additions.
+ */
+export class PromptReasoning {
+ static REASONING_PLACEHOLDER = '\u200B';
+
+ constructor() {
+ this.counter = 0;
+ }
+
+ /**
+ * Checks if the limit of reasoning additions has been reached.
+ * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise.
+ */
+ isLimitReached() {
+ if (!power_user.reasoning.add_to_prompts) {
+ return true;
+ }
+
+ return this.counter >= power_user.reasoning.max_additions;
+ }
+
+ /**
+ * Add reasoning to a message according to the power user settings.
+ * @param {string} content Message content
+ * @param {string} reasoning Message reasoning
+ * @returns {string} Message content with reasoning
+ */
+ addToMessage(content, reasoning) {
+ // Disabled or reached limit of additions
+ if (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions) {
+ return content;
+ }
+
+ // No reasoning provided or a placeholder
+ if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) {
+ return content;
+ }
+
+ // Increment the counter
+ this.counter++;
+
+ // Substitute macros in variable parts
+ const prefix = substituteParams(power_user.reasoning.prefix || '');
+ const separator = substituteParams(power_user.reasoning.separator || '');
+ const suffix = substituteParams(power_user.reasoning.suffix || '');
+
+ // Combine parts with reasoning and content
+ return `${prefix}${reasoning}${suffix}${separator}${content}`;
+ }
+}
+
+function loadReasoningSettings() {
+ $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts);
+ $('#reasoning_add_to_prompts').on('change', function () {
+ power_user.reasoning.add_to_prompts = !!$(this).prop('checked');
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_prefix').val(power_user.reasoning.prefix);
+ $('#reasoning_prefix').on('input', function () {
+ power_user.reasoning.prefix = String($(this).val());
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_suffix').val(power_user.reasoning.suffix);
+ $('#reasoning_suffix').on('input', function () {
+ power_user.reasoning.suffix = String($(this).val());
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_separator').val(power_user.reasoning.separator);
+ $('#reasoning_separator').on('input', function () {
+ power_user.reasoning.separator = String($(this).val());
+ saveSettingsDebounced();
+ });
+
+ $('#reasoning_max_additions').val(power_user.reasoning.max_additions);
+ $('#reasoning_max_additions').on('input', function () {
+ power_user.reasoning.max_additions = Number($(this).val());
+ saveSettingsDebounced();
+ });
+}
+
+function setReasoningEventHandlers(){
+ $(document).on('click', '.mes_reasoning_copy', (e) => {
+ e.stopPropagation();
+ e.preventDefault();
+ });
+
+ $(document).on('click', '.mes_reasoning_edit', function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ const { message, messageBlock } = getMessageFromJquery(this);
+ if (!message?.extra) {
+ return;
+ }
+
+ const reasoning = message?.extra?.reasoning;
+ const chatElement = document.getElementById('chat');
+ const textarea = document.createElement('textarea');
+ const reasoningBlock = messageBlock.find('.mes_reasoning');
+ textarea.classList.add('reasoning_edit_textarea');
+ textarea.value = reasoning === PromptReasoning.REASONING_PLACEHOLDER ? '' : reasoning;
+ $(textarea).insertBefore(reasoningBlock);
+
+ if (!CSS.supports('field-sizing', 'content')) {
+ const resetHeight = function () {
+ const scrollTop = chatElement.scrollTop;
+ textarea.style.height = '0px';
+ textarea.style.height = `${textarea.scrollHeight}px`;
+ chatElement.scrollTop = scrollTop;
+ };
+
+ textarea.addEventListener('input', resetHeight);
+ resetHeight();
+ }
+
+ textarea.focus();
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length);
+
+ const textareaRect = textarea.getBoundingClientRect();
+ const chatRect = chatElement.getBoundingClientRect();
+
+ // Scroll if textarea bottom is below visible area
+ if (textareaRect.bottom > chatRect.bottom) {
+ const scrollOffset = textareaRect.bottom - chatRect.bottom;
+ chatElement.scrollTop += scrollOffset;
+ }
+ });
+
+ $(document).on('click', '.mes_reasoning_edit_done', async function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ const { message, messageId, messageBlock } = getMessageFromJquery(this);
+ if (!message?.extra) {
+ return;
+ }
+
+ const textarea = messageBlock.find('.reasoning_edit_textarea');
+ const reasoning = String(textarea.val());
+ message.extra.reasoning = reasoning;
+ await saveChatConditional();
+ updateMessageBlock(messageId, message);
+ textarea.remove();
+ });
+
+ $(document).on('click', '.mes_reasoning_edit_cancel', function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ const { messageBlock } = getMessageFromJquery(this);
+ const textarea = messageBlock.find('.reasoning_edit_textarea');
+ textarea.remove();
+ });
+
+ $(document).on('click', '.mes_edit_add_reasoning', async function () {
+ const { message, messageId } = getMessageFromJquery(this);
+ if (!message?.extra) {
+ return;
+ }
+
+ if (message.extra.reasoning) {
+ toastr.info(t`Reasoning already exists.`, t`Edit Message`);
+ return;
+ }
+
+ message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER;
+ await saveChatConditional();
+ closeMessageEditor();
+ updateMessageBlock(messageId, message);
+ });
+
+ $(document).on('click', '.mes_reasoning_delete', async function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ const confirm = await Popup.show.confirm(t`Are you sure you want to clear the reasoning?`, t`Visible message contents will stay intact.`);
+
+ if (!confirm) {
+ return;
+ }
+
+ const { message, messageId } = getMessageFromJquery(this);
+ if (!message?.extra) {
+ return;
+ }
+ message.extra.reasoning = '';
+ await saveChatConditional();
+ updateMessageBlock(messageId, message);
+ });
+
+ $(document).on('pointerup', '.mes_reasoning_copy', async function () {
+ const { message } = getMessageFromJquery(this);
+ const reasoning = message?.extra?.reasoning;
+
+ if (!reasoning) {
+ return;
+ }
+
+ await copyText(reasoning);
+ toastr.info(t`Copied!`, '', { timeOut: 2000 });
+ });
+}
+
+export function initReasoning() {
+ loadReasoningSettings();
+ setReasoningEventHandlers();
+}
From c9ab987658d4d961a157d7f235cfd317ad334996 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 26 Jan 2025 16:48:04 +0200
Subject: [PATCH 18/83] Fix default thonk separator
---
public/scripts/power-user.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index 08840c7bc..2a74f8722 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -257,7 +257,7 @@ let power_user = {
add_to_prompts: false,
prefix: '\n',
suffix: '\n',
- separator: '\n',
+ separator: '\n\n',
max_additions: 1,
},
From a7516937f78108f5cfa589a4f5869e8c92ef5e0d Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 26 Jan 2025 18:00:14 +0200
Subject: [PATCH 19/83] Add reasoning slash commands
---
public/scripts/reasoning.js | 68 +++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/public/scripts/reasoning.js b/public/scripts/reasoning.js
index 0d2011b12..90721cd5d 100644
--- a/public/scripts/reasoning.js
+++ b/public/scripts/reasoning.js
@@ -1,7 +1,12 @@
import { chat, closeMessageEditor, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js';
import { t } from './i18n.js';
+import { MacrosParser } from './macros.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 { copyText } from './utils.js';
/**
@@ -101,6 +106,67 @@ function loadReasoningSettings() {
});
}
+function registerReasoningSlashCommands() {
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'reasoning-get',
+ returns: ARGUMENT_TYPE.STRING,
+ helpString: t`Get the contents of a reasoning block of a message. Returns an empty string if the message does not have a reasoning block.`,
+ unnamedArgumentList: [
+ SlashCommandArgument.fromProps({
+ description: 'Message ID. If not provided, the message ID of the last message is used.',
+ typeList: ARGUMENT_TYPE.NUMBER,
+ enumProvider: commonEnumProviders.messages(),
+ }),
+ ],
+ callback: (_args, value) => {
+ const messageId = !isNaN(Number(value)) ? Number(value) : chat.length - 1;
+ const message = chat[messageId];
+ const reasoning = message?.extra?.reasoning;
+ return reasoning !== PromptReasoning.REASONING_PLACEHOLDER ? reasoning : '';
+ },
+ }));
+
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'reasoning-set',
+ returns: ARGUMENT_TYPE.STRING,
+ helpString: t`Set the reasoning block of a message. Returns the reasoning block content.`,
+ namedArgumentList: [
+ SlashCommandNamedArgument.fromProps({
+ name: 'at',
+ description: 'Message ID. If not provided, the message ID of the last message is used.',
+ typeList: ARGUMENT_TYPE.NUMBER,
+ enumProvider: commonEnumProviders.messages(),
+ }),
+ ],
+ unnamedArgumentList: [
+ SlashCommandArgument.fromProps({
+ description: 'Reasoning block content.',
+ typeList: ARGUMENT_TYPE.STRING,
+ }),
+ ],
+ callback: async (args, value) => {
+ const messageId = !isNaN(Number(args[0])) ? Number(args[0]) : chat.length - 1;
+ const message = chat[messageId];
+ if (!message?.extra) {
+ return '';
+ }
+
+ message.extra.reasoning = String(value);
+ await saveChatConditional();
+
+ closeMessageEditor('reasoning');
+ updateMessageBlock(messageId, message);
+ return message.extra.reasoning;
+ },
+ }));
+}
+
+function registerReasoningMacros() {
+ MacrosParser.registerMacro('reasoningPrefix', () => power_user.reasoning.prefix, t`Reasoning Prefix`);
+ MacrosParser.registerMacro('reasoningSuffix', () => power_user.reasoning.suffix, t`Reasoning Suffix`);
+ MacrosParser.registerMacro('reasoningSeparator', () => power_user.reasoning.separator, t`Reasoning Separator`);
+}
+
function setReasoningEventHandlers(){
$(document).on('click', '.mes_reasoning_copy', (e) => {
e.stopPropagation();
@@ -225,4 +291,6 @@ function setReasoningEventHandlers(){
export function initReasoning() {
loadReasoningSettings();
setReasoningEventHandlers();
+ registerReasoningSlashCommands();
+ registerReasoningMacros();
}
From bfedf20db52c1e91ab5e141e12a93d2c220b982d Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sun, 26 Jan 2025 18:29:31 +0200
Subject: [PATCH 20/83] Add reasoning tokens to token count.
---
public/script.js | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/public/script.js b/public/script.js
index 43245e7a3..38a4d562f 100644
--- a/public/script.js
+++ b/public/script.js
@@ -3195,7 +3195,8 @@ class StreamingProcessor {
this.#updateMessageBlockVisibility();
const currentTime = new Date();
// Don't waste time calculating token count for streaming
- const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(processedText, 0) : 0;
+ const tokenCountText = (this.reasoning || '') + processedText;
+ const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(tokenCountText, 0) : 0;
const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount);
chat[messageId]['mes'] = processedText;
chat[messageId]['gen_started'] = this.timeStarted;
@@ -5936,7 +5937,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
chat[chat.length - 1]['extra']['reasoning'] = reasoning;
if (power_user.message_token_count_enabled) {
- chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);
+ const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
+ chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
}
const chat_id = (chat.length - 1);
await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);
@@ -5957,7 +5959,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
chat[chat.length - 1]['extra']['reasoning'] += reasoning;
if (power_user.message_token_count_enabled) {
- chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);
+ const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
+ chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
}
const chat_id = (chat.length - 1);
await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);
@@ -5975,7 +5978,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
chat[chat.length - 1]['extra']['reasoning'] += reasoning;
if (power_user.message_token_count_enabled) {
- chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);
+ const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
+ chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
}
const chat_id = (chat.length - 1);
await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id);
@@ -6001,7 +6005,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
chat[chat.length - 1]['gen_finished'] = generationFinished;
if (power_user.message_token_count_enabled) {
- chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0);
+ const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
+ chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
}
if (selected_group) {
@@ -8544,7 +8549,8 @@ function swipe_left() { // when we swipe left..but no generation.
}
const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`);
- const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0);
+ const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes;
+ const tokenCount = await getTokenCountAsync(tokenCountText, 0);
chat[chat.length - 1]['extra']['token_count'] = tokenCount;
swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);
}
@@ -8719,7 +8725,8 @@ const swipe_right = () => {
chat[chat.length - 1].extra = {};
}
- const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0);
+ const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes;
+ const tokenCount = await getTokenCountAsync(tokenCountText, 0);
chat[chat.length - 1]['extra']['token_count'] = tokenCount;
swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);
}
@@ -9521,7 +9528,8 @@ function addDebugFunctions() {
message.extra = {};
}
- message.extra.token_count = await getTokenCountAsync(message.mes, 0);
+ const tokenCountText = (message?.extra?.reasoning || '') + message.mes;
+ message.extra.token_count = await getTokenCountAsync(tokenCountText, 0);
}
await saveChatConditional();
From fd1fdc646665d733bd2179d9439391c3c26ce84d Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 27 Jan 2025 17:48:50 +0000
Subject: [PATCH 21/83] Fix fitting class resetting after picking BG
---
public/scripts/backgrounds.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js
index f83d4e044..683e9c4ee 100644
--- a/public/scripts/backgrounds.js
+++ b/public/scripts/backgrounds.js
@@ -482,10 +482,10 @@ function highlightNewBackground(bg) {
*/
function setFittingClass(fitting) {
const backgrounds = $('#bg1, #bg_custom');
- backgrounds.toggleClass('cover', fitting === 'cover');
- backgrounds.toggleClass('contain', fitting === 'contain');
- backgrounds.toggleClass('stretch', fitting === 'stretch');
- backgrounds.toggleClass('center', fitting === 'center');
+ for (const option of ['cover', 'contain', 'stretch', 'center']) {
+ backgrounds.toggleClass(option, option === fitting);
+ }
+ background_settings.fitting = fitting;
}
function onBackgroundFilterInput() {
From a03193b2f7ee553cffdaf68d61f115d3d422518c Mon Sep 17 00:00:00 2001
From: Dan Helfman
Date: Mon, 27 Jan 2025 11:08:08 -0800
Subject: [PATCH 22/83] Change docker create to run so it actually runs the
container.
---
.github/readme.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/readme.md b/.github/readme.md
index 28d3a390d..01a3ab8ba 100644
--- a/.github/readme.md
+++ b/.github/readme.md
@@ -274,7 +274,7 @@ You will need two mandatory directory mappings and a port mapping to allow Silly
1. Open your Command Line
2. Run the following command
-`docker create --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' -v '[extensions]':'/home/node/app/public/scripts/extensions/third-party':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
+`docker run --name='sillytavern' --net='[DockerNet]' -p '8000:8000/tcp' -v '[plugins]':'/home/node/app/plugins':'rw' -v '[config]':'/home/node/app/config':'rw' -v '[data]':'/home/node/app/data':'rw' -v '[extensions]':'/home/node/app/public/scripts/extensions/third-party':'rw' 'ghcr.io/sillytavern/sillytavern:[version]'`
> Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config.
From 8b5e0df2d70b80d265a208cc021537c967e5a4d8 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 27 Jan 2025 21:56:15 +0200
Subject: [PATCH 23/83] Refactor reasoning placeholder clean-up
---
public/script.js | 11 +++++++----
public/scripts/reasoning.js | 13 +++++++------
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/public/script.js b/public/script.js
index 38a4d562f..006f946d7 100644
--- a/public/script.js
+++ b/public/script.js
@@ -3194,10 +3194,6 @@ class StreamingProcessor {
this.#checkDomElements(messageId);
this.#updateMessageBlockVisibility();
const currentTime = new Date();
- // Don't waste time calculating token count for streaming
- const tokenCountText = (this.reasoning || '') + processedText;
- const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(tokenCountText, 0) : 0;
- const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount);
chat[messageId]['mes'] = processedText;
chat[messageId]['gen_started'] = this.timeStarted;
chat[messageId]['gen_finished'] = currentTime;
@@ -3214,6 +3210,10 @@ class StreamingProcessor {
}
}
+ // Don't waste time calculating token count for streaming
+ const tokenCountText = (this.reasoning || '') + processedText;
+ const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(tokenCountText, 0) : 0;
+
if (currentTokenCount) {
chat[messageId]['extra']['token_count'] = currentTokenCount;
if (this.messageTokenCounterDom instanceof HTMLElement) {
@@ -3236,10 +3236,13 @@ class StreamingProcessor {
if (this.messageTextDom instanceof HTMLElement) {
this.messageTextDom.innerHTML = formattedText;
}
+
+ const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount);
if (this.messageTimerDom instanceof HTMLElement) {
this.messageTimerDom.textContent = timePassed.timerValue;
this.messageTimerDom.title = timePassed.timerTitle;
}
+
this.setFirstSwipe(messageId);
}
diff --git a/public/scripts/reasoning.js b/public/scripts/reasoning.js
index 90721cd5d..c9b0efc3b 100644
--- a/public/scripts/reasoning.js
+++ b/public/scripts/reasoning.js
@@ -27,6 +27,7 @@ function getMessageFromJquery(element) {
*/
export class PromptReasoning {
static REASONING_PLACEHOLDER = '\u200B';
+ static REASONING_PLACEHOLDER_REGEX = new RegExp(`${PromptReasoning.REASONING_PLACEHOLDER}$`);
constructor() {
this.counter = 0;
@@ -121,8 +122,8 @@ function registerReasoningSlashCommands() {
callback: (_args, value) => {
const messageId = !isNaN(Number(value)) ? Number(value) : chat.length - 1;
const message = chat[messageId];
- const reasoning = message?.extra?.reasoning;
- return reasoning !== PromptReasoning.REASONING_PLACEHOLDER ? reasoning : '';
+ const reasoning = String(message?.extra?.reasoning ?? '');
+ return reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, '');
},
}));
@@ -151,7 +152,7 @@ function registerReasoningSlashCommands() {
return '';
}
- message.extra.reasoning = String(value);
+ message.extra.reasoning = String(value ?? '');
await saveChatConditional();
closeMessageEditor('reasoning');
@@ -181,12 +182,12 @@ function setReasoningEventHandlers(){
return;
}
- const reasoning = message?.extra?.reasoning;
+ const reasoning = String(message?.extra?.reasoning ?? '');
const chatElement = document.getElementById('chat');
const textarea = document.createElement('textarea');
const reasoningBlock = messageBlock.find('.mes_reasoning');
textarea.classList.add('reasoning_edit_textarea');
- textarea.value = reasoning === PromptReasoning.REASONING_PLACEHOLDER ? '' : reasoning;
+ textarea.value = reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, '');
$(textarea).insertBefore(reasoningBlock);
if (!CSS.supports('field-sizing', 'content')) {
@@ -277,7 +278,7 @@ function setReasoningEventHandlers(){
$(document).on('pointerup', '.mes_reasoning_copy', async function () {
const { message } = getMessageFromJquery(this);
- const reasoning = message?.extra?.reasoning;
+ const reasoning = String(message?.extra?.reasoning ?? '').replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, '');
if (!reasoning) {
return;
From abe240397d35d220820beac19d99d60580adb2ca Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 27 Jan 2025 22:01:44 +0200
Subject: [PATCH 24/83] Set assistant role to bias in CC #3366
---
public/scripts/openai.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/scripts/openai.js b/public/scripts/openai.js
index d715377fb..2d0f44458 100644
--- a/public/scripts/openai.js
+++ b/public/scripts/openai.js
@@ -1096,8 +1096,8 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name
// Unordered prompts without marker
{ role: 'system', content: impersonationPrompt, identifier: 'impersonate' },
{ role: 'system', content: quietPrompt, identifier: 'quietPrompt' },
- { role: 'system', content: bias, identifier: 'bias' },
{ role: 'system', content: groupNudge, identifier: 'groupNudge' },
+ { role: 'assistant', content: bias, identifier: 'bias' },
];
// Tavern Extras - Summary
From fad4e4e75e074dd99bca3d5adc63b946b3aa7023 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 27 Jan 2025 22:30:35 +0200
Subject: [PATCH 25/83] Add command and profile for custom stop strings
---
.../extensions/connection-manager/index.js | 4 +++
public/scripts/power-user.js | 30 +++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/public/scripts/extensions/connection-manager/index.js b/public/scripts/extensions/connection-manager/index.js
index e9d0dd2fe..f31e4b495 100644
--- a/public/scripts/extensions/connection-manager/index.js
+++ b/public/scripts/extensions/connection-manager/index.js
@@ -30,6 +30,7 @@ const CC_COMMANDS = [
'api-url',
'model',
'proxy',
+ 'stop-strings',
];
const TC_COMMANDS = [
@@ -43,6 +44,7 @@ const TC_COMMANDS = [
'context',
'instruct-state',
'tokenizer',
+ 'stop-strings',
];
const FANCY_NAMES = {
@@ -57,6 +59,7 @@ const FANCY_NAMES = {
'instruct': 'Instruct Template',
'context': 'Context Template',
'tokenizer': 'Tokenizer',
+ 'stop-strings': 'Custom Stopping Strings',
};
/**
@@ -138,6 +141,7 @@ const profilesProvider = () => [
* @property {string} [context] Context Template
* @property {string} [instruct-state] Instruct Mode
* @property {string} [tokenizer] Tokenizer
+ * @property {string} [stop-strings] Custom Stopping Strings
* @property {string[]} [exclude] Commands to exclude
*/
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index 2a74f8722..2c1d5cd4c 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -4072,4 +4072,34 @@ $(document).ready(() => {
],
helpString: 'activates a movingUI preset by name',
}));
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'stop-strings',
+ aliases: ['stopping-strings'],
+ helpString: 'Sets a list of custom stopping strings. Gets the list if no value is provided.',
+ returns: ARGUMENT_TYPE.LIST,
+ unnamedArgumentList: [
+ SlashCommandArgument.fromProps({
+ description: 'list of strings',
+ typeList: [ARGUMENT_TYPE.LIST],
+ acceptsMultiple: false,
+ isRequired: false,
+ }),
+ ],
+ callback: (_, value) => {
+ if (String(value ?? '').trim()) {
+ const parsedValue = ((x) => { try { return JSON.parse(x.toString()); } catch { return null; } })(value);
+ if (!parsedValue || !Array.isArray(parsedValue)) {
+ throw new Error('Invalid list format. The value must be a JSON-serialized array of strings.');
+ }
+ parsedValue.forEach((item, index) => {
+ parsedValue[index] = String(item);
+ });
+ power_user.custom_stopping_strings = JSON.stringify(parsedValue);
+ $('#custom_stopping_strings').val(power_user.custom_stopping_strings);
+ saveSettingsDebounced();
+ }
+
+ return power_user.custom_stopping_strings;
+ },
+ }));
});
From 6fc342d446f8bf6368852c0a2528c901a6e0a1b7 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Mon, 27 Jan 2025 23:06:07 +0200
Subject: [PATCH 26/83] Add regex processing for reasoning blocks
---
public/script.js | 59 +++++++++++++++------
public/scripts/extensions/regex/editor.html | 6 +++
public/scripts/extensions/regex/engine.js | 1 +
3 files changed, 49 insertions(+), 17 deletions(-)
diff --git a/public/script.js b/public/script.js
index 006f946d7..09d7a921b 100644
--- a/public/script.js
+++ b/public/script.js
@@ -1993,9 +1993,10 @@ export async function sendTextareaMessage() {
* @param {boolean} isUser If the message was sent by the user
* @param {number} messageId Message index in chat array
* @param {object} [sanitizerOverrides] DOMPurify sanitizer option overrides
+ * @param {boolean} [isReasoning] If the message is reasoning output
* @returns {string} HTML string
*/
-export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}) {
+export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, sanitizerOverrides = {}, isReasoning = false) {
if (!mes) {
return '';
}
@@ -2029,6 +2030,9 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san
if (!isSystem) {
function getRegexPlacement() {
try {
+ if (isReasoning) {
+ return regex_placement.REASONING;
+ }
if (isUser) {
return regex_placement.USER_INPUT;
} else if (chat[messageId]?.extra?.type === 'narrator') {
@@ -2250,8 +2254,8 @@ function getMessageFromTemplate({
export function updateMessageBlock(messageId, message) {
const messageElement = $(`#chat [mesid="${messageId}"]`);
const text = message?.extra?.display_text ?? message.mes;
- messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId));
- messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, -1));
+ 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));
addCopyToCodeBlocks(messageElement);
appendMediaToMessage(message, messageElement);
}
@@ -2408,9 +2412,10 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
mes.is_user,
chat.indexOf(mes),
sanitizerOverrides,
+ false,
);
- const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1);
- const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, -1);
+ const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1, {}, false);
+ const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, chat.indexOf(mes), {}, true);
let bookmarkLink = mes?.extra?.bookmark_link ?? '';
let params = {
@@ -3205,7 +3210,7 @@ class StreamingProcessor {
if (this.reasoning) {
chat[messageId]['extra']['reasoning'] = this.reasoning;
if (this.messageReasoningDom instanceof HTMLElement) {
- const formattedReasoning = messageFormatting(this.reasoning, '', false, false, -1);
+ const formattedReasoning = messageFormatting(this.reasoning, '', false, false, messageId, {}, true);
this.messageReasoningDom.innerHTML = formattedReasoning;
}
}
@@ -3232,6 +3237,8 @@ class StreamingProcessor {
chat[messageId].is_system,
chat[messageId].is_user,
messageId,
+ {},
+ false,
);
if (this.messageTextDom instanceof HTMLElement) {
this.messageTextDom.innerHTML = formattedText;
@@ -3383,7 +3390,7 @@ class StreamingProcessor {
if (logprobs) {
this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs]));
}
- this.reasoning = state?.reasoning ?? '';
+ this.reasoning = getRegexedString(state?.reasoning ?? '', regex_placement.REASONING);
await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text);
await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text));
}
@@ -3850,14 +3857,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
coreChat.pop();
}
- const reasoning = new PromptReasoning();
- for (let i = coreChat.length - 1; i >= 0; i--) {
- if (reasoning.isLimitReached()) {
- break;
- }
- coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) };
- }
-
coreChat = await Promise.all(coreChat.map(async (chatItem, index) => {
let message = chatItem.mes;
let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT;
@@ -3877,6 +3876,25 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
};
}));
+ const reasoning = new PromptReasoning();
+ for (let i = coreChat.length - 1; i >= 0; i--) {
+ if (reasoning.isLimitReached()) {
+ break;
+ }
+ const depth = coreChat.length - i - 1;
+ coreChat[i] = {
+ mes: reasoning.addToMessage(
+ coreChat[i].mes,
+ getRegexedString(
+ coreChat[i].extra?.reasoning,
+ regex_placement.REASONING,
+ { isPrompt: true, depth: depth },
+ ),
+ ),
+ ...coreChat[i],
+ };
+ }
+
// Determine token limit
let this_max_context = getMaxContextSize();
@@ -4785,6 +4803,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
const swipes = extractMultiSwipes(data, type);
messageChunk = cleanUpMessage(getMessage, isImpersonate, isContinue, false);
+ reasoning = getRegexedString(reasoning, regex_placement.REASONING);
if (isContinue) {
getMessage = continue_mag + getMessage;
@@ -7177,9 +7196,11 @@ function messageEditAuto(div) {
mes.is_system,
mes.is_user,
this_edit_mes_id,
+ {},
+ false,
));
mesBlock.find('.mes_bias').empty();
- mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1));
+ mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false));
saveChatDebounced();
}
@@ -7201,10 +7222,12 @@ async function messageEditDone(div) {
mes.is_system,
mes.is_user,
this_edit_mes_id,
+ {},
+ false,
),
);
mesBlock.find('.mes_bias').empty();
- mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1));
+ mesBlock.find('.mes_bias').append(messageFormatting(bias, '', false, false, -1, {}, false));
appendMediaToMessage(mes, div.closest('.mes'));
addCopyToCodeBlocks(div.closest('.mes'));
@@ -10841,6 +10864,8 @@ jQuery(async function () {
chat[this_edit_mes_id].is_system,
chat[this_edit_mes_id].is_user,
this_edit_mes_id,
+ {},
+ false,
));
appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes'));
addCopyToCodeBlocks($(this).closest('.mes'));
diff --git a/public/scripts/extensions/regex/editor.html b/public/scripts/extensions/regex/editor.html
index 9e699a622..8cfe9e008 100644
--- a/public/scripts/extensions/regex/editor.html
+++ b/public/scripts/extensions/regex/editor.html
@@ -94,6 +94,12 @@
World Info
+