Merge branch 'staging' into persona-improvements
| @@ -274,7 +274,7 @@ You will need two mandatory directory mappings and a port mapping to allow Silly | ||
| 274 | 274 | 1. Open your Command Line |
| 275 | 275 | 2. Run the following command |
| 276 | 276 | |
| 277 | 277 | `docker createrun --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]'` |
| 278 | 278 | |
| 279 | 279 | > Note that 8000 is a default listening port. Don't forget to use an appropriate port if you change it in the config. |
| 280 | 280 | |
| @@ -1977,11 +1977,11 @@ | ||
| 1977 | 1977 | </span> |
| 1978 | 1978 | </div> |
| 1979 | 1979 | </div> |
| 1980 | 1980 | <div class="range-block" data-source="makersuite,deepseek,openrouter"> |
| 1981 | 1981 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> |
| 1982 | 1982 | <input id="openai_show_thoughts" type="checkbox" /> |
| 1983 | 1983 | <span> |
| 1984 | 1984 | <span data-i18n="Show model thoughtsreasoning">Show model thoughtsreasoning</span> |
| 1985 | 1985 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i> |
| 1986 | 1986 | </span> |
| 1987 | 1987 | </label> |
| @@ -3800,6 +3800,39 @@ | ||
| 3800 | 3800 | </div> |
| 3801 | 3801 | </div> |
| 3802 | 3802 | <div> |
| 3803 | + <h4 class="standoutHeader"> | |
| 3804 | + <span data-i18n="Reasoning">Reasoning</span> | |
| 3805 | + </h4> | |
| 3806 | + <div> | |
| 3807 | + <label class="checkbox_label" for="reasoning_add_to_prompts" title="Add existing reasoning blocks to prompts. To add a new reasoning block, use the message edit menu." data-i18n="[title]reasoning_add_to_prompts"> | |
| 3808 | + <input id="reasoning_add_to_prompts" type="checkbox" /> | |
| 3809 | + <small data-i18n="Add Reasoning to Prompts"> | |
| 3810 | + Add Reasoning to Prompts | |
| 3811 | + </small> | |
| 3812 | + </label> | |
| 3813 | + <div class="flex-container"> | |
| 3814 | + <div class="flex1" title="Inserted before the reasoning content." data-i18n="[title]reasoning_prefix"> | |
| 3815 | + <small data-i18n="Prefix">Prefix</small> | |
| 3816 | + <textarea id="reasoning_prefix" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3817 | + </div> | |
| 3818 | + <div class="flex1" title="Inserted after the reasoning content." data-i18n="[title]reasoning_suffix"> | |
| 3819 | + <small data-i18n="Suffix">Suffix</small> | |
| 3820 | + <textarea id="reasoning_suffix" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3821 | + </div> | |
| 3822 | + </div> | |
| 3823 | + <div class="flex-container"> | |
| 3824 | + <div class="flex1" title="Inserted between the reasoning and the message content." data-i18n="[title]reasoning_separator"> | |
| 3825 | + <small data-i18n="Separator">Separator</small> | |
| 3826 | + <textarea id="reasoning_separator" class="text_pole textarea_compact autoSetHeight"></textarea> | |
| 3827 | + </div> | |
| 3828 | + <div class="flex1" title="Maximum number of reasoning blocks to be added per prompt, counting from the last message." data-i18n="[title]reasoning_max_additions"> | |
| 3829 | + <small data-i18n="Max Additions">Max Additions</small> | |
| 3830 | + <input id="reasoning_max_additions" class="text_pole textarea_compact" type="number" min="0" max="999"></textarea> | |
| 3831 | + </div> | |
| 3832 | + </div> | |
| 3833 | + </div> | |
| 3834 | + </div> | |
| 3835 | + <div> | |
| 3803 | 3836 | <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4> |
| 3804 | 3837 | <div> |
| 3805 | 3838 | <small> |
| @@ -6265,14 +6298,26 @@ | ||
| 6265 | 6298 | <div class="mes_edit_buttons"> |
| 6266 | 6299 | <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div> |
| 6267 | 6300 | <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div> |
| 6268 | 6301 | <div class="mes_edit_deletemes_edit_add_reasoning menu_button fa-solid fa-trash-canlightbulb" title="DeleteAdd thisa messagereasoning block" data-i18n="[title]DeleteAdd thisa messagereasoning block"></div> |
| 6269 | - </div> | |
| 6302 | + <div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message"></div> | |
| 6270 | 6303 | <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div> |
| 6271 | 6304 | <div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down"></div> |
| 6272 | - </div> | |
| 6273 | 6305 | <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div> |
| 6274 | 6306 | </div> |
| 6275 | 6307 | </div> |
| 6308 | + <details class="mes_reasoning_details"> | |
| 6309 | + <summary class="mes_reasoning_summary"> | |
| 6310 | + <span data-i18n="Reasoning">Reasoning</span> | |
| 6311 | + <div class="mes_reasoning_actions"> | |
| 6312 | + <div class="mes_reasoning_edit_done mes_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div> | |
| 6313 | + <div class="mes_reasoning_edit_cancel mes_button fa-solid fa-xmark" title="Cancel edit" data-i18n="[title]Cancel edit"></div> | |
| 6314 | + <div class="mes_reasoning_edit mes_button fa-solid fa-pencil" title="Edit reasoning" data-i18n="[title]Edit reasoning"></div> | |
| 6315 | + <div class="mes_reasoning_copy mes_button fa-solid fa-copy" title="Copy reasoning" data-i18n="[title]Copy reasoning"></div> | |
| 6316 | + <div class="mes_reasoning_delete mes_button fa-solid fa-trash-can" title="Remove reasoning" data-i18n="[title]Remove reasoning"></div> | |
| 6317 | + </div> | |
| 6318 | + </summary> | |
| 6319 | + <div class="mes_reasoning"></div> | |
| 6320 | + </details> | |
| 6276 | 6321 | <div class="mes_text"></div> |
| 6277 | 6322 | <div class="mes_img_container"> |
| 6278 | 6323 | <div class="mes_img_controls"> |
| @@ -1385,7 +1385,7 @@ | ||
| 1385 | 1385 | "enable_functions_desc_1": "Autorise l'utilisation", |
| 1386 | 1386 | "enable_functions_desc_2": "outils de fonction", |
| 1387 | 1387 | "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.", |
| 1388 | 1388 | "Show model thoughtsreasoning": "Afficher les pensées du modèle", |
| 1389 | 1389 | "Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.", |
| 1390 | 1390 | "Confirm token parsing with": "Confirmer l'analyse des tokens avec", |
| 1391 | 1391 | "openai_logit_bias_no_items": "Aucun élément", |
| @@ -266,7 +266,7 @@ | ||
| 266 | 266 | "Use system prompt": "使用系统提示词", |
| 267 | 267 | "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过", |
| 268 | 268 | "Merges_all_system_messages_desc_2": "字段发送。", |
| 269 | 269 | "Show model thoughtsreasoning": "展示思维链", |
| 270 | 270 | "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。", |
| 271 | 271 | "Assistant Prefill": "AI预填", |
| 272 | 272 | "Expand the editor": "展开编辑器", |
| @@ -2357,7 +2357,7 @@ | ||
| 2357 | 2357 | "Forbid": "禁止", |
| 2358 | 2358 | "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。", |
| 2359 | 2359 | "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。", |
| 2360 | 2360 | "Show model thoughtsreasoning": "顯示模型思維鏈", |
| 2361 | 2361 | "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。", |
| 2362 | 2362 | "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]", |
| 2363 | 2363 | "Model ID (optional)": "模型 ID(可選)", |
| @@ -271,6 +271,7 @@ import { initSettingsSearch } from './scripts/setting-search.js'; | ||
| 271 | 271 | import { initBulkEdit } from './scripts/bulk-edit.js'; |
| 272 | 272 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; |
| 273 | 273 | import { getContext } from './scripts/st-context.js'; |
| 274 | +import { initReasoning, PromptReasoning } from './scripts/reasoning.js'; | |
| 274 | 275 | |
| 275 | 276 | // API OBJECT FOR EXTERNAL WIRING |
| 276 | 277 | globalThis.SillyTavern = { |
| @@ -986,6 +987,7 @@ async function firstLoadInit() { | ||
| 986 | 987 | initServerHistory(); |
| 987 | 988 | initSettingsSearch(); |
| 988 | 989 | initBulkEdit(); |
| 990 | + initReasoning(); | |
| 989 | 991 | await initScrapers(); |
| 990 | 992 | doDailyExtensionUpdatesCheck(); |
| 991 | 993 | await hideLoader(); |
| @@ -2213,6 +2215,7 @@ function getMessageFromTemplate({ | ||
| 2213 | 2215 | isUser, |
| 2214 | 2216 | avatarImg, |
| 2215 | 2217 | bias, |
| 2218 | + reasoning, | |
| 2216 | 2219 | isSystem, |
| 2217 | 2220 | title, |
| 2218 | 2221 | timerValue, |
| @@ -2237,6 +2240,7 @@ function getMessageFromTemplate({ | ||
| 2237 | 2240 | mes.find('.avatar img').attr('src', avatarImg); |
| 2238 | 2241 | mes.find('.ch_name .name_text').text(characterName); |
| 2239 | 2242 | mes.find('.mes_bias').html(bias); |
| 2243 | + mes.find('.mes_reasoning').html(reasoning); | |
| 2240 | 2244 | mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`); |
| 2241 | 2245 | mes.find('.mesIDDisplay').text(`#${mesId}`); |
| 2242 | 2246 | tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| @@ -2251,10 +2255,16 @@ function getMessageFromTemplate({ | ||
| 2251 | 2255 | return mes; |
| 2252 | 2256 | } |
| 2253 | 2257 | |
| 2258 | +/** | |
| 2259 | + * Re-renders a message block with updated content. | |
| 2260 | + * @param {number} messageId Message ID | |
| 2261 | + * @param {object} message Message object | |
| 2262 | + */ | |
| 2254 | 2263 | export function updateMessageBlock(messageId, message) { |
| 2255 | 2264 | const messageElement = $(`#chat [mesid="${messageId}"]`); |
| 2256 | 2265 | const text = message?.extra?.display_text ?? message.mes; |
| 2257 | 2266 | messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId)); |
| 2267 | + messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, -1)); | |
| 2258 | 2268 | addCopyToCodeBlocks(messageElement); |
| 2259 | 2269 | appendMediaToMessage(message, messageElement); |
| 2260 | 2270 | } |
| @@ -2413,6 +2423,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2413 | 2423 | sanitizerOverrides, |
| 2414 | 2424 | ); |
| 2415 | 2425 | const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1); |
| 2426 | + const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, -1); | |
| 2416 | 2427 | let bookmarkLink = mes?.extra?.bookmark_link ?? ''; |
| 2417 | 2428 | |
| 2418 | 2429 | let params = { |
| @@ -2422,6 +2433,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2422 | 2433 | isUser: mes.is_user, |
| 2423 | 2434 | avatarImg: avatarImg, |
| 2424 | 2435 | bias: bias, |
| 2436 | + reasoning: reasoning, | |
| 2425 | 2437 | isSystem: isSystem, |
| 2426 | 2438 | title: title, |
| 2427 | 2439 | bookmarkLink: bookmarkLink, |
| @@ -2481,6 +2493,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2481 | 2493 | const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`); |
| 2482 | 2494 | swipeMessage.attr('swipeid', params.swipeId); |
| 2483 | 2495 | swipeMessage.find('.mes_text').html(messageText).attr('title', title); |
| 2496 | + swipeMessage.find('.mes_reasoning').html(reasoning); | |
| 2484 | 2497 | swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`); |
| 2485 | 2498 | appendMediaToMessage(mes, swipeMessage); |
| 2486 | 2499 | if (power_user.timestamp_model_icon && params.extra?.api) { |
| @@ -3091,6 +3104,7 @@ class StreamingProcessor { | ||
| 3091 | 3104 | this.messageTextDom = null; |
| 3092 | 3105 | this.messageTimerDom = null; |
| 3093 | 3106 | this.messageTokenCounterDom = null; |
| 3107 | + this.messageReasoningDom = null; | |
| 3094 | 3108 | /** @type {HTMLTextAreaElement} */ |
| 3095 | 3109 | this.sendTextarea = document.querySelector('#send_textarea'); |
| 3096 | 3110 | this.type = type; |
| @@ -3106,6 +3120,7 @@ class StreamingProcessor { | ||
| 3106 | 3120 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ |
| 3107 | 3121 | this.messageLogprobs = []; |
| 3108 | 3122 | this.toolCalls = []; |
| 3123 | + this.reasoning = ''; | |
| 3109 | 3124 | } |
| 3110 | 3125 | |
| 3111 | 3126 | #checkDomElements(messageId) { |
| @@ -3114,6 +3129,7 @@ class StreamingProcessor { | ||
| 3114 | 3129 | this.messageTextDom = this.messageDom?.querySelector('.mes_text'); |
| 3115 | 3130 | this.messageTimerDom = this.messageDom?.querySelector('.mes_timer'); |
| 3116 | 3131 | this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay'); |
| 3132 | + this.messageReasoningDom = this.messageDom?.querySelector('.mes_reasoning'); | |
| 3117 | 3133 | } |
| 3118 | 3134 | } |
| 3119 | 3135 | |
| @@ -3191,18 +3207,27 @@ class StreamingProcessor { | ||
| 3191 | 3207 | this.#checkDomElements(messageId); |
| 3192 | 3208 | this.#updateMessageBlockVisibility(); |
| 3193 | 3209 | const currentTime = new Date(); |
| 3194 | - // Don't waste time calculating token count for streaming | |
| 3195 | - const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(processedText, 0) : 0; | |
| 3196 | - const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount); | |
| 3197 | 3210 | chat[messageId]['mes'] = processedText; |
| 3198 | 3211 | chat[messageId]['gen_started'] = this.timeStarted; |
| 3199 | 3212 | chat[messageId]['gen_finished'] = currentTime; |
| 3200 | 3213 | |
| 3201 | - if (currentTokenCount) { | |
| 3202 | 3214 | if (!chat[messageId]['extra']) { |
| 3203 | 3215 | chat[messageId]['extra'] = {}; |
| 3204 | 3216 | } |
| 3205 | 3217 | |
| 3218 | + if (this.reasoning) { | |
| 3219 | + chat[messageId]['extra']['reasoning'] = this.reasoning; | |
| 3220 | + if (this.messageReasoningDom instanceof HTMLElement) { | |
| 3221 | + const formattedReasoning = messageFormatting(this.reasoning, '', false, false, -1); | |
| 3222 | + this.messageReasoningDom.innerHTML = formattedReasoning; | |
| 3223 | + } | |
| 3224 | + } | |
| 3225 | + | |
| 3226 | + // Don't waste time calculating token count for streaming | |
| 3227 | + const tokenCountText = (this.reasoning || '') + processedText; | |
| 3228 | + const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(tokenCountText, 0) : 0; | |
| 3229 | + | |
| 3230 | + if (currentTokenCount) { | |
| 3206 | 3231 | chat[messageId]['extra']['token_count'] = currentTokenCount; |
| 3207 | 3232 | if (this.messageTokenCounterDom instanceof HTMLElement) { |
| 3208 | 3233 | this.messageTokenCounterDom.textContent = `${currentTokenCount}t`; |
| @@ -3224,10 +3249,13 @@ class StreamingProcessor { | ||
| 3224 | 3249 | if (this.messageTextDom instanceof HTMLElement) { |
| 3225 | 3250 | this.messageTextDom.innerHTML = formattedText; |
| 3226 | 3251 | } |
| 3252 | + | |
| 3253 | + const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount); | |
| 3227 | 3254 | if (this.messageTimerDom instanceof HTMLElement) { |
| 3228 | 3255 | this.messageTimerDom.textContent = timePassed.timerValue; |
| 3229 | 3256 | this.messageTimerDom.title = timePassed.timerTitle; |
| 3230 | 3257 | } |
| 3258 | + | |
| 3231 | 3259 | this.setFirstSwipe(messageId); |
| 3232 | 3260 | } |
| 3233 | 3261 | |
| @@ -3334,7 +3362,7 @@ class StreamingProcessor { | ||
| 3334 | 3362 | } |
| 3335 | 3363 | |
| 3336 | 3364 | /** |
| 3337 | 3365 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>} |
| 3338 | 3366 | */ |
| 3339 | 3367 | *nullStreamingGeneration() { |
| 3340 | 3368 | throw new Error('Generation function for streaming is not hooked up'); |
| @@ -3356,7 +3384,7 @@ class StreamingProcessor { | ||
| 3356 | 3384 | try { |
| 3357 | 3385 | const sw = new Stopwatch(1000 / power_user.streaming_fps); |
| 3358 | 3386 | const timestamps = []; |
| 3359 | 3387 | for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) { |
| 3360 | 3388 | timestamps.push(Date.now()); |
| 3361 | 3389 | if (this.isStopped) { |
| 3362 | 3390 | return; |
| @@ -3368,6 +3396,7 @@ class StreamingProcessor { | ||
| 3368 | 3396 | if (logprobs) { |
| 3369 | 3397 | this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs])); |
| 3370 | 3398 | } |
| 3399 | + this.reasoning = state?.reasoning ?? ''; | |
| 3371 | 3400 | await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text); |
| 3372 | 3401 | await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text)); |
| 3373 | 3402 | } |
| @@ -3834,6 +3863,14 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 3834 | 3863 | coreChat.pop(); |
| 3835 | 3864 | } |
| 3836 | 3865 | |
| 3866 | + const reasoning = new PromptReasoning(); | |
| 3867 | + for (let i = coreChat.length - 1; i >= 0; i--) { | |
| 3868 | + if (reasoning.isLimitReached()) { | |
| 3869 | + break; | |
| 3870 | + } | |
| 3871 | + coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) }; | |
| 3872 | + } | |
| 3873 | + | |
| 3837 | 3874 | coreChat = await Promise.all(coreChat.map(async (chatItem, index) => { |
| 3838 | 3875 | let message = chatItem.mes; |
| 3839 | 3876 | let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT; |
| @@ -4755,6 +4792,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4755 | 4792 | //const getData = await response.json(); |
| 4756 | 4793 | let getMessage = extractMessageFromData(data); |
| 4757 | 4794 | let title = extractTitleFromData(data); |
| 4795 | + let reasoning = extractReasoningFromData(data); | |
| 4758 | 4796 | kobold_horde_model = title; |
| 4759 | 4797 | |
| 4760 | 4798 | const swipes = extractMultiSwipes(data, type); |
| @@ -4781,10 +4819,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4781 | 4819 | else { |
| 4782 | 4820 | // Without streaming we'll be having a full message on continuation. Treat it as a last chunk. |
| 4783 | 4821 | if (originalType !== 'continue') { |
| 4784 | 4822 | ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes, reasoning)); |
| 4785 | 4823 | } |
| 4786 | 4824 | else { |
| 4787 | 4825 | ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes, reasoning)); |
| 4788 | 4826 | } |
| 4789 | 4827 | |
| 4790 | 4828 | // This relies on `saveReply` having been called to add the message to the chat, so it must be last. |
| @@ -5672,7 +5710,6 @@ function extractMessageFromData(data) { | ||
| 5672 | 5710 | return data; |
| 5673 | 5711 | } |
| 5674 | 5712 | |
| 5675 | - function getTextContext() { | |
| 5676 | 5713 | switch (main_api) { |
| 5677 | 5714 | case 'kobold': |
| 5678 | 5715 | return data.results[0].text; |
| @@ -5689,14 +5726,24 @@ function extractMessageFromData(data) { | ||
| 5689 | 5726 | } |
| 5690 | 5727 | } |
| 5691 | 5728 | |
| 5692 | - const content = getTextContext(); | |
| 5729 | +/** | |
| 5693 | - | |
| 5730 | + * Extracts the reasoning from the response data. | |
| 5694 | - if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) { | |
| 5731 | + * @param {object} data Response data | |
| 5695 | - const thoughts = data?.choices?.[0]?.message?.reasoning_content ?? ''; | |
| 5732 | + * @returns {string} Extracted reasoning | |
| 5696 | - return [thoughts, content].filter(x => x).join('\n\n'); | |
| 5733 | + */ | |
| 5734 | +function extractReasoningFromData(data) { | |
| 5735 | + if (main_api === 'openai' && oai_settings.show_thoughts) { | |
| 5736 | + switch (oai_settings.chat_completion_source) { | |
| 5737 | + case chat_completion_sources.DEEPSEEK: | |
| 5738 | + return data?.choices?.[0]?.message?.reasoning_content ?? ''; | |
| 5739 | + case chat_completion_sources.OPENROUTER: | |
| 5740 | + return data?.choices?.[0]?.message?.reasoning ?? ''; | |
| 5741 | + case chat_completion_sources.MAKERSUITE: | |
| 5742 | + return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; | |
| 5743 | + } | |
| 5697 | 5744 | } |
| 5698 | 5745 | |
| 5699 | 5746 | return content''; |
| 5700 | 5747 | } |
| 5701 | 5748 | |
| 5702 | 5749 | /** |
| @@ -5879,7 +5926,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc | ||
| 5879 | 5926 | return getMessage; |
| 5880 | 5927 | } |
| 5881 | 5928 | |
| 5882 | 5929 | export async function saveReply(type, getMessage, fromStreaming, title, swipes, reasoning) { |
| 5883 | 5930 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || |
| 5884 | 5931 | chat[chat.length - 1]['is_user'])) { |
| 5885 | 5932 | type = 'normal'; |
| @@ -5904,8 +5951,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5904 | 5951 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5905 | 5952 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5906 | 5953 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5954 | + chat[chat.length - 1]['extra']['reasoning'] = reasoning; | |
| 5907 | 5955 | if (power_user.message_token_count_enabled) { |
| 5908 | 5956 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 5957 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5909 | 5958 | } |
| 5910 | 5959 | const chat_id = (chat.length - 1); |
| 5911 | 5960 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5924,8 +5973,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5924 | 5973 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5925 | 5974 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5926 | 5975 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5976 | + chat[chat.length - 1]['extra']['reasoning'] += reasoning; | |
| 5927 | 5977 | if (power_user.message_token_count_enabled) { |
| 5928 | 5978 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 5979 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5929 | 5980 | } |
| 5930 | 5981 | const chat_id = (chat.length - 1); |
| 5931 | 5982 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5941,8 +5992,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5941 | 5992 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5942 | 5993 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5943 | 5994 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5995 | + chat[chat.length - 1]['extra']['reasoning'] += reasoning; | |
| 5944 | 5996 | if (power_user.message_token_count_enabled) { |
| 5945 | 5997 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 5998 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5946 | 5999 | } |
| 5947 | 6000 | const chat_id = (chat.length - 1); |
| 5948 | 6001 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5958,6 +6011,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5958 | 6011 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5959 | 6012 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5960 | 6013 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 6014 | + chat[chat.length - 1]['extra']['reasoning'] = reasoning; | |
| 5961 | 6015 | if (power_user.trim_spaces) { |
| 5962 | 6016 | getMessage = getMessage.trim(); |
| 5963 | 6017 | } |
| @@ -5967,7 +6021,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5967 | 6021 | chat[chat.length - 1]['gen_finished'] = generationFinished; |
| 5968 | 6022 | |
| 5969 | 6023 | if (power_user.message_token_count_enabled) { |
| 5970 | 6024 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 6025 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5971 | 6026 | } |
| 5972 | 6027 | |
| 5973 | 6028 | if (selected_group) { |
| @@ -6032,6 +6087,19 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 6032 | 6087 | return { type, getMessage }; |
| 6033 | 6088 | } |
| 6034 | 6089 | |
| 6090 | +export function syncCurrentSwipeInfoExtras() { | |
| 6091 | + if (!chat.length) { | |
| 6092 | + return; | |
| 6093 | + } | |
| 6094 | + const currentMessage = chat[chat.length - 1]; | |
| 6095 | + if (currentMessage && Array.isArray(currentMessage.swipe_info) && typeof currentMessage.swipe_id === 'number') { | |
| 6096 | + const swipeInfo = currentMessage.swipe_info[currentMessage.swipe_id]; | |
| 6097 | + if (swipeInfo && typeof swipeInfo === 'object') { | |
| 6098 | + swipeInfo.extra = structuredClone(currentMessage.extra); | |
| 6099 | + } | |
| 6100 | + } | |
| 6101 | +} | |
| 6102 | + | |
| 6035 | 6103 | function saveImageToMessage(img, mes) { |
| 6036 | 6104 | if (mes && img.image) { |
| 6037 | 6105 | if (!mes.extra || typeof mes.extra !== 'object') { |
| @@ -8016,11 +8084,25 @@ function updateEditArrowClasses() { | ||
| 8016 | 8084 | } |
| 8017 | 8085 | } |
| 8018 | 8086 | |
| 8019 | -function closeMessageEditor() { | |
| 8087 | +/** | |
| 8088 | + * Closes the message editor. | |
| 8089 | + * @param {'message'|'reasoning'|'all'} what What to close. Default is 'all'. | |
| 8090 | + */ | |
| 8091 | +export function closeMessageEditor(what = 'all') { | |
| 8092 | + if (what === 'message' || what === 'all') { | |
| 8020 | 8093 | if (this_edit_mes_id) { |
| 8021 | 8094 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click(); |
| 8022 | 8095 | } |
| 8023 | 8096 | } |
| 8097 | + if (what === 'reasoning' || what === 'all') { | |
| 8098 | + document.querySelectorAll('.reasoning_edit_textarea').forEach((el) => { | |
| 8099 | + const cancelButton = el.closest('.mes')?.querySelector('.mes_reasoning_edit_cancel'); | |
| 8100 | + if (cancelButton instanceof HTMLElement) { | |
| 8101 | + cancelButton.click(); | |
| 8102 | + } | |
| 8103 | + }); | |
| 8104 | + } | |
| 8105 | +} | |
| 8024 | 8106 | |
| 8025 | 8107 | export function setGenerationProgress(progress) { |
| 8026 | 8108 | if (!progress) { |
| @@ -8451,6 +8533,9 @@ function swipe_left() { // when we swipe left..but no generation. | ||
| 8451 | 8533 | streamingProcessor.onStopStreaming(); |
| 8452 | 8534 | } |
| 8453 | 8535 | |
| 8536 | + // Make sure ad-hoc changes to extras are saved before swiping away | |
| 8537 | + syncCurrentSwipeInfoExtras(); | |
| 8538 | + | |
| 8454 | 8539 | const swipe_duration = 120; |
| 8455 | 8540 | const swipe_range = '700px'; |
| 8456 | 8541 | chat[chat.length - 1]['swipe_id']--; |
| @@ -8502,7 +8587,8 @@ function swipe_left() { // when we swipe left..but no generation. | ||
| 8502 | 8587 | } |
| 8503 | 8588 | |
| 8504 | 8589 | const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`); |
| 8505 | 8590 | const tokenCounttokenCountText = await getTokenCountAsync(chat[chat.length - 1]?.mes,extra?.reasoning 0|| '') + chat[chat.length - 1].mes; |
| 8591 | + const tokenCount = await getTokenCountAsync(tokenCountText, 0); | |
| 8506 | 8592 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8507 | 8593 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8508 | 8594 | } |
| @@ -8585,6 +8671,9 @@ const swipe_right = () => { | ||
| 8585 | 8671 | return unblockGeneration(); |
| 8586 | 8672 | } |
| 8587 | 8673 | |
| 8674 | + // Make sure ad-hoc changes to extras are saved before swiping away | |
| 8675 | + syncCurrentSwipeInfoExtras(); | |
| 8676 | + | |
| 8588 | 8677 | const swipe_duration = 200; |
| 8589 | 8678 | const swipe_range = 700; |
| 8590 | 8679 | //console.log(swipe_range); |
| @@ -8666,6 +8755,7 @@ const swipe_right = () => { | ||
| 8666 | 8755 | // resets the timer |
| 8667 | 8756 | swipeMessage.find('.mes_timer').html(''); |
| 8668 | 8757 | swipeMessage.find('.tokenCounterDisplay').text(''); |
| 8758 | + swipeMessage.find('.mes_reasoning').html(''); | |
| 8669 | 8759 | } else { |
| 8670 | 8760 | //console.log('showing previously generated swipe candidate, or "..."'); |
| 8671 | 8761 | //console.log('onclick right swipe calling addOneMessage'); |
| @@ -8676,7 +8766,8 @@ const swipe_right = () => { | ||
| 8676 | 8766 | chat[chat.length - 1].extra = {}; |
| 8677 | 8767 | } |
| 8678 | 8768 | |
| 8679 | 8769 | const tokenCounttokenCountText = await getTokenCountAsync(chat[chat.length - 1]?.mes,extra?.reasoning 0|| '') + chat[chat.length - 1].mes; |
| 8770 | + const tokenCount = await getTokenCountAsync(tokenCountText, 0); | |
| 8680 | 8771 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8681 | 8772 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8682 | 8773 | } |
| @@ -9478,7 +9569,8 @@ function addDebugFunctions() { | ||
| 9478 | 9569 | message.extra = {}; |
| 9479 | 9570 | } |
| 9480 | 9571 | |
| 9481 | - message.extra.token_count = await getTokenCountAsync(message.mes, 0); | |
| 9572 | + const tokenCountText = (message?.extra?.reasoning || '') + message.mes; | |
| 9573 | + message.extra.token_count = await getTokenCountAsync(tokenCountText, 0); | |
| 9482 | 9574 | } |
| 9483 | 9575 | |
| 9484 | 9576 | await saveChatConditional(); |
| @@ -11290,14 +11382,15 @@ jQuery(async function () { | ||
| 11290 | 11382 | |
| 11291 | 11383 | $(document).keyup(function (e) { |
| 11292 | 11384 | if (e.key === 'Escape') { |
| 11293 | 11385 | const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0; |
| 11294 | 11386 | if (isEditVisible && power_user.auto_save_msg_edits === false) { |
| 11295 | 11387 | closeMessageEditor('all'); |
| 11296 | 11388 | $('#send_textarea').focus(); |
| 11297 | 11389 | return; |
| 11298 | 11390 | } |
| 11299 | 11391 | if (isEditVisible && power_user.auto_save_msg_edits === true) { |
| 11300 | 11392 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click(); |
| 11393 | + closeMessageEditor('reasoning'); | |
| 11301 | 11394 | $('#send_textarea').focus(); |
| 11302 | 11395 | return; |
| 11303 | 11396 | } |
| @@ -482,10 +482,10 @@ function highlightNewBackground(bg) { | ||
| 482 | 482 | */ |
| 483 | 483 | function setFittingClass(fitting) { |
| 484 | 484 | const backgrounds = $('#bg1, #bg_custom'); |
| 485 | - backgrounds.toggleClass('cover', fitting === 'cover'); | |
| 485 | + for (const option of ['cover', 'contain', 'stretch', 'center']) { | |
| 486 | 486 | backgrounds.toggleClass('contain'option, fittingoption === 'contain'fitting); |
| 487 | - backgrounds.toggleClass('stretch', fitting === 'stretch'); | |
| 487 | + } | |
| 488 | - backgrounds.toggleClass('center', fitting === 'center'); | |
| 488 | + background_settings.fitting = fitting; | |
| 489 | 489 | } |
| 490 | 490 | |
| 491 | 491 | function onBackgroundFilterInput() { |
| @@ -466,7 +466,7 @@ async function processTtsQueue() { | ||
| 466 | 466 | } |
| 467 | 467 | |
| 468 | 468 | if (extension_settings.tts.skip_tags) { |
| 469 | 469 | text = text.replace(/<.*?>.[\s\S]*?<\/.*?>/g, '').trim(); |
| 470 | 470 | } |
| 471 | 471 | |
| 472 | 472 | if (!extension_settings.tts.pass_asterisks) { |
| @@ -188,7 +188,7 @@ export async function generateKoboldWithStreaming(generate_data, signal) { | ||
| 188 | 188 | if (data?.token) { |
| 189 | 189 | text += data.token; |
| 190 | 190 | } |
| 191 | 191 | yield { text, swipes: [], toolCalls: [], state: {} }; |
| 192 | 192 | } |
| 193 | 193 | }; |
| 194 | 194 | } |
| @@ -746,7 +746,7 @@ export async function generateNovelWithStreaming(generate_data, signal) { | ||
| 746 | 746 | text += data.token; |
| 747 | 747 | } |
| 748 | 748 | |
| 749 | 749 | yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [], state: {} }; |
| 750 | 750 | } |
| 751 | 751 | }; |
| 752 | 752 | } |
| @@ -1096,8 +1096,8 @@ async function preparePromptsForChatCompletion({ Scenario, charPersonality, name | ||
| 1096 | 1096 | // Unordered prompts without marker |
| 1097 | 1097 | { role: 'system', content: impersonationPrompt, identifier: 'impersonate' }, |
| 1098 | 1098 | { role: 'system', content: quietPrompt, identifier: 'quietPrompt' }, |
| 1099 | - { role: 'system', content: bias, identifier: 'bias' }, | |
| 1100 | 1099 | { role: 'system', content: groupNudge, identifier: 'groupNudge' }, |
| 1100 | + { role: 'assistant', content: bias, identifier: 'bias' }, | |
| 1101 | 1101 | ]; |
| 1102 | 1102 | |
| 1103 | 1103 | // Tavern Extras - Summary |
| @@ -2095,7 +2095,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 2095 | 2095 | let text = ''; |
| 2096 | 2096 | const swipes = []; |
| 2097 | 2097 | const toolCalls = []; |
| 2098 | 2098 | const state = { reasoning: '' }; |
| 2099 | 2099 | while (true) { |
| 2100 | 2100 | const { done, value } = await reader.read(); |
| 2101 | 2101 | if (done) return; |
| @@ -2113,7 +2113,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 2113 | 2113 | |
| 2114 | 2114 | ToolManager.parseToolCalls(toolCalls, parsed); |
| 2115 | 2115 | |
| 2116 | 2116 | yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls, state: state }; |
| 2117 | 2117 | } |
| 2118 | 2118 | }; |
| 2119 | 2119 | } |
| @@ -2150,16 +2150,22 @@ function getStreamingReply(data, state) { | ||
| 2150 | 2150 | if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { |
| 2151 | 2151 | return data?.delta?.text || ''; |
| 2152 | 2152 | } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { |
| 2153 | - return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || ''; | |
| 2153 | + if (oai_settings.show_thoughts) { | |
| 2154 | + state.reasoning += (data?.candidates?.[0]?.content?.parts?.filter(x => x.thought)?.map(x => x.text)?.[0] || ''); | |
| 2155 | + } | |
| 2156 | + return data?.candidates?.[0]?.content?.parts?.filter(x => !x.thought)?.map(x => x.text)?.[0] || ''; | |
| 2154 | 2157 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { |
| 2155 | 2158 | return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || ''; |
| 2156 | 2159 | } else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { |
| 2157 | - const hadThoughts = state.hadThoughts; | |
| 2160 | + if (oai_settings.show_thoughts) { | |
| 2158 | 2161 | const thoughts state.reasoning += (data.choices?.filter(x => oai_settings.show_thoughts || !x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''); |
| 2159 | - const content = data.choices?.[0]?.delta?.content || ''; | |
| 2162 | + } | |
| 2160 | - state.hadThoughts = !!thoughts; | |
| 2163 | + return data.choices?.[0]?.delta?.content || ''; | |
| 2161 | - const separator = hadThoughts && !thoughts ? '\n\n' : ''; | |
| 2164 | + } else if (oai_settings.chat_completion_source === chat_completion_sources.OPENROUTER) { | |
| 2162 | - return [thoughts, separator, content].filter(x => x).join('\n\n'); | |
| 2165 | + if (oai_settings.show_thoughts) { | |
| 2166 | + state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || ''); | |
| 2167 | + } | |
| 2168 | + return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; | |
| 2163 | 2169 | } else { |
| 2164 | 2170 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; |
| 2165 | 2171 | } |
| @@ -253,6 +253,14 @@ let power_user = { | ||
| 253 | 253 | content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.', |
| 254 | 254 | }, |
| 255 | 255 | |
| 256 | + reasoning: { | |
| 257 | + add_to_prompts: false, | |
| 258 | + prefix: '<think>\n', | |
| 259 | + suffix: '\n</think>', | |
| 260 | + separator: '\n\n', | |
| 261 | + max_additions: 1, | |
| 262 | + }, | |
| 263 | + | |
| 256 | 264 | personas: {}, |
| 257 | 265 | default_persona: null, |
| 258 | 266 | persona_descriptions: {}, |
| @@ -0,0 +1,297 @@ | ||
| 1 | +import { chat, closeMessageEditor, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js'; | |
| 2 | +import { t } from './i18n.js'; | |
| 3 | +import { MacrosParser } from './macros.js'; | |
| 4 | +import { Popup } from './popup.js'; | |
| 5 | +import { power_user } from './power-user.js'; | |
| 6 | +import { SlashCommand } from './slash-commands/SlashCommand.js'; | |
| 7 | +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; | |
| 8 | +import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | |
| 9 | +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | |
| 10 | +import { copyText } from './utils.js'; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * Gets a message from a jQuery element. | |
| 14 | + * @param {Element} element | |
| 15 | + * @returns {{messageId: number, message: object, messageBlock: JQuery<HTMLElement>}} | |
| 16 | + */ | |
| 17 | +function getMessageFromJquery(element) { | |
| 18 | + const messageBlock = $(element).closest('.mes'); | |
| 19 | + const messageId = Number(messageBlock.attr('mesid')); | |
| 20 | + const message = chat[messageId]; | |
| 21 | + return { messageId: messageId, message, messageBlock }; | |
| 22 | +} | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * Helper class for adding reasoning to messages. | |
| 26 | + * Keeps track of the number of reasoning additions. | |
| 27 | + */ | |
| 28 | +export class PromptReasoning { | |
| 29 | + static REASONING_PLACEHOLDER = '\u200B'; | |
| 30 | + static REASONING_PLACEHOLDER_REGEX = new RegExp(`${PromptReasoning.REASONING_PLACEHOLDER}$`); | |
| 31 | + | |
| 32 | + constructor() { | |
| 33 | + this.counter = 0; | |
| 34 | + } | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * Checks if the limit of reasoning additions has been reached. | |
| 38 | + * @returns {boolean} True if the limit of reasoning additions has been reached, false otherwise. | |
| 39 | + */ | |
| 40 | + isLimitReached() { | |
| 41 | + if (!power_user.reasoning.add_to_prompts) { | |
| 42 | + return true; | |
| 43 | + } | |
| 44 | + | |
| 45 | + return this.counter >= power_user.reasoning.max_additions; | |
| 46 | + } | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * Add reasoning to a message according to the power user settings. | |
| 50 | + * @param {string} content Message content | |
| 51 | + * @param {string} reasoning Message reasoning | |
| 52 | + * @returns {string} Message content with reasoning | |
| 53 | + */ | |
| 54 | + addToMessage(content, reasoning) { | |
| 55 | + // Disabled or reached limit of additions | |
| 56 | + if (!power_user.reasoning.add_to_prompts || this.counter >= power_user.reasoning.max_additions) { | |
| 57 | + return content; | |
| 58 | + } | |
| 59 | + | |
| 60 | + // No reasoning provided or a placeholder | |
| 61 | + if (!reasoning || reasoning === PromptReasoning.REASONING_PLACEHOLDER) { | |
| 62 | + return content; | |
| 63 | + } | |
| 64 | + | |
| 65 | + // Increment the counter | |
| 66 | + this.counter++; | |
| 67 | + | |
| 68 | + // Substitute macros in variable parts | |
| 69 | + const prefix = substituteParams(power_user.reasoning.prefix || ''); | |
| 70 | + const separator = substituteParams(power_user.reasoning.separator || ''); | |
| 71 | + const suffix = substituteParams(power_user.reasoning.suffix || ''); | |
| 72 | + | |
| 73 | + // Combine parts with reasoning and content | |
| 74 | + return `${prefix}${reasoning}${suffix}${separator}${content}`; | |
| 75 | + } | |
| 76 | +} | |
| 77 | + | |
| 78 | +function loadReasoningSettings() { | |
| 79 | + $('#reasoning_add_to_prompts').prop('checked', power_user.reasoning.add_to_prompts); | |
| 80 | + $('#reasoning_add_to_prompts').on('change', function () { | |
| 81 | + power_user.reasoning.add_to_prompts = !!$(this).prop('checked'); | |
| 82 | + saveSettingsDebounced(); | |
| 83 | + }); | |
| 84 | + | |
| 85 | + $('#reasoning_prefix').val(power_user.reasoning.prefix); | |
| 86 | + $('#reasoning_prefix').on('input', function () { | |
| 87 | + power_user.reasoning.prefix = String($(this).val()); | |
| 88 | + saveSettingsDebounced(); | |
| 89 | + }); | |
| 90 | + | |
| 91 | + $('#reasoning_suffix').val(power_user.reasoning.suffix); | |
| 92 | + $('#reasoning_suffix').on('input', function () { | |
| 93 | + power_user.reasoning.suffix = String($(this).val()); | |
| 94 | + saveSettingsDebounced(); | |
| 95 | + }); | |
| 96 | + | |
| 97 | + $('#reasoning_separator').val(power_user.reasoning.separator); | |
| 98 | + $('#reasoning_separator').on('input', function () { | |
| 99 | + power_user.reasoning.separator = String($(this).val()); | |
| 100 | + saveSettingsDebounced(); | |
| 101 | + }); | |
| 102 | + | |
| 103 | + $('#reasoning_max_additions').val(power_user.reasoning.max_additions); | |
| 104 | + $('#reasoning_max_additions').on('input', function () { | |
| 105 | + power_user.reasoning.max_additions = Number($(this).val()); | |
| 106 | + saveSettingsDebounced(); | |
| 107 | + }); | |
| 108 | +} | |
| 109 | + | |
| 110 | +function registerReasoningSlashCommands() { | |
| 111 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 112 | + name: 'reasoning-get', | |
| 113 | + returns: ARGUMENT_TYPE.STRING, | |
| 114 | + 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.`, | |
| 115 | + unnamedArgumentList: [ | |
| 116 | + SlashCommandArgument.fromProps({ | |
| 117 | + description: 'Message ID. If not provided, the message ID of the last message is used.', | |
| 118 | + typeList: ARGUMENT_TYPE.NUMBER, | |
| 119 | + enumProvider: commonEnumProviders.messages(), | |
| 120 | + }), | |
| 121 | + ], | |
| 122 | + callback: (_args, value) => { | |
| 123 | + const messageId = !isNaN(Number(value)) ? Number(value) : chat.length - 1; | |
| 124 | + const message = chat[messageId]; | |
| 125 | + const reasoning = String(message?.extra?.reasoning ?? ''); | |
| 126 | + return reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, ''); | |
| 127 | + }, | |
| 128 | + })); | |
| 129 | + | |
| 130 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 131 | + name: 'reasoning-set', | |
| 132 | + returns: ARGUMENT_TYPE.STRING, | |
| 133 | + helpString: t`Set the reasoning block of a message. Returns the reasoning block content.`, | |
| 134 | + namedArgumentList: [ | |
| 135 | + SlashCommandNamedArgument.fromProps({ | |
| 136 | + name: 'at', | |
| 137 | + description: 'Message ID. If not provided, the message ID of the last message is used.', | |
| 138 | + typeList: ARGUMENT_TYPE.NUMBER, | |
| 139 | + enumProvider: commonEnumProviders.messages(), | |
| 140 | + }), | |
| 141 | + ], | |
| 142 | + unnamedArgumentList: [ | |
| 143 | + SlashCommandArgument.fromProps({ | |
| 144 | + description: 'Reasoning block content.', | |
| 145 | + typeList: ARGUMENT_TYPE.STRING, | |
| 146 | + }), | |
| 147 | + ], | |
| 148 | + callback: async (args, value) => { | |
| 149 | + const messageId = !isNaN(Number(args[0])) ? Number(args[0]) : chat.length - 1; | |
| 150 | + const message = chat[messageId]; | |
| 151 | + if (!message?.extra) { | |
| 152 | + return ''; | |
| 153 | + } | |
| 154 | + | |
| 155 | + message.extra.reasoning = String(value ?? ''); | |
| 156 | + await saveChatConditional(); | |
| 157 | + | |
| 158 | + closeMessageEditor('reasoning'); | |
| 159 | + updateMessageBlock(messageId, message); | |
| 160 | + return message.extra.reasoning; | |
| 161 | + }, | |
| 162 | + })); | |
| 163 | +} | |
| 164 | + | |
| 165 | +function registerReasoningMacros() { | |
| 166 | + MacrosParser.registerMacro('reasoningPrefix', () => power_user.reasoning.prefix, t`Reasoning Prefix`); | |
| 167 | + MacrosParser.registerMacro('reasoningSuffix', () => power_user.reasoning.suffix, t`Reasoning Suffix`); | |
| 168 | + MacrosParser.registerMacro('reasoningSeparator', () => power_user.reasoning.separator, t`Reasoning Separator`); | |
| 169 | +} | |
| 170 | + | |
| 171 | +function setReasoningEventHandlers(){ | |
| 172 | + $(document).on('click', '.mes_reasoning_copy', (e) => { | |
| 173 | + e.stopPropagation(); | |
| 174 | + e.preventDefault(); | |
| 175 | + }); | |
| 176 | + | |
| 177 | + $(document).on('click', '.mes_reasoning_edit', function (e) { | |
| 178 | + e.stopPropagation(); | |
| 179 | + e.preventDefault(); | |
| 180 | + const { message, messageBlock } = getMessageFromJquery(this); | |
| 181 | + if (!message?.extra) { | |
| 182 | + return; | |
| 183 | + } | |
| 184 | + | |
| 185 | + const reasoning = String(message?.extra?.reasoning ?? ''); | |
| 186 | + const chatElement = document.getElementById('chat'); | |
| 187 | + const textarea = document.createElement('textarea'); | |
| 188 | + const reasoningBlock = messageBlock.find('.mes_reasoning'); | |
| 189 | + textarea.classList.add('reasoning_edit_textarea'); | |
| 190 | + textarea.value = reasoning.replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, ''); | |
| 191 | + $(textarea).insertBefore(reasoningBlock); | |
| 192 | + | |
| 193 | + if (!CSS.supports('field-sizing', 'content')) { | |
| 194 | + const resetHeight = function () { | |
| 195 | + const scrollTop = chatElement.scrollTop; | |
| 196 | + textarea.style.height = '0px'; | |
| 197 | + textarea.style.height = `${textarea.scrollHeight}px`; | |
| 198 | + chatElement.scrollTop = scrollTop; | |
| 199 | + }; | |
| 200 | + | |
| 201 | + textarea.addEventListener('input', resetHeight); | |
| 202 | + resetHeight(); | |
| 203 | + } | |
| 204 | + | |
| 205 | + textarea.focus(); | |
| 206 | + textarea.setSelectionRange(textarea.value.length, textarea.value.length); | |
| 207 | + | |
| 208 | + const textareaRect = textarea.getBoundingClientRect(); | |
| 209 | + const chatRect = chatElement.getBoundingClientRect(); | |
| 210 | + | |
| 211 | + // Scroll if textarea bottom is below visible area | |
| 212 | + if (textareaRect.bottom > chatRect.bottom) { | |
| 213 | + const scrollOffset = textareaRect.bottom - chatRect.bottom; | |
| 214 | + chatElement.scrollTop += scrollOffset; | |
| 215 | + } | |
| 216 | + }); | |
| 217 | + | |
| 218 | + $(document).on('click', '.mes_reasoning_edit_done', async function (e) { | |
| 219 | + e.stopPropagation(); | |
| 220 | + e.preventDefault(); | |
| 221 | + const { message, messageId, messageBlock } = getMessageFromJquery(this); | |
| 222 | + if (!message?.extra) { | |
| 223 | + return; | |
| 224 | + } | |
| 225 | + | |
| 226 | + const textarea = messageBlock.find('.reasoning_edit_textarea'); | |
| 227 | + const reasoning = String(textarea.val()); | |
| 228 | + message.extra.reasoning = reasoning; | |
| 229 | + await saveChatConditional(); | |
| 230 | + updateMessageBlock(messageId, message); | |
| 231 | + textarea.remove(); | |
| 232 | + }); | |
| 233 | + | |
| 234 | + $(document).on('click', '.mes_reasoning_edit_cancel', function (e) { | |
| 235 | + e.stopPropagation(); | |
| 236 | + e.preventDefault(); | |
| 237 | + | |
| 238 | + const { messageBlock } = getMessageFromJquery(this); | |
| 239 | + const textarea = messageBlock.find('.reasoning_edit_textarea'); | |
| 240 | + textarea.remove(); | |
| 241 | + }); | |
| 242 | + | |
| 243 | + $(document).on('click', '.mes_edit_add_reasoning', async function () { | |
| 244 | + const { message, messageId } = getMessageFromJquery(this); | |
| 245 | + if (!message?.extra) { | |
| 246 | + return; | |
| 247 | + } | |
| 248 | + | |
| 249 | + if (message.extra.reasoning) { | |
| 250 | + toastr.info(t`Reasoning already exists.`, t`Edit Message`); | |
| 251 | + return; | |
| 252 | + } | |
| 253 | + | |
| 254 | + message.extra.reasoning = PromptReasoning.REASONING_PLACEHOLDER; | |
| 255 | + await saveChatConditional(); | |
| 256 | + closeMessageEditor(); | |
| 257 | + updateMessageBlock(messageId, message); | |
| 258 | + }); | |
| 259 | + | |
| 260 | + $(document).on('click', '.mes_reasoning_delete', async function (e) { | |
| 261 | + e.stopPropagation(); | |
| 262 | + e.preventDefault(); | |
| 263 | + | |
| 264 | + const confirm = await Popup.show.confirm(t`Are you sure you want to clear the reasoning?`, t`Visible message contents will stay intact.`); | |
| 265 | + | |
| 266 | + if (!confirm) { | |
| 267 | + return; | |
| 268 | + } | |
| 269 | + | |
| 270 | + const { message, messageId } = getMessageFromJquery(this); | |
| 271 | + if (!message?.extra) { | |
| 272 | + return; | |
| 273 | + } | |
| 274 | + message.extra.reasoning = ''; | |
| 275 | + await saveChatConditional(); | |
| 276 | + updateMessageBlock(messageId, message); | |
| 277 | + }); | |
| 278 | + | |
| 279 | + $(document).on('pointerup', '.mes_reasoning_copy', async function () { | |
| 280 | + const { message } = getMessageFromJquery(this); | |
| 281 | + const reasoning = String(message?.extra?.reasoning ?? '').replace(PromptReasoning.REASONING_PLACEHOLDER_REGEX, ''); | |
| 282 | + | |
| 283 | + if (!reasoning) { | |
| 284 | + return; | |
| 285 | + } | |
| 286 | + | |
| 287 | + await copyText(reasoning); | |
| 288 | + toastr.info(t`Copied!`, '', { timeOut: 2000 }); | |
| 289 | + }); | |
| 290 | +} | |
| 291 | + | |
| 292 | +export function initReasoning() { | |
| 293 | + loadReasoningSettings(); | |
| 294 | + setReasoningEventHandlers(); | |
| 295 | + registerReasoningSlashCommands(); | |
| 296 | + registerReasoningMacros(); | |
| 297 | +} | |
| @@ -42,6 +42,7 @@ import { | ||
| 42 | 42 | showMoreMessages, |
| 43 | 43 | stopGeneration, |
| 44 | 44 | substituteParams, |
| 45 | + syncCurrentSwipeInfoExtras, | |
| 45 | 46 | system_avatar, |
| 46 | 47 | system_message_types, |
| 47 | 48 | this_chid, |
| @@ -2862,8 +2863,11 @@ async function addSwipeCallback(args, value) { | ||
| 2862 | 2863 | const newSwipeId = lastMessage.swipes.length - 1; |
| 2863 | 2864 | |
| 2864 | 2865 | if (isTrueBoolean(args.switch)) { |
| 2866 | + // Make sure ad-hoc changes to extras are saved before swiping away | |
| 2867 | + syncCurrentSwipeInfoExtras(); | |
| 2865 | 2868 | lastMessage.swipe_id = newSwipeId; |
| 2866 | 2869 | lastMessage.mes = lastMessage.swipes[newSwipeId]; |
| 2870 | + lastMessage.extra = structuredClone(lastMessage.swipe_info?.[newSwipeId]?.extra ?? lastMessage.extra ?? {}); | |
| 2867 | 2871 | } |
| 2868 | 2872 | |
| 2869 | 2873 | await saveChatConditional(); |
| @@ -235,6 +235,21 @@ async function* parseStreamData(json) { | ||
| 235 | 235 | } |
| 236 | 236 | return; |
| 237 | 237 | } |
| 238 | + else if (typeof json.choices[0].delta.reasoning === 'string' && json.choices[0].delta.reasoning.length > 0) { | |
| 239 | + for (let j = 0; j < json.choices[0].delta.reasoning.length; j++) { | |
| 240 | + const str = json.choices[0].delta.reasoning[j]; | |
| 241 | + const isLastSymbol = j === json.choices[0].delta.reasoning.length - 1; | |
| 242 | + const choiceClone = structuredClone(json.choices[0]); | |
| 243 | + choiceClone.delta.reasoning = str; | |
| 244 | + choiceClone.delta.content = isLastSymbol ? choiceClone.delta.content : ''; | |
| 245 | + const choices = [choiceClone]; | |
| 246 | + yield { | |
| 247 | + data: { ...json, choices }, | |
| 248 | + chunk: str, | |
| 249 | + }; | |
| 250 | + } | |
| 251 | + return; | |
| 252 | + } | |
| 238 | 253 | else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) { |
| 239 | 254 | for (let j = 0; j < json.choices[0].delta.content.length; j++) { |
| 240 | 255 | const str = json.choices[0].delta.content[j]; |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | import { |
| 2 | 2 | activateSendButtons, |
| 3 | 3 | addOneMessage, |
| 4 | + appendMediaToMessage, | |
| 4 | 5 | callPopup, |
| 5 | 6 | characters, |
| 6 | 7 | chat, |
| @@ -41,6 +42,7 @@ import { | ||
| 41 | 42 | substituteParamsExtended, |
| 42 | 43 | this_chid, |
| 43 | 44 | updateChatMetadata, |
| 45 | + updateMessageBlock, | |
| 44 | 46 | } from '../script.js'; |
| 45 | 47 | import { |
| 46 | 48 | extension_settings, |
| @@ -171,6 +173,8 @@ export function getContext() { | ||
| 171 | 173 | getCharacters, |
| 172 | 174 | uuidv4, |
| 173 | 175 | humanizedDateTime, |
| 176 | + updateMessageBlock, | |
| 177 | + appendMediaToMessage, | |
| 174 | 178 | }; |
| 175 | 179 | } |
| 176 | 180 | |
| @@ -986,6 +986,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | ||
| 986 | 986 | let logprobs = null; |
| 987 | 987 | const swipes = []; |
| 988 | 988 | const toolCalls = []; |
| 989 | + const state = {}; | |
| 989 | 990 | while (true) { |
| 990 | 991 | const { done, value } = await reader.read(); |
| 991 | 992 | if (done) return; |
| @@ -1004,7 +1005,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | ||
| 1004 | 1005 | logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities); |
| 1005 | 1006 | } |
| 1006 | 1007 | |
| 1007 | 1008 | yield { text, swipes, logprobs, toolCalls, state }; |
| 1008 | 1009 | } |
| 1009 | 1010 | }; |
| 1010 | 1011 | } |
| @@ -292,36 +292,44 @@ input[type='checkbox']:focus-visible { | ||
| 292 | 292 | filter: grayscale(25%); |
| 293 | 293 | } |
| 294 | 294 | |
| 295 | 295 | .mes_text table {, |
| 296 | +.mes_reasoning table { | |
| 296 | 297 | border-spacing: 0; |
| 297 | 298 | border-collapse: collapse; |
| 298 | 299 | margin-bottom: 10px; |
| 299 | 300 | } |
| 300 | 301 | |
| 301 | 302 | .mes_text td, |
| 302 | 303 | .mes_text th {, |
| 304 | +.mes_reasoning td, | |
| 305 | +.mes_reasoning th { | |
| 303 | 306 | border: 1px solid; |
| 304 | 307 | border-collapse: collapse; |
| 305 | 308 | padding: 0.25em; |
| 306 | 309 | } |
| 307 | 310 | |
| 308 | 311 | .mes_text p {, |
| 312 | +.mes_reasoning p { | |
| 309 | 313 | margin-top: 0; |
| 310 | 314 | margin-bottom: 10px; |
| 311 | 315 | } |
| 312 | 316 | |
| 313 | 317 | .mes_text li tt {, |
| 318 | +.mes_reasoning li tt { | |
| 314 | 319 | display: inline-block; |
| 315 | 320 | } |
| 316 | 321 | |
| 317 | 322 | .mes_text ol, |
| 318 | 323 | .mes_text ul {, |
| 324 | +.mes_reasoning ol, | |
| 325 | +.mes_reasoning ul { | |
| 319 | 326 | margin-top: 5px; |
| 320 | 327 | margin-bottom: 5px; |
| 321 | 328 | } |
| 322 | 329 | |
| 323 | 330 | .mes_text br, |
| 324 | 331 | .mes_bias br {, |
| 332 | +.mes_reasoning br { | |
| 325 | 333 | content: ' '; |
| 326 | 334 | } |
| 327 | 335 | |
| @@ -332,25 +340,83 @@ input[type='checkbox']:focus-visible { | ||
| 332 | 340 | color: var(--SmartThemeQuoteColor); |
| 333 | 341 | } |
| 334 | 342 | |
| 343 | +.mes_reasoning { | |
| 344 | + display: block; | |
| 345 | + border: 1px solid var(--SmartThemeBorderColor); | |
| 346 | + background-color: var(--black30a); | |
| 347 | + border-radius: 5px; | |
| 348 | + padding: 5px; | |
| 349 | + margin: 5px 0; | |
| 350 | + overflow-y: auto; | |
| 351 | +} | |
| 352 | + | |
| 353 | +.mes_reasoning_summary { | |
| 354 | + cursor: pointer; | |
| 355 | + position: relative; | |
| 356 | + margin: 2px; | |
| 357 | +} | |
| 358 | + | |
| 359 | +@supports not selector(:has(*)) { | |
| 360 | + .mes_reasoning_details { | |
| 361 | + display: none !important; | |
| 362 | + } | |
| 363 | +} | |
| 364 | + | |
| 365 | +.mes_bias:empty, | |
| 366 | +.mes_reasoning:empty, | |
| 367 | +.mes_reasoning_details:has(.mes_reasoning:empty), | |
| 368 | +.mes_block:has(.edit_textarea) .mes_reasoning_details, | |
| 369 | +.mes_reasoning_details:not([open]) .mes_reasoning_actions, | |
| 370 | +.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning, | |
| 371 | +.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_done, | |
| 372 | +.mes_reasoning_details:not(:has(.reasoning_edit_textarea)) .mes_reasoning_actions .mes_button.mes_reasoning_edit_cancel, | |
| 373 | +.mes_reasoning_details:has(.reasoning_edit_textarea) .mes_reasoning_actions .mes_button:not(.mes_reasoning_edit_done, .mes_reasoning_edit_cancel) { | |
| 374 | + display: none; | |
| 375 | +} | |
| 376 | + | |
| 377 | +.mes_reasoning_actions { | |
| 378 | + position: absolute; | |
| 379 | + right: 0; | |
| 380 | + top: 0; | |
| 381 | + | |
| 382 | + display: flex; | |
| 383 | + gap: 4px; | |
| 384 | + flex-wrap: nowrap; | |
| 385 | + justify-content: flex-end; | |
| 386 | + transition: all 200ms; | |
| 387 | + overflow-x: hidden; | |
| 388 | + padding: 1px; | |
| 389 | +} | |
| 390 | + | |
| 391 | +.mes_reasoning_summary>span { | |
| 392 | + margin-left: 0.5em; | |
| 393 | +} | |
| 394 | + | |
| 335 | 395 | .mes_text i, |
| 336 | 396 | .mes_text em {, |
| 397 | +.mes_reasoning i, | |
| 398 | +.mes_reasoning em { | |
| 337 | 399 | color: var(--SmartThemeEmColor); |
| 338 | 400 | } |
| 339 | 401 | |
| 340 | 402 | .mes_text u {, |
| 403 | +.mes_reasoning u { | |
| 341 | 404 | color: var(--SmartThemeUnderlineColor); |
| 342 | 405 | } |
| 343 | 406 | |
| 344 | 407 | .mes_text q {, |
| 408 | +.mes_reasoning q { | |
| 345 | 409 | color: var(--SmartThemeQuoteColor); |
| 346 | 410 | } |
| 347 | 411 | |
| 348 | 412 | .mes_text font[color] em, |
| 349 | 413 | .mes_text font[color] i {, |
| 350 | - color: inherit; | |
| 414 | +.mes_text font[color] u, | |
| 351 | -} | |
| 415 | +.mes_text font[color] q, | |
| 352 | - | |
| 416 | +.mes_reasoning font[color] em, | |
| 353 | 417 | .mes_textmes_reasoning font[color] q {i, |
| 418 | +.mes_reasoning font[color] u, | |
| 419 | +.mes_reasoning font[color] q { | |
| 354 | 420 | color: inherit; |
| 355 | 421 | } |
| 356 | 422 | |
| @@ -358,7 +424,8 @@ input[type='checkbox']:focus-visible { | ||
| 358 | 424 | display: block; |
| 359 | 425 | } |
| 360 | 426 | |
| 361 | 427 | .mes_text blockquote {, |
| 428 | +.mes_reasoning blockquote { | |
| 362 | 429 | border-left: 3px solid var(--SmartThemeQuoteColor); |
| 363 | 430 | padding-left: 10px; |
| 364 | 431 | background-color: var(--black30a); |
| @@ -368,18 +435,24 @@ input[type='checkbox']:focus-visible { | ||
| 368 | 435 | .mes_text strong em, |
| 369 | 436 | .mes_text strong, |
| 370 | 437 | .mes_text h2, |
| 371 | 438 | .mes_text h1 {, |
| 439 | +.mes_reasoning strong em, | |
| 440 | +.mes_reasoning strong, | |
| 441 | +.mes_reasoning h2, | |
| 442 | +.mes_reasoning h1 { | |
| 372 | 443 | font-weight: bold; |
| 373 | 444 | } |
| 374 | 445 | |
| 375 | 446 | .mes_text pre code {, |
| 447 | +.mes_reasoning pre code { | |
| 376 | 448 | position: relative; |
| 377 | 449 | display: block; |
| 378 | 450 | overflow-x: auto; |
| 379 | 451 | padding: 1em; |
| 380 | 452 | } |
| 381 | 453 | |
| 382 | 454 | .mes_text img:not(.mes_img) {, |
| 455 | +.mes_reasoning img:not(.mes_img) { | |
| 383 | 456 | max-width: 100%; |
| 384 | 457 | max-height: var(--doc-height); |
| 385 | 458 | } |
| @@ -1022,6 +1095,11 @@ body .panelControlBar { | ||
| 1022 | 1095 | /*only affects bubblechat to make it sit nicely at the bottom*/ |
| 1023 | 1096 | } |
| 1024 | 1097 | |
| 1098 | +.last_mes:has(.mes_text:empty):has(.mes_reasoning_details[open]) .mes_reasoning:not(:empty) { | |
| 1099 | + margin-bottom: 30px; | |
| 1100 | +} | |
| 1101 | + | |
| 1102 | +.last_mes .mes_reasoning, | |
| 1025 | 1103 | .last_mes .mes_text { |
| 1026 | 1104 | padding-right: 30px; |
| 1027 | 1105 | } |
| @@ -1242,14 +1320,18 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter { | ||
| 1242 | 1320 | overflow-y: clip; |
| 1243 | 1321 | } |
| 1244 | 1322 | |
| 1245 | 1323 | .mes_text {, |
| 1324 | +.mes_reasoning { | |
| 1246 | 1325 | font-weight: 500; |
| 1247 | 1326 | line-height: calc(var(--mainFontSize) + .5rem); |
| 1327 | + max-width: 100%; | |
| 1328 | + overflow-wrap: anywhere; | |
| 1329 | +} | |
| 1330 | + | |
| 1331 | +.mes_text { | |
| 1248 | 1332 | padding-left: 0; |
| 1249 | 1333 | padding-top: 5px; |
| 1250 | 1334 | padding-bottom: 5px; |
| 1251 | - max-width: 100%; | |
| 1252 | - overflow-wrap: anywhere; | |
| 1253 | 1335 | } |
| 1254 | 1336 | |
| 1255 | 1337 | br { |
| @@ -4169,10 +4251,12 @@ input[type="range"]::-webkit-slider-thumb { | ||
| 4169 | 4251 | align-items: center; |
| 4170 | 4252 | } |
| 4171 | 4253 | |
| 4254 | +.mes_reasoning_edit_cancel, | |
| 4172 | 4255 | .mes_edit_cancel.menu_button { |
| 4173 | 4256 | background-color: var(--crimson70a); |
| 4174 | 4257 | } |
| 4175 | 4258 | |
| 4259 | +.mes_reasoning_edit_done, | |
| 4176 | 4260 | .mes_edit_done.menu_button { |
| 4177 | 4261 | background-color: var(--okGreen70a); |
| 4178 | 4262 | } |
| @@ -4181,6 +4265,7 @@ input[type="range"]::-webkit-slider-thumb { | ||
| 4181 | 4265 | opacity: 1; |
| 4182 | 4266 | } |
| 4183 | 4267 | |
| 4268 | +.reasoning_edit_textarea, | |
| 4184 | 4269 | .edit_textarea { |
| 4185 | 4270 | padding: 5px; |
| 4186 | 4271 | margin: 0; |
| @@ -289,6 +289,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 289 | 289 | const model = String(request.body.model); |
| 290 | 290 | const stream = Boolean(request.body.stream); |
| 291 | 291 | const showThoughts = Boolean(request.body.show_thoughts); |
| 292 | + const isThinking = model.includes('thinking'); | |
| 292 | 293 | |
| 293 | 294 | const generationConfig = { |
| 294 | 295 | stopSequences: request.body.stop, |
| @@ -329,6 +330,12 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 329 | 330 | body.systemInstruction = prompt.system_instruction; |
| 330 | 331 | } |
| 331 | 332 | |
| 333 | + if (isThinking && showThoughts) { | |
| 334 | + generationConfig.thinkingConfig = { | |
| 335 | + includeThoughts: true, | |
| 336 | + }; | |
| 337 | + } | |
| 338 | + | |
| 332 | 339 | return body; |
| 333 | 340 | } |
| 334 | 341 | |
| @@ -342,7 +349,6 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 342 | 349 | controller.abort(); |
| 343 | 350 | }); |
| 344 | 351 | |
| 345 | - const isThinking = model.includes('thinking'); | |
| 346 | 352 | const apiVersion = isThinking ? 'v1alpha' : 'v1beta'; |
| 347 | 353 | const responseType = (stream ? 'streamGenerateContent' : 'generateContent'); |
| 348 | 354 | |
| @@ -387,11 +393,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 387 | 393 | const responseContent = candidates[0].content ?? candidates[0].output; |
| 388 | 394 | console.log('Google AI Studio response:', responseContent); |
| 389 | 395 | |
| 390 | - if (Array.isArray(responseContent?.parts) && isThinking && !showThoughts) { | |
| 396 | + const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n'); | |
| 391 | - responseContent.parts = responseContent.parts.filter(part => !part.thought); | |
| 392 | - } | |
| 393 | - | |
| 394 | - const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join('\n\n'); | |
| 395 | 397 | if (!responseText) { |
| 396 | 398 | let message = 'Google AI Studio Candidate text empty'; |
| 397 | 399 | console.log(message, generateResponseJson); |
| @@ -399,7 +401,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 399 | 401 | } |
| 400 | 402 | |
| 401 | 403 | // Wrap it back to OAI format |
| 402 | 404 | const reply = { choices: [{ 'message': { 'content': responseText } }], responseContent }; |
| 403 | 405 | return response.send(reply); |
| 404 | 406 | } |
| 405 | 407 | } catch (error) { |
| @@ -996,6 +998,10 @@ router.post('/generate', jsonParser, function (request, response) { | ||
| 996 | 998 | bodyParams['route'] = 'fallback'; |
| 997 | 999 | } |
| 998 | 1000 | |
| 1001 | + if (request.body.show_thoughts) { | |
| 1002 | + bodyParams['include_reasoning'] = true; | |
| 1003 | + } | |
| 1004 | + | |
| 999 | 1005 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); |
| 1000 | 1006 | if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) { |
| 1001 | 1007 | cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth); |