Merge pull request #3345 from SillyTavern/woo-yeah Better reasoning
Signed| @@ -1977,11 +1977,11 @@ | |||
| 1977 | </span> | 1977 | </span> |
| 1978 | </div> | 1978 | </div> |
| 1979 | </div> | 1979 | </div> |
| 1980 | <div class="range-block" data-source="makersuite,deepseek"> | 1980 | <div class="range-block" data-source="makersuite,deepseek,openrouter"> |
| 1981 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> | 1981 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> |
| 1982 | <input id="openai_show_thoughts" type="checkbox" /> | 1982 | <input id="openai_show_thoughts" type="checkbox" /> |
| 1983 | <span> | 1983 | <span> |
| 1984 | <span data-i18n="Show model thoughts">Show model thoughts</span> | 1984 | <span data-i18n="Show model reasoning">Show model reasoning</span> |
| 1985 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i> | 1985 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i> |
| 1986 | </span> | 1986 | </span> |
| 1987 | </label> | 1987 | </label> |
| @@ -3800,6 +3800,39 @@ | |||
| 3800 | </div> | 3800 | </div> |
| 3801 | </div> | 3801 | </div> |
| 3802 | <div> | 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 | <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4> | 3836 | <h4 class="standoutHeader" data-i18n="Miscellaneous">Miscellaneous</h4> |
| 3804 | <div> | 3837 | <div> |
| 3805 | <small> | 3838 | <small> |
| @@ -6221,14 +6254,26 @@ | |||
| 6221 | <div class="mes_edit_buttons"> | 6254 | <div class="mes_edit_buttons"> |
| 6222 | <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div> | 6255 | <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div> |
| 6223 | <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div> | 6256 | <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div> |
| 6224 | <div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message"> | 6257 | <div class="mes_edit_add_reasoning menu_button fa-solid fa-lightbulb" title="Add a reasoning block" data-i18n="[title]Add a reasoning block"></div> |
| 6225 | </div> | 6258 | <div class="mes_edit_delete menu_button fa-solid fa-trash-can" title="Delete this message" data-i18n="[title]Delete this message"></div> |
| 6226 | <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div> | 6259 | <div class="mes_edit_up menu_button fa-solid fa-chevron-up " title="Move message up" data-i18n="[title]Move message up"></div> |
| 6227 | <div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down"> | 6260 | <div class="mes_edit_down menu_button fa-solid fa-chevron-down" title="Move message down" data-i18n="[title]Move message down"></div> |
| 6228 | </div> | ||
| 6229 | <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div> | 6261 | <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div> |
| 6230 | </div> | 6262 | </div> |
| 6231 | </div> | 6263 | </div> |
| 6264 | <details class="mes_reasoning_details"> | ||
| 6265 | <summary class="mes_reasoning_summary"> | ||
| 6266 | <span data-i18n="Reasoning">Reasoning</span> | ||
| 6267 | <div class="mes_reasoning_actions"> | ||
| 6268 | <div class="mes_reasoning_edit_done mes_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirmedit"></div> | ||
| 6269 | <div class="mes_reasoning_edit_cancel mes_button fa-solid fa-xmark" title="Cancel edit" data-i18n="[title]Cancel edit"></div> | ||
| 6270 | <div class="mes_reasoning_edit mes_button fa-solid fa-pencil" title="Edit reasoning" data-i18n="[title]Edit reasoning"></div> | ||
| 6271 | <div class="mes_reasoning_copy mes_button fa-solid fa-copy" title="Copy reasoning" data-i18n="[title]Copy reasoning"></div> | ||
| 6272 | <div class="mes_reasoning_delete mes_button fa-solid fa-trash-can" title="Remove reasoning" data-i18n="[title]Remove reasoning"></div> | ||
| 6273 | </div> | ||
| 6274 | </summary> | ||
| 6275 | <div class="mes_reasoning"></div> | ||
| 6276 | </details> | ||
| 6232 | <div class="mes_text"></div> | 6277 | <div class="mes_text"></div> |
| 6233 | <div class="mes_img_container"> | 6278 | <div class="mes_img_container"> |
| 6234 | <div class="mes_img_controls"> | 6279 | <div class="mes_img_controls"> |
| @@ -1385,7 +1385,7 @@ | |||
| 1385 | "enable_functions_desc_1": "Autorise l'utilisation", | 1385 | "enable_functions_desc_1": "Autorise l'utilisation", |
| 1386 | "enable_functions_desc_2": "outils de fonction", | 1386 | "enable_functions_desc_2": "outils de fonction", |
| 1387 | "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.", | 1387 | "enable_functions_desc_3": "Peut être utilisé par diverses extensions pour fournir des fonctionnalités supplémentaires.", |
| 1388 | "Show model thoughts": "Afficher les pensées du modèle", | 1388 | "Show model reasoning": "Afficher les pensées du modèle", |
| 1389 | "Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.", | 1389 | "Display the model's internal thoughts in the response.": "Afficher les pensées internes du modèle dans la réponse.", |
| 1390 | "Confirm token parsing with": "Confirmer l'analyse des tokens avec", | 1390 | "Confirm token parsing with": "Confirmer l'analyse des tokens avec", |
| 1391 | "openai_logit_bias_no_items": "Aucun élément", | 1391 | "openai_logit_bias_no_items": "Aucun élément", |
| @@ -266,7 +266,7 @@ | |||
| 266 | "Use system prompt": "使用系统提示词", | 266 | "Use system prompt": "使用系统提示词", |
| 267 | "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过", | 267 | "Merges_all_system_messages_desc_1": "合并所有系统消息,直到第一条具有非系统角色的消息,然后通过", |
| 268 | "Merges_all_system_messages_desc_2": "字段发送。", | 268 | "Merges_all_system_messages_desc_2": "字段发送。", |
| 269 | "Show model thoughts": "展示思维链", | 269 | "Show model reasoning": "展示思维链", |
| 270 | "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。", | 270 | "Display the model's internal thoughts in the response.": "展示模型在回复时的内部思维链。", |
| 271 | "Assistant Prefill": "AI预填", | 271 | "Assistant Prefill": "AI预填", |
| 272 | "Expand the editor": "展开编辑器", | 272 | "Expand the editor": "展开编辑器", |
| @@ -2357,7 +2357,7 @@ | |||
| 2357 | "Forbid": "禁止", | 2357 | "Forbid": "禁止", |
| 2358 | "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。", | 2358 | "Aphrodite only. Determines the order of samplers. Skew is always applied post-softmax, so it's not included here.": "僅限 Aphrodite 使用。決定採樣器的順序。偏移總是在 softmax 後應用,因此不包括在此。", |
| 2359 | "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。", | 2359 | "Aphrodite only. Determines the order of samplers.": "僅限 Aphrodite 使用。決定採樣器的順序。", |
| 2360 | "Show model thoughts": "顯示模型思維鏈", | 2360 | "Show model reasoning": "顯示模型思維鏈", |
| 2361 | "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。", | 2361 | "Display the model's internal thoughts in the response.": "在回應中顯示模型的思維鏈(內部思考過程)。", |
| 2362 | "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]", | 2362 | "Generic (OpenAI-compatible) [LM Studio, LiteLLM, etc.]": "通用(兼容 OpenAI)[LM Studio, LiteLLM 等]", |
| 2363 | "Model ID (optional)": "模型 ID(可選)", | 2363 | "Model ID (optional)": "模型 ID(可選)", |
| @@ -267,6 +267,7 @@ import { initSettingsSearch } from './scripts/setting-search.js'; | |||
| 267 | import { initBulkEdit } from './scripts/bulk-edit.js'; | 267 | import { initBulkEdit } from './scripts/bulk-edit.js'; |
| 268 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; | 268 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; |
| 269 | import { getContext } from './scripts/st-context.js'; | 269 | import { getContext } from './scripts/st-context.js'; |
| 270 | import { initReasoning, PromptReasoning } from './scripts/reasoning.js'; | ||
| 270 | 271 | ||
| 271 | // API OBJECT FOR EXTERNAL WIRING | 272 | // API OBJECT FOR EXTERNAL WIRING |
| 272 | globalThis.SillyTavern = { | 273 | globalThis.SillyTavern = { |
| @@ -982,6 +983,7 @@ async function firstLoadInit() { | |||
| 982 | initServerHistory(); | 983 | initServerHistory(); |
| 983 | initSettingsSearch(); | 984 | initSettingsSearch(); |
| 984 | initBulkEdit(); | 985 | initBulkEdit(); |
| 986 | initReasoning(); | ||
| 985 | await initScrapers(); | 987 | await initScrapers(); |
| 986 | doDailyExtensionUpdatesCheck(); | 988 | doDailyExtensionUpdatesCheck(); |
| 987 | await hideLoader(); | 989 | await hideLoader(); |
| @@ -2200,6 +2202,7 @@ function getMessageFromTemplate({ | |||
| 2200 | isUser, | 2202 | isUser, |
| 2201 | avatarImg, | 2203 | avatarImg, |
| 2202 | bias, | 2204 | bias, |
| 2205 | reasoning, | ||
| 2203 | isSystem, | 2206 | isSystem, |
| 2204 | title, | 2207 | title, |
| 2205 | timerValue, | 2208 | timerValue, |
| @@ -2224,6 +2227,7 @@ function getMessageFromTemplate({ | |||
| 2224 | mes.find('.avatar img').attr('src', avatarImg); | 2227 | mes.find('.avatar img').attr('src', avatarImg); |
| 2225 | mes.find('.ch_name .name_text').text(characterName); | 2228 | mes.find('.ch_name .name_text').text(characterName); |
| 2226 | mes.find('.mes_bias').html(bias); | 2229 | mes.find('.mes_bias').html(bias); |
| 2230 | mes.find('.mes_reasoning').html(reasoning); | ||
| 2227 | mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`); | 2231 | mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`); |
| 2228 | mes.find('.mesIDDisplay').text(`#${mesId}`); | 2232 | mes.find('.mesIDDisplay').text(`#${mesId}`); |
| 2229 | tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`); | 2233 | tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| @@ -2238,10 +2242,16 @@ function getMessageFromTemplate({ | |||
| 2238 | return mes; | 2242 | return mes; |
| 2239 | } | 2243 | } |
| 2240 | 2244 | ||
| 2245 | /** | ||
| 2246 | * Re-renders a message block with updated content. | ||
| 2247 | * @param {number} messageId Message ID | ||
| 2248 | * @param {object} message Message object | ||
| 2249 | */ | ||
| 2241 | export function updateMessageBlock(messageId, message) { | 2250 | export function updateMessageBlock(messageId, message) { |
| 2242 | const messageElement = $(`#chat [mesid="${messageId}"]`); | 2251 | const messageElement = $(`#chat [mesid="${messageId}"]`); |
| 2243 | const text = message?.extra?.display_text ?? message.mes; | 2252 | const text = message?.extra?.display_text ?? message.mes; |
| 2244 | messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId)); | 2253 | messageElement.find('.mes_text').html(messageFormatting(text, message.name, message.is_system, message.is_user, messageId)); |
| 2254 | messageElement.find('.mes_reasoning').html(messageFormatting(message.extra?.reasoning ?? '', '', false, false, -1)); | ||
| 2245 | addCopyToCodeBlocks(messageElement); | 2255 | addCopyToCodeBlocks(messageElement); |
| 2246 | appendMediaToMessage(message, messageElement); | 2256 | appendMediaToMessage(message, messageElement); |
| 2247 | } | 2257 | } |
| @@ -2400,6 +2410,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | |||
| 2400 | sanitizerOverrides, | 2410 | sanitizerOverrides, |
| 2401 | ); | 2411 | ); |
| 2402 | const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1); | 2412 | const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1); |
| 2413 | const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, -1); | ||
| 2403 | let bookmarkLink = mes?.extra?.bookmark_link ?? ''; | 2414 | let bookmarkLink = mes?.extra?.bookmark_link ?? ''; |
| 2404 | 2415 | ||
| 2405 | let params = { | 2416 | let params = { |
| @@ -2409,6 +2420,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | |||
| 2409 | isUser: mes.is_user, | 2420 | isUser: mes.is_user, |
| 2410 | avatarImg: avatarImg, | 2421 | avatarImg: avatarImg, |
| 2411 | bias: bias, | 2422 | bias: bias, |
| 2423 | reasoning: reasoning, | ||
| 2412 | isSystem: isSystem, | 2424 | isSystem: isSystem, |
| 2413 | title: title, | 2425 | title: title, |
| 2414 | bookmarkLink: bookmarkLink, | 2426 | bookmarkLink: bookmarkLink, |
| @@ -2468,6 +2480,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | |||
| 2468 | const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`); | 2480 | const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`); |
| 2469 | swipeMessage.attr('swipeid', params.swipeId); | 2481 | swipeMessage.attr('swipeid', params.swipeId); |
| 2470 | swipeMessage.find('.mes_text').html(messageText).attr('title', title); | 2482 | swipeMessage.find('.mes_text').html(messageText).attr('title', title); |
| 2483 | swipeMessage.find('.mes_reasoning').html(reasoning); | ||
| 2471 | swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`); | 2484 | swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`); |
| 2472 | appendMediaToMessage(mes, swipeMessage); | 2485 | appendMediaToMessage(mes, swipeMessage); |
| 2473 | if (power_user.timestamp_model_icon && params.extra?.api) { | 2486 | if (power_user.timestamp_model_icon && params.extra?.api) { |
| @@ -3078,6 +3091,7 @@ class StreamingProcessor { | |||
| 3078 | this.messageTextDom = null; | 3091 | this.messageTextDom = null; |
| 3079 | this.messageTimerDom = null; | 3092 | this.messageTimerDom = null; |
| 3080 | this.messageTokenCounterDom = null; | 3093 | this.messageTokenCounterDom = null; |
| 3094 | this.messageReasoningDom = null; | ||
| 3081 | /** @type {HTMLTextAreaElement} */ | 3095 | /** @type {HTMLTextAreaElement} */ |
| 3082 | this.sendTextarea = document.querySelector('#send_textarea'); | 3096 | this.sendTextarea = document.querySelector('#send_textarea'); |
| 3083 | this.type = type; | 3097 | this.type = type; |
| @@ -3093,6 +3107,7 @@ class StreamingProcessor { | |||
| 3093 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ | 3107 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ |
| 3094 | this.messageLogprobs = []; | 3108 | this.messageLogprobs = []; |
| 3095 | this.toolCalls = []; | 3109 | this.toolCalls = []; |
| 3110 | this.reasoning = ''; | ||
| 3096 | } | 3111 | } |
| 3097 | 3112 | ||
| 3098 | #checkDomElements(messageId) { | 3113 | #checkDomElements(messageId) { |
| @@ -3101,6 +3116,7 @@ class StreamingProcessor { | |||
| 3101 | this.messageTextDom = this.messageDom?.querySelector('.mes_text'); | 3116 | this.messageTextDom = this.messageDom?.querySelector('.mes_text'); |
| 3102 | this.messageTimerDom = this.messageDom?.querySelector('.mes_timer'); | 3117 | this.messageTimerDom = this.messageDom?.querySelector('.mes_timer'); |
| 3103 | this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay'); | 3118 | this.messageTokenCounterDom = this.messageDom?.querySelector('.tokenCounterDisplay'); |
| 3119 | this.messageReasoningDom = this.messageDom?.querySelector('.mes_reasoning'); | ||
| 3104 | } | 3120 | } |
| 3105 | } | 3121 | } |
| 3106 | 3122 | ||
| @@ -3178,18 +3194,27 @@ class StreamingProcessor { | |||
| 3178 | this.#checkDomElements(messageId); | 3194 | this.#checkDomElements(messageId); |
| 3179 | this.#updateMessageBlockVisibility(); | 3195 | this.#updateMessageBlockVisibility(); |
| 3180 | const currentTime = new Date(); | 3196 | const currentTime = new Date(); |
| 3181 | // Don't waste time calculating token count for streaming | ||
| 3182 | const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(processedText, 0) : 0; | ||
| 3183 | const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount); | ||
| 3184 | chat[messageId]['mes'] = processedText; | 3197 | chat[messageId]['mes'] = processedText; |
| 3185 | chat[messageId]['gen_started'] = this.timeStarted; | 3198 | chat[messageId]['gen_started'] = this.timeStarted; |
| 3186 | chat[messageId]['gen_finished'] = currentTime; | 3199 | chat[messageId]['gen_finished'] = currentTime; |
| 3187 | 3200 | ||
| 3188 | if (currentTokenCount) { | ||
| 3189 | if (!chat[messageId]['extra']) { | 3201 | if (!chat[messageId]['extra']) { |
| 3190 | chat[messageId]['extra'] = {}; | 3202 | chat[messageId]['extra'] = {}; |
| 3191 | } | 3203 | } |
| 3192 | 3204 | ||
| 3205 | if (this.reasoning) { | ||
| 3206 | chat[messageId]['extra']['reasoning'] = this.reasoning; | ||
| 3207 | if (this.messageReasoningDom instanceof HTMLElement) { | ||
| 3208 | const formattedReasoning = messageFormatting(this.reasoning, '', false, false, -1); | ||
| 3209 | this.messageReasoningDom.innerHTML = formattedReasoning; | ||
| 3210 | } | ||
| 3211 | } | ||
| 3212 | |||
| 3213 | // Don't waste time calculating token count for streaming | ||
| 3214 | const tokenCountText = (this.reasoning || '') + processedText; | ||
| 3215 | const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCount(tokenCountText, 0) : 0; | ||
| 3216 | |||
| 3217 | if (currentTokenCount) { | ||
| 3193 | chat[messageId]['extra']['token_count'] = currentTokenCount; | 3218 | chat[messageId]['extra']['token_count'] = currentTokenCount; |
| 3194 | if (this.messageTokenCounterDom instanceof HTMLElement) { | 3219 | if (this.messageTokenCounterDom instanceof HTMLElement) { |
| 3195 | this.messageTokenCounterDom.textContent = `${currentTokenCount}t`; | 3220 | this.messageTokenCounterDom.textContent = `${currentTokenCount}t`; |
| @@ -3211,10 +3236,13 @@ class StreamingProcessor { | |||
| 3211 | if (this.messageTextDom instanceof HTMLElement) { | 3236 | if (this.messageTextDom instanceof HTMLElement) { |
| 3212 | this.messageTextDom.innerHTML = formattedText; | 3237 | this.messageTextDom.innerHTML = formattedText; |
| 3213 | } | 3238 | } |
| 3239 | |||
| 3240 | const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount); | ||
| 3214 | if (this.messageTimerDom instanceof HTMLElement) { | 3241 | if (this.messageTimerDom instanceof HTMLElement) { |
| 3215 | this.messageTimerDom.textContent = timePassed.timerValue; | 3242 | this.messageTimerDom.textContent = timePassed.timerValue; |
| 3216 | this.messageTimerDom.title = timePassed.timerTitle; | 3243 | this.messageTimerDom.title = timePassed.timerTitle; |
| 3217 | } | 3244 | } |
| 3245 | |||
| 3218 | this.setFirstSwipe(messageId); | 3246 | this.setFirstSwipe(messageId); |
| 3219 | } | 3247 | } |
| 3220 | 3248 | ||
| @@ -3321,7 +3349,7 @@ class StreamingProcessor { | |||
| 3321 | } | 3349 | } |
| 3322 | 3350 | ||
| 3323 | /** | 3351 | /** |
| 3324 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[] }, void, void>} | 3352 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>} |
| 3325 | */ | 3353 | */ |
| 3326 | *nullStreamingGeneration() { | 3354 | *nullStreamingGeneration() { |
| 3327 | throw new Error('Generation function for streaming is not hooked up'); | 3355 | throw new Error('Generation function for streaming is not hooked up'); |
| @@ -3343,7 +3371,7 @@ class StreamingProcessor { | |||
| 3343 | try { | 3371 | try { |
| 3344 | const sw = new Stopwatch(1000 / power_user.streaming_fps); | 3372 | const sw = new Stopwatch(1000 / power_user.streaming_fps); |
| 3345 | const timestamps = []; | 3373 | const timestamps = []; |
| 3346 | for await (const { text, swipes, logprobs, toolCalls } of this.generator()) { | 3374 | for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) { |
| 3347 | timestamps.push(Date.now()); | 3375 | timestamps.push(Date.now()); |
| 3348 | if (this.isStopped) { | 3376 | if (this.isStopped) { |
| 3349 | return; | 3377 | return; |
| @@ -3355,6 +3383,7 @@ class StreamingProcessor { | |||
| 3355 | if (logprobs) { | 3383 | if (logprobs) { |
| 3356 | this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs])); | 3384 | this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs])); |
| 3357 | } | 3385 | } |
| 3386 | this.reasoning = state?.reasoning ?? ''; | ||
| 3358 | await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text); | 3387 | await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text); |
| 3359 | await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text)); | 3388 | await sw.tick(() => this.onProgressStreaming(this.messageId, this.continueMessage + text)); |
| 3360 | } | 3389 | } |
| @@ -3821,6 +3850,14 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 3821 | coreChat.pop(); | 3850 | coreChat.pop(); |
| 3822 | } | 3851 | } |
| 3823 | 3852 | ||
| 3853 | const reasoning = new PromptReasoning(); | ||
| 3854 | for (let i = coreChat.length - 1; i >= 0; i--) { | ||
| 3855 | if (reasoning.isLimitReached()) { | ||
| 3856 | break; | ||
| 3857 | } | ||
| 3858 | coreChat[i] = { ...coreChat[i], mes: reasoning.addToMessage(coreChat[i].mes, coreChat[i].extra?.reasoning) }; | ||
| 3859 | } | ||
| 3860 | |||
| 3824 | coreChat = await Promise.all(coreChat.map(async (chatItem, index) => { | 3861 | coreChat = await Promise.all(coreChat.map(async (chatItem, index) => { |
| 3825 | let message = chatItem.mes; | 3862 | let message = chatItem.mes; |
| 3826 | let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT; | 3863 | let regexType = chatItem.is_user ? regex_placement.USER_INPUT : regex_placement.AI_OUTPUT; |
| @@ -4742,6 +4779,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4742 | //const getData = await response.json(); | 4779 | //const getData = await response.json(); |
| 4743 | let getMessage = extractMessageFromData(data); | 4780 | let getMessage = extractMessageFromData(data); |
| 4744 | let title = extractTitleFromData(data); | 4781 | let title = extractTitleFromData(data); |
| 4782 | let reasoning = extractReasoningFromData(data); | ||
| 4745 | kobold_horde_model = title; | 4783 | kobold_horde_model = title; |
| 4746 | 4784 | ||
| 4747 | const swipes = extractMultiSwipes(data, type); | 4785 | const swipes = extractMultiSwipes(data, type); |
| @@ -4768,10 +4806,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | |||
| 4768 | else { | 4806 | else { |
| 4769 | // Without streaming we'll be having a full message on continuation. Treat it as a last chunk. | 4807 | // Without streaming we'll be having a full message on continuation. Treat it as a last chunk. |
| 4770 | if (originalType !== 'continue') { | 4808 | if (originalType !== 'continue') { |
| 4771 | ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes)); | 4809 | ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes, reasoning)); |
| 4772 | } | 4810 | } |
| 4773 | else { | 4811 | else { |
| 4774 | ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes)); | 4812 | ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes, reasoning)); |
| 4775 | } | 4813 | } |
| 4776 | 4814 | ||
| 4777 | // This relies on `saveReply` having been called to add the message to the chat, so it must be last. | 4815 | // This relies on `saveReply` having been called to add the message to the chat, so it must be last. |
| @@ -5659,7 +5697,6 @@ function extractMessageFromData(data) { | |||
| 5659 | return data; | 5697 | return data; |
| 5660 | } | 5698 | } |
| 5661 | 5699 | ||
| 5662 | function getTextContext() { | ||
| 5663 | switch (main_api) { | 5700 | switch (main_api) { |
| 5664 | case 'kobold': | 5701 | case 'kobold': |
| 5665 | return data.results[0].text; | 5702 | return data.results[0].text; |
| @@ -5676,14 +5713,24 @@ function extractMessageFromData(data) { | |||
| 5676 | } | 5713 | } |
| 5677 | } | 5714 | } |
| 5678 | 5715 | ||
| 5679 | const content = getTextContext(); | 5716 | /** |
| 5680 | 5717 | * Extracts the reasoning from the response data. | |
| 5681 | if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) { | 5718 | * @param {object} data Response data |
| 5682 | const thoughts = data?.choices?.[0]?.message?.reasoning_content ?? ''; | 5719 | * @returns {string} Extracted reasoning |
| 5683 | return [thoughts, content].filter(x => x).join('\n\n'); | 5720 | */ |
| 5721 | function extractReasoningFromData(data) { | ||
| 5722 | if (main_api === 'openai' && oai_settings.show_thoughts) { | ||
| 5723 | switch (oai_settings.chat_completion_source) { | ||
| 5724 | case chat_completion_sources.DEEPSEEK: | ||
| 5725 | return data?.choices?.[0]?.message?.reasoning_content ?? ''; | ||
| 5726 | case chat_completion_sources.OPENROUTER: | ||
| 5727 | return data?.choices?.[0]?.message?.reasoning ?? ''; | ||
| 5728 | case chat_completion_sources.MAKERSUITE: | ||
| 5729 | return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; | ||
| 5730 | } | ||
| 5684 | } | 5731 | } |
| 5685 | 5732 | ||
| 5686 | return content; | 5733 | return ''; |
| 5687 | } | 5734 | } |
| 5688 | 5735 | ||
| 5689 | /** | 5736 | /** |
| @@ -5866,7 +5913,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc | |||
| 5866 | return getMessage; | 5913 | return getMessage; |
| 5867 | } | 5914 | } |
| 5868 | 5915 | ||
| 5869 | export async function saveReply(type, getMessage, fromStreaming, title, swipes) { | 5916 | export async function saveReply(type, getMessage, fromStreaming, title, swipes, reasoning) { |
| 5870 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || | 5917 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || |
| 5871 | chat[chat.length - 1]['is_user'])) { | 5918 | chat[chat.length - 1]['is_user'])) { |
| 5872 | type = 'normal'; | 5919 | type = 'normal'; |
| @@ -5891,8 +5938,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5891 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5938 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5892 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5939 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5893 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 5940 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5941 | chat[chat.length - 1]['extra']['reasoning'] = reasoning; | ||
| 5894 | if (power_user.message_token_count_enabled) { | 5942 | if (power_user.message_token_count_enabled) { |
| 5895 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 5943 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 5944 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5896 | } | 5945 | } |
| 5897 | const chat_id = (chat.length - 1); | 5946 | const chat_id = (chat.length - 1); |
| 5898 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); | 5947 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5911,8 +5960,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5911 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5960 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5912 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5961 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5913 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 5962 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5963 | chat[chat.length - 1]['extra']['reasoning'] += reasoning; | ||
| 5914 | if (power_user.message_token_count_enabled) { | 5964 | if (power_user.message_token_count_enabled) { |
| 5915 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 5965 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 5966 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5916 | } | 5967 | } |
| 5917 | const chat_id = (chat.length - 1); | 5968 | const chat_id = (chat.length - 1); |
| 5918 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); | 5969 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5928,8 +5979,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5928 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5979 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5929 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5980 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5930 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 5981 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5982 | chat[chat.length - 1]['extra']['reasoning'] += reasoning; | ||
| 5931 | if (power_user.message_token_count_enabled) { | 5983 | if (power_user.message_token_count_enabled) { |
| 5932 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 5984 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 5985 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5933 | } | 5986 | } |
| 5934 | const chat_id = (chat.length - 1); | 5987 | const chat_id = (chat.length - 1); |
| 5935 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); | 5988 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5945,6 +5998,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5945 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); | 5998 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5946 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); | 5999 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5947 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); | 6000 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 6001 | chat[chat.length - 1]['extra']['reasoning'] = reasoning; | ||
| 5948 | if (power_user.trim_spaces) { | 6002 | if (power_user.trim_spaces) { |
| 5949 | getMessage = getMessage.trim(); | 6003 | getMessage = getMessage.trim(); |
| 5950 | } | 6004 | } |
| @@ -5954,7 +6008,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | |||
| 5954 | chat[chat.length - 1]['gen_finished'] = generationFinished; | 6008 | chat[chat.length - 1]['gen_finished'] = generationFinished; |
| 5955 | 6009 | ||
| 5956 | if (power_user.message_token_count_enabled) { | 6010 | if (power_user.message_token_count_enabled) { |
| 5957 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(chat[chat.length - 1]['mes'], 0); | 6011 | const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes']; |
| 6012 | chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | ||
| 5958 | } | 6013 | } |
| 5959 | 6014 | ||
| 5960 | if (selected_group) { | 6015 | if (selected_group) { |
| @@ -7997,11 +8052,25 @@ function updateEditArrowClasses() { | |||
| 7997 | } | 8052 | } |
| 7998 | } | 8053 | } |
| 7999 | 8054 | ||
| 8000 | function closeMessageEditor() { | 8055 | /** |
| 8056 | * Closes the message editor. | ||
| 8057 | * @param {'message'|'reasoning'|'all'} what What to close. Default is 'all'. | ||
| 8058 | */ | ||
| 8059 | export function closeMessageEditor(what = 'all') { | ||
| 8060 | if (what === 'message' || what === 'all') { | ||
| 8001 | if (this_edit_mes_id) { | 8061 | if (this_edit_mes_id) { |
| 8002 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click(); | 8062 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click(); |
| 8003 | } | 8063 | } |
| 8004 | } | 8064 | } |
| 8065 | if (what === 'reasoning' || what === 'all') { | ||
| 8066 | document.querySelectorAll('.reasoning_edit_textarea').forEach((el) => { | ||
| 8067 | const cancelButton = el.closest('.mes')?.querySelector('.mes_reasoning_edit_cancel'); | ||
| 8068 | if (cancelButton instanceof HTMLElement) { | ||
| 8069 | cancelButton.click(); | ||
| 8070 | } | ||
| 8071 | }); | ||
| 8072 | } | ||
| 8073 | } | ||
| 8005 | 8074 | ||
| 8006 | export function setGenerationProgress(progress) { | 8075 | export function setGenerationProgress(progress) { |
| 8007 | if (!progress) { | 8076 | if (!progress) { |
| @@ -8483,7 +8552,8 @@ function swipe_left() { // when we swipe left..but no generation. | |||
| 8483 | } | 8552 | } |
| 8484 | 8553 | ||
| 8485 | const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`); | 8554 | const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`); |
| 8486 | const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0); | 8555 | const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes; |
| 8556 | const tokenCount = await getTokenCountAsync(tokenCountText, 0); | ||
| 8487 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; | 8557 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8488 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); | 8558 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8489 | } | 8559 | } |
| @@ -8647,6 +8717,7 @@ const swipe_right = () => { | |||
| 8647 | // resets the timer | 8717 | // resets the timer |
| 8648 | swipeMessage.find('.mes_timer').html(''); | 8718 | swipeMessage.find('.mes_timer').html(''); |
| 8649 | swipeMessage.find('.tokenCounterDisplay').text(''); | 8719 | swipeMessage.find('.tokenCounterDisplay').text(''); |
| 8720 | swipeMessage.find('.mes_reasoning').html(''); | ||
| 8650 | } else { | 8721 | } else { |
| 8651 | //console.log('showing previously generated swipe candidate, or "..."'); | 8722 | //console.log('showing previously generated swipe candidate, or "..."'); |
| 8652 | //console.log('onclick right swipe calling addOneMessage'); | 8723 | //console.log('onclick right swipe calling addOneMessage'); |
| @@ -8657,7 +8728,8 @@ const swipe_right = () => { | |||
| 8657 | chat[chat.length - 1].extra = {}; | 8728 | chat[chat.length - 1].extra = {}; |
| 8658 | } | 8729 | } |
| 8659 | 8730 | ||
| 8660 | const tokenCount = await getTokenCountAsync(chat[chat.length - 1].mes, 0); | 8731 | const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes; |
| 8732 | const tokenCount = await getTokenCountAsync(tokenCountText, 0); | ||
| 8661 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; | 8733 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8662 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); | 8734 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8663 | } | 8735 | } |
| @@ -9459,7 +9531,8 @@ function addDebugFunctions() { | |||
| 9459 | message.extra = {}; | 9531 | message.extra = {}; |
| 9460 | } | 9532 | } |
| 9461 | 9533 | ||
| 9462 | message.extra.token_count = await getTokenCountAsync(message.mes, 0); | 9534 | const tokenCountText = (message?.extra?.reasoning || '') + message.mes; |
| 9535 | message.extra.token_count = await getTokenCountAsync(tokenCountText, 0); | ||
| 9463 | } | 9536 | } |
| 9464 | 9537 | ||
| 9465 | await saveChatConditional(); | 9538 | await saveChatConditional(); |
| @@ -11228,14 +11301,15 @@ jQuery(async function () { | |||
| 11228 | 11301 | ||
| 11229 | $(document).keyup(function (e) { | 11302 | $(document).keyup(function (e) { |
| 11230 | if (e.key === 'Escape') { | 11303 | if (e.key === 'Escape') { |
| 11231 | const isEditVisible = $('#curEditTextarea').is(':visible'); | 11304 | const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0; |
| 11232 | if (isEditVisible && power_user.auto_save_msg_edits === false) { | 11305 | if (isEditVisible && power_user.auto_save_msg_edits === false) { |
| 11233 | closeMessageEditor(); | 11306 | closeMessageEditor('all'); |
| 11234 | $('#send_textarea').focus(); | 11307 | $('#send_textarea').focus(); |
| 11235 | return; | 11308 | return; |
| 11236 | } | 11309 | } |
| 11237 | if (isEditVisible && power_user.auto_save_msg_edits === true) { | 11310 | if (isEditVisible && power_user.auto_save_msg_edits === true) { |
| 11238 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click(); | 11311 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click(); |
| 11312 | closeMessageEditor('reasoning'); | ||
| 11239 | $('#send_textarea').focus(); | 11313 | $('#send_textarea').focus(); |
| 11240 | return; | 11314 | return; |
| 11241 | } | 11315 | } |
| @@ -188,7 +188,7 @@ export async function generateKoboldWithStreaming(generate_data, signal) { | |||
| 188 | if (data?.token) { | 188 | if (data?.token) { |
| 189 | text += data.token; | 189 | text += data.token; |
| 190 | } | 190 | } |
| 191 | yield { text, swipes: [], toolCalls: [] }; | 191 | yield { text, swipes: [], toolCalls: [], state: {} }; |
| 192 | } | 192 | } |
| 193 | }; | 193 | }; |
| 194 | } | 194 | } |
| @@ -746,7 +746,7 @@ export async function generateNovelWithStreaming(generate_data, signal) { | |||
| 746 | text += data.token; | 746 | text += data.token; |
| 747 | } | 747 | } |
| 748 | 748 | ||
| 749 | yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [] }; | 749 | yield { text, swipes: [], logprobs: parseNovelAILogprobs(data.logprobs), toolCalls: [], state: {} }; |
| 750 | } | 750 | } |
| 751 | }; | 751 | }; |
| 752 | } | 752 | } |
| @@ -2095,7 +2095,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2095 | let text = ''; | 2095 | let text = ''; |
| 2096 | const swipes = []; | 2096 | const swipes = []; |
| 2097 | const toolCalls = []; | 2097 | const toolCalls = []; |
| 2098 | const state = {}; | 2098 | const state = { reasoning: '' }; |
| 2099 | while (true) { | 2099 | while (true) { |
| 2100 | const { done, value } = await reader.read(); | 2100 | const { done, value } = await reader.read(); |
| 2101 | if (done) return; | 2101 | if (done) return; |
| @@ -2113,7 +2113,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2113 | 2113 | ||
| 2114 | ToolManager.parseToolCalls(toolCalls, parsed); | 2114 | ToolManager.parseToolCalls(toolCalls, parsed); |
| 2115 | 2115 | ||
| 2116 | yield { text, swipes: swipes, logprobs: parseChatCompletionLogprobs(parsed), toolCalls: toolCalls }; | 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 | if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { | 2150 | if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { |
| 2151 | return data?.delta?.text || ''; | 2151 | return data?.delta?.text || ''; |
| 2152 | } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { | 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 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { | 2157 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { |
| 2155 | return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || ''; | 2158 | return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || ''; |
| 2156 | } else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { | 2159 | } else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { |
| 2157 | const hadThoughts = state.hadThoughts; | 2160 | if (oai_settings.show_thoughts) { |
| 2158 | const thoughts = data.choices?.filter(x => oai_settings.show_thoughts || !x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''; | 2161 | state.reasoning += (data.choices?.filter(x => 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 | } else { | 2169 | } else { |
| 2164 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; | 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 | content: 'Write {{char}}\'s next reply in a fictional chat between {{char}} and {{user}}.', | 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 | personas: {}, | 264 | personas: {}, |
| 257 | default_persona: null, | 265 | default_persona: null, |
| 258 | persona_descriptions: {}, | 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 | } | ||
| @@ -235,6 +235,21 @@ async function* parseStreamData(json) { | |||
| 235 | } | 235 | } |
| 236 | return; | 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 | else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) { | 253 | else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) { |
| 239 | for (let j = 0; j < json.choices[0].delta.content.length; j++) { | 254 | for (let j = 0; j < json.choices[0].delta.content.length; j++) { |
| 240 | const str = json.choices[0].delta.content[j]; | 255 | const str = json.choices[0].delta.content[j]; |
| @@ -1,6 +1,7 @@ | |||
| 1 | import { | 1 | import { |
| 2 | activateSendButtons, | 2 | activateSendButtons, |
| 3 | addOneMessage, | 3 | addOneMessage, |
| 4 | appendMediaToMessage, | ||
| 4 | callPopup, | 5 | callPopup, |
| 5 | characters, | 6 | characters, |
| 6 | chat, | 7 | chat, |
| @@ -41,6 +42,7 @@ import { | |||
| 41 | substituteParamsExtended, | 42 | substituteParamsExtended, |
| 42 | this_chid, | 43 | this_chid, |
| 43 | updateChatMetadata, | 44 | updateChatMetadata, |
| 45 | updateMessageBlock, | ||
| 44 | } from '../script.js'; | 46 | } from '../script.js'; |
| 45 | import { | 47 | import { |
| 46 | extension_settings, | 48 | extension_settings, |
| @@ -171,6 +173,8 @@ export function getContext() { | |||
| 171 | getCharacters, | 173 | getCharacters, |
| 172 | uuidv4, | 174 | uuidv4, |
| 173 | humanizedDateTime, | 175 | humanizedDateTime, |
| 176 | updateMessageBlock, | ||
| 177 | appendMediaToMessage, | ||
| 174 | }; | 178 | }; |
| 175 | } | 179 | } |
| 176 | 180 | ||
| @@ -986,6 +986,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | |||
| 986 | let logprobs = null; | 986 | let logprobs = null; |
| 987 | const swipes = []; | 987 | const swipes = []; |
| 988 | const toolCalls = []; | 988 | const toolCalls = []; |
| 989 | const state = {}; | ||
| 989 | while (true) { | 990 | while (true) { |
| 990 | const { done, value } = await reader.read(); | 991 | const { done, value } = await reader.read(); |
| 991 | if (done) return; | 992 | if (done) return; |
| @@ -1004,7 +1005,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) { | |||
| 1004 | logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities); | 1005 | logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities); |
| 1005 | } | 1006 | } |
| 1006 | 1007 | ||
| 1007 | yield { text, swipes, logprobs, toolCalls }; | 1008 | yield { text, swipes, logprobs, toolCalls, state }; |
| 1008 | } | 1009 | } |
| 1009 | }; | 1010 | }; |
| 1010 | } | 1011 | } |
| @@ -292,36 +292,44 @@ input[type='checkbox']:focus-visible { | |||
| 292 | filter: grayscale(25%); | 292 | filter: grayscale(25%); |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | .mes_text table { | 295 | .mes_text table, |
| 296 | .mes_reasoning table { | ||
| 296 | border-spacing: 0; | 297 | border-spacing: 0; |
| 297 | border-collapse: collapse; | 298 | border-collapse: collapse; |
| 298 | margin-bottom: 10px; | 299 | margin-bottom: 10px; |
| 299 | } | 300 | } |
| 300 | 301 | ||
| 301 | .mes_text td, | 302 | .mes_text td, |
| 302 | .mes_text th { | 303 | .mes_text th, |
| 304 | .mes_reasoning td, | ||
| 305 | .mes_reasoning th { | ||
| 303 | border: 1px solid; | 306 | border: 1px solid; |
| 304 | border-collapse: collapse; | 307 | border-collapse: collapse; |
| 305 | padding: 0.25em; | 308 | padding: 0.25em; |
| 306 | } | 309 | } |
| 307 | 310 | ||
| 308 | .mes_text p { | 311 | .mes_text p, |
| 312 | .mes_reasoning p { | ||
| 309 | margin-top: 0; | 313 | margin-top: 0; |
| 310 | margin-bottom: 10px; | 314 | margin-bottom: 10px; |
| 311 | } | 315 | } |
| 312 | 316 | ||
| 313 | .mes_text li tt { | 317 | .mes_text li tt, |
| 318 | .mes_reasoning li tt { | ||
| 314 | display: inline-block; | 319 | display: inline-block; |
| 315 | } | 320 | } |
| 316 | 321 | ||
| 317 | .mes_text ol, | 322 | .mes_text ol, |
| 318 | .mes_text ul { | 323 | .mes_text ul, |
| 324 | .mes_reasoning ol, | ||
| 325 | .mes_reasoning ul { | ||
| 319 | margin-top: 5px; | 326 | margin-top: 5px; |
| 320 | margin-bottom: 5px; | 327 | margin-bottom: 5px; |
| 321 | } | 328 | } |
| 322 | 329 | ||
| 323 | .mes_text br, | 330 | .mes_text br, |
| 324 | .mes_bias br { | 331 | .mes_bias br, |
| 332 | .mes_reasoning br { | ||
| 325 | content: ' '; | 333 | content: ' '; |
| 326 | } | 334 | } |
| 327 | 335 | ||
| @@ -332,25 +340,83 @@ input[type='checkbox']:focus-visible { | |||
| 332 | color: var(--SmartThemeQuoteColor); | 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 | .mes_text i, | 395 | .mes_text i, |
| 336 | .mes_text em { | 396 | .mes_text em, |
| 397 | .mes_reasoning i, | ||
| 398 | .mes_reasoning em { | ||
| 337 | color: var(--SmartThemeEmColor); | 399 | color: var(--SmartThemeEmColor); |
| 338 | } | 400 | } |
| 339 | 401 | ||
| 340 | .mes_text u { | 402 | .mes_text u, |
| 403 | .mes_reasoning u { | ||
| 341 | color: var(--SmartThemeUnderlineColor); | 404 | color: var(--SmartThemeUnderlineColor); |
| 342 | } | 405 | } |
| 343 | 406 | ||
| 344 | .mes_text q { | 407 | .mes_text q, |
| 408 | .mes_reasoning q { | ||
| 345 | color: var(--SmartThemeQuoteColor); | 409 | color: var(--SmartThemeQuoteColor); |
| 346 | } | 410 | } |
| 347 | 411 | ||
| 348 | .mes_text font[color] em, | 412 | .mes_text font[color] em, |
| 349 | .mes_text font[color] i { | 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 | .mes_text font[color] q { | 417 | .mes_reasoning font[color] i, |
| 418 | .mes_reasoning font[color] u, | ||
| 419 | .mes_reasoning font[color] q { | ||
| 354 | color: inherit; | 420 | color: inherit; |
| 355 | } | 421 | } |
| 356 | 422 | ||
| @@ -358,7 +424,8 @@ input[type='checkbox']:focus-visible { | |||
| 358 | display: block; | 424 | display: block; |
| 359 | } | 425 | } |
| 360 | 426 | ||
| 361 | .mes_text blockquote { | 427 | .mes_text blockquote, |
| 428 | .mes_reasoning blockquote { | ||
| 362 | border-left: 3px solid var(--SmartThemeQuoteColor); | 429 | border-left: 3px solid var(--SmartThemeQuoteColor); |
| 363 | padding-left: 10px; | 430 | padding-left: 10px; |
| 364 | background-color: var(--black30a); | 431 | background-color: var(--black30a); |
| @@ -368,18 +435,24 @@ input[type='checkbox']:focus-visible { | |||
| 368 | .mes_text strong em, | 435 | .mes_text strong em, |
| 369 | .mes_text strong, | 436 | .mes_text strong, |
| 370 | .mes_text h2, | 437 | .mes_text h2, |
| 371 | .mes_text h1 { | 438 | .mes_text h1, |
| 439 | .mes_reasoning strong em, | ||
| 440 | .mes_reasoning strong, | ||
| 441 | .mes_reasoning h2, | ||
| 442 | .mes_reasoning h1 { | ||
| 372 | font-weight: bold; | 443 | font-weight: bold; |
| 373 | } | 444 | } |
| 374 | 445 | ||
| 375 | .mes_text pre code { | 446 | .mes_text pre code, |
| 447 | .mes_reasoning pre code { | ||
| 376 | position: relative; | 448 | position: relative; |
| 377 | display: block; | 449 | display: block; |
| 378 | overflow-x: auto; | 450 | overflow-x: auto; |
| 379 | padding: 1em; | 451 | padding: 1em; |
| 380 | } | 452 | } |
| 381 | 453 | ||
| 382 | .mes_text img:not(.mes_img) { | 454 | .mes_text img:not(.mes_img), |
| 455 | .mes_reasoning img:not(.mes_img) { | ||
| 383 | max-width: 100%; | 456 | max-width: 100%; |
| 384 | max-height: var(--doc-height); | 457 | max-height: var(--doc-height); |
| 385 | } | 458 | } |
| @@ -1022,6 +1095,11 @@ body .panelControlBar { | |||
| 1022 | /*only affects bubblechat to make it sit nicely at the bottom*/ | 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 | .last_mes .mes_text { | 1103 | .last_mes .mes_text { |
| 1026 | padding-right: 30px; | 1104 | padding-right: 30px; |
| 1027 | } | 1105 | } |
| @@ -1235,14 +1313,18 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter { | |||
| 1235 | overflow-y: clip; | 1313 | overflow-y: clip; |
| 1236 | } | 1314 | } |
| 1237 | 1315 | ||
| 1238 | .mes_text { | 1316 | .mes_text, |
| 1317 | .mes_reasoning { | ||
| 1239 | font-weight: 500; | 1318 | font-weight: 500; |
| 1240 | line-height: calc(var(--mainFontSize) + .5rem); | 1319 | line-height: calc(var(--mainFontSize) + .5rem); |
| 1320 | max-width: 100%; | ||
| 1321 | overflow-wrap: anywhere; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | .mes_text { | ||
| 1241 | padding-left: 0; | 1325 | padding-left: 0; |
| 1242 | padding-top: 5px; | 1326 | padding-top: 5px; |
| 1243 | padding-bottom: 5px; | 1327 | padding-bottom: 5px; |
| 1244 | max-width: 100%; | ||
| 1245 | overflow-wrap: anywhere; | ||
| 1246 | } | 1328 | } |
| 1247 | 1329 | ||
| 1248 | br { | 1330 | br { |
| @@ -4150,10 +4232,12 @@ input[type="range"]::-webkit-slider-thumb { | |||
| 4150 | align-items: center; | 4232 | align-items: center; |
| 4151 | } | 4233 | } |
| 4152 | 4234 | ||
| 4235 | .mes_reasoning_edit_cancel, | ||
| 4153 | .mes_edit_cancel.menu_button { | 4236 | .mes_edit_cancel.menu_button { |
| 4154 | background-color: var(--crimson70a); | 4237 | background-color: var(--crimson70a); |
| 4155 | } | 4238 | } |
| 4156 | 4239 | ||
| 4240 | .mes_reasoning_edit_done, | ||
| 4157 | .mes_edit_done.menu_button { | 4241 | .mes_edit_done.menu_button { |
| 4158 | background-color: var(--okGreen70a); | 4242 | background-color: var(--okGreen70a); |
| 4159 | } | 4243 | } |
| @@ -4162,6 +4246,7 @@ input[type="range"]::-webkit-slider-thumb { | |||
| 4162 | opacity: 1; | 4246 | opacity: 1; |
| 4163 | } | 4247 | } |
| 4164 | 4248 | ||
| 4249 | .reasoning_edit_textarea, | ||
| 4165 | .edit_textarea { | 4250 | .edit_textarea { |
| 4166 | padding: 5px; | 4251 | padding: 5px; |
| 4167 | margin: 0; | 4252 | margin: 0; |
| @@ -289,6 +289,7 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 289 | const model = String(request.body.model); | 289 | const model = String(request.body.model); |
| 290 | const stream = Boolean(request.body.stream); | 290 | const stream = Boolean(request.body.stream); |
| 291 | const showThoughts = Boolean(request.body.show_thoughts); | 291 | const showThoughts = Boolean(request.body.show_thoughts); |
| 292 | const isThinking = model.includes('thinking'); | ||
| 292 | 293 | ||
| 293 | const generationConfig = { | 294 | const generationConfig = { |
| 294 | stopSequences: request.body.stop, | 295 | stopSequences: request.body.stop, |
| @@ -329,6 +330,12 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 329 | body.systemInstruction = prompt.system_instruction; | 330 | body.systemInstruction = prompt.system_instruction; |
| 330 | } | 331 | } |
| 331 | 332 | ||
| 333 | if (isThinking && showThoughts) { | ||
| 334 | generationConfig.thinkingConfig = { | ||
| 335 | includeThoughts: true, | ||
| 336 | }; | ||
| 337 | } | ||
| 338 | |||
| 332 | return body; | 339 | return body; |
| 333 | } | 340 | } |
| 334 | 341 | ||
| @@ -342,7 +349,6 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 342 | controller.abort(); | 349 | controller.abort(); |
| 343 | }); | 350 | }); |
| 344 | 351 | ||
| 345 | const isThinking = model.includes('thinking'); | ||
| 346 | const apiVersion = isThinking ? 'v1alpha' : 'v1beta'; | 352 | const apiVersion = isThinking ? 'v1alpha' : 'v1beta'; |
| 347 | const responseType = (stream ? 'streamGenerateContent' : 'generateContent'); | 353 | const responseType = (stream ? 'streamGenerateContent' : 'generateContent'); |
| 348 | 354 | ||
| @@ -387,11 +393,7 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 387 | const responseContent = candidates[0].content ?? candidates[0].output; | 393 | const responseContent = candidates[0].content ?? candidates[0].output; |
| 388 | console.log('Google AI Studio response:', responseContent); | 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 | if (!responseText) { | 397 | if (!responseText) { |
| 396 | let message = 'Google AI Studio Candidate text empty'; | 398 | let message = 'Google AI Studio Candidate text empty'; |
| 397 | console.log(message, generateResponseJson); | 399 | console.log(message, generateResponseJson); |
| @@ -399,7 +401,7 @@ async function sendMakerSuiteRequest(request, response) { | |||
| 399 | } | 401 | } |
| 400 | 402 | ||
| 401 | // Wrap it back to OAI format | 403 | // Wrap it back to OAI format |
| 402 | const reply = { choices: [{ 'message': { 'content': responseText } }] }; | 404 | const reply = { choices: [{ 'message': { 'content': responseText } }], responseContent }; |
| 403 | return response.send(reply); | 405 | return response.send(reply); |
| 404 | } | 406 | } |
| 405 | } catch (error) { | 407 | } catch (error) { |
| @@ -996,6 +998,10 @@ router.post('/generate', jsonParser, function (request, response) { | |||
| 996 | bodyParams['route'] = 'fallback'; | 998 | bodyParams['route'] = 'fallback'; |
| 997 | } | 999 | } |
| 998 | 1000 | ||
| 1001 | if (request.body.show_thoughts) { | ||
| 1002 | bodyParams['include_reasoning'] = true; | ||
| 1003 | } | ||
| 1004 | |||
| 999 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); | 1005 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); |
| 1000 | if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) { | 1006 | if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) { |
| 1001 | cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth); | 1007 | cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth); |