Merge pull request #3345 from SillyTavern/woo-yeah Better reasoning
Signed| @@ -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> |
| @@ -6221,14 +6254,26 @@ | ||
| 6221 | 6254 | <div class="mes_edit_buttons"> |
| 6222 | 6255 | <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div> |
| 6223 | 6256 | <div class="mes_edit_copy menu_button fa-solid fa-copy" title="Copy this message" data-i18n="[title]Copy this message"></div> |
| 6224 | 6257 | <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> |
| 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 | 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 | 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 | 6261 | <div class="mes_edit_cancel menu_button fa-solid fa-xmark" title="Cancel" data-i18n="[title]Cancel"></div> |
| 6230 | 6262 | </div> |
| 6231 | 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 | 6277 | <div class="mes_text"></div> |
| 6233 | 6278 | <div class="mes_img_container"> |
| 6234 | 6279 | <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(可選)", |
| @@ -267,6 +267,7 @@ import { initSettingsSearch } from './scripts/setting-search.js'; | ||
| 267 | 267 | import { initBulkEdit } from './scripts/bulk-edit.js'; |
| 268 | 268 | import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js'; |
| 269 | 269 | import { getContext } from './scripts/st-context.js'; |
| 270 | +import { initReasoning, PromptReasoning } from './scripts/reasoning.js'; | |
| 270 | 271 | |
| 271 | 272 | // API OBJECT FOR EXTERNAL WIRING |
| 272 | 273 | globalThis.SillyTavern = { |
| @@ -982,6 +983,7 @@ async function firstLoadInit() { | ||
| 982 | 983 | initServerHistory(); |
| 983 | 984 | initSettingsSearch(); |
| 984 | 985 | initBulkEdit(); |
| 986 | + initReasoning(); | |
| 985 | 987 | await initScrapers(); |
| 986 | 988 | doDailyExtensionUpdatesCheck(); |
| 987 | 989 | await hideLoader(); |
| @@ -2200,6 +2202,7 @@ function getMessageFromTemplate({ | ||
| 2200 | 2202 | isUser, |
| 2201 | 2203 | avatarImg, |
| 2202 | 2204 | bias, |
| 2205 | + reasoning, | |
| 2203 | 2206 | isSystem, |
| 2204 | 2207 | title, |
| 2205 | 2208 | timerValue, |
| @@ -2224,6 +2227,7 @@ function getMessageFromTemplate({ | ||
| 2224 | 2227 | mes.find('.avatar img').attr('src', avatarImg); |
| 2225 | 2228 | mes.find('.ch_name .name_text').text(characterName); |
| 2226 | 2229 | mes.find('.mes_bias').html(bias); |
| 2230 | + mes.find('.mes_reasoning').html(reasoning); | |
| 2227 | 2231 | mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`); |
| 2228 | 2232 | mes.find('.mesIDDisplay').text(`#${mesId}`); |
| 2229 | 2233 | tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| @@ -2238,10 +2242,16 @@ function getMessageFromTemplate({ | ||
| 2238 | 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 | 2250 | export function updateMessageBlock(messageId, message) { |
| 2242 | 2251 | const messageElement = $(`#chat [mesid="${messageId}"]`); |
| 2243 | 2252 | const text = message?.extra?.display_text ?? message.mes; |
| 2244 | 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 | 2255 | addCopyToCodeBlocks(messageElement); |
| 2246 | 2256 | appendMediaToMessage(message, messageElement); |
| 2247 | 2257 | } |
| @@ -2400,6 +2410,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2400 | 2410 | sanitizerOverrides, |
| 2401 | 2411 | ); |
| 2402 | 2412 | const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1); |
| 2413 | + const reasoning = messageFormatting(mes.extra?.reasoning ?? '', '', false, false, -1); | |
| 2403 | 2414 | let bookmarkLink = mes?.extra?.bookmark_link ?? ''; |
| 2404 | 2415 | |
| 2405 | 2416 | let params = { |
| @@ -2409,6 +2420,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2409 | 2420 | isUser: mes.is_user, |
| 2410 | 2421 | avatarImg: avatarImg, |
| 2411 | 2422 | bias: bias, |
| 2423 | + reasoning: reasoning, | |
| 2412 | 2424 | isSystem: isSystem, |
| 2413 | 2425 | title: title, |
| 2414 | 2426 | bookmarkLink: bookmarkLink, |
| @@ -2468,6 +2480,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2468 | 2480 | const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`); |
| 2469 | 2481 | swipeMessage.attr('swipeid', params.swipeId); |
| 2470 | 2482 | swipeMessage.find('.mes_text').html(messageText).attr('title', title); |
| 2483 | + swipeMessage.find('.mes_reasoning').html(reasoning); | |
| 2471 | 2484 | swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`); |
| 2472 | 2485 | appendMediaToMessage(mes, swipeMessage); |
| 2473 | 2486 | if (power_user.timestamp_model_icon && params.extra?.api) { |
| @@ -3078,6 +3091,7 @@ class StreamingProcessor { | ||
| 3078 | 3091 | this.messageTextDom = null; |
| 3079 | 3092 | this.messageTimerDom = null; |
| 3080 | 3093 | this.messageTokenCounterDom = null; |
| 3094 | + this.messageReasoningDom = null; | |
| 3081 | 3095 | /** @type {HTMLTextAreaElement} */ |
| 3082 | 3096 | this.sendTextarea = document.querySelector('#send_textarea'); |
| 3083 | 3097 | this.type = type; |
| @@ -3093,6 +3107,7 @@ class StreamingProcessor { | ||
| 3093 | 3107 | /** @type {import('./scripts/logprobs.js').TokenLogprobs[]} */ |
| 3094 | 3108 | this.messageLogprobs = []; |
| 3095 | 3109 | this.toolCalls = []; |
| 3110 | + this.reasoning = ''; | |
| 3096 | 3111 | } |
| 3097 | 3112 | |
| 3098 | 3113 | #checkDomElements(messageId) { |
| @@ -3101,6 +3116,7 @@ class StreamingProcessor { | ||
| 3101 | 3116 | this.messageTextDom = this.messageDom?.querySelector('.mes_text'); |
| 3102 | 3117 | this.messageTimerDom = this.messageDom?.querySelector('.mes_timer'); |
| 3103 | 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 | 3194 | this.#checkDomElements(messageId); |
| 3179 | 3195 | this.#updateMessageBlockVisibility(); |
| 3180 | 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 | 3197 | chat[messageId]['mes'] = processedText; |
| 3185 | 3198 | chat[messageId]['gen_started'] = this.timeStarted; |
| 3186 | 3199 | chat[messageId]['gen_finished'] = currentTime; |
| 3187 | 3200 | |
| 3188 | 3201 | if (currentTokenCount!chat[messageId]['extra']) { |
| 3189 | 3202 | if (!chat[messageId]['extra']) = {}; |
| 3190 | - chat[messageId]['extra'] = {}; | |
| 3203 | + } | |
| 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; | |
| 3191 | 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; | |
| 3192 | 3216 | |
| 3217 | + if (currentTokenCount) { | |
| 3193 | 3218 | chat[messageId]['extra']['token_count'] = currentTokenCount; |
| 3194 | 3219 | if (this.messageTokenCounterDom instanceof HTMLElement) { |
| 3195 | 3220 | this.messageTokenCounterDom.textContent = `${currentTokenCount}t`; |
| @@ -3211,10 +3236,13 @@ class StreamingProcessor { | ||
| 3211 | 3236 | if (this.messageTextDom instanceof HTMLElement) { |
| 3212 | 3237 | this.messageTextDom.innerHTML = formattedText; |
| 3213 | 3238 | } |
| 3239 | + | |
| 3240 | + const timePassed = formatGenerationTimer(this.timeStarted, currentTime, currentTokenCount); | |
| 3214 | 3241 | if (this.messageTimerDom instanceof HTMLElement) { |
| 3215 | 3242 | this.messageTimerDom.textContent = timePassed.timerValue; |
| 3216 | 3243 | this.messageTimerDom.title = timePassed.timerTitle; |
| 3217 | 3244 | } |
| 3245 | + | |
| 3218 | 3246 | this.setFirstSwipe(messageId); |
| 3219 | 3247 | } |
| 3220 | 3248 | |
| @@ -3321,7 +3349,7 @@ class StreamingProcessor { | ||
| 3321 | 3349 | } |
| 3322 | 3350 | |
| 3323 | 3351 | /** |
| 3324 | 3352 | * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>} |
| 3325 | 3353 | */ |
| 3326 | 3354 | *nullStreamingGeneration() { |
| 3327 | 3355 | throw new Error('Generation function for streaming is not hooked up'); |
| @@ -3343,7 +3371,7 @@ class StreamingProcessor { | ||
| 3343 | 3371 | try { |
| 3344 | 3372 | const sw = new Stopwatch(1000 / power_user.streaming_fps); |
| 3345 | 3373 | const timestamps = []; |
| 3346 | 3374 | for await (const { text, swipes, logprobs, toolCalls, state } of this.generator()) { |
| 3347 | 3375 | timestamps.push(Date.now()); |
| 3348 | 3376 | if (this.isStopped) { |
| 3349 | 3377 | return; |
| @@ -3355,6 +3383,7 @@ class StreamingProcessor { | ||
| 3355 | 3383 | if (logprobs) { |
| 3356 | 3384 | this.messageLogprobs.push(...(Array.isArray(logprobs) ? logprobs : [logprobs])); |
| 3357 | 3385 | } |
| 3386 | + this.reasoning = state?.reasoning ?? ''; | |
| 3358 | 3387 | await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text); |
| 3359 | 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 | 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 | 3861 | coreChat = await Promise.all(coreChat.map(async (chatItem, index) => { |
| 3825 | 3862 | let message = chatItem.mes; |
| 3826 | 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 | 4779 | //const getData = await response.json(); |
| 4743 | 4780 | let getMessage = extractMessageFromData(data); |
| 4744 | 4781 | let title = extractTitleFromData(data); |
| 4782 | + let reasoning = extractReasoningFromData(data); | |
| 4745 | 4783 | kobold_horde_model = title; |
| 4746 | 4784 | |
| 4747 | 4785 | const swipes = extractMultiSwipes(data, type); |
| @@ -4768,10 +4806,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4768 | 4806 | else { |
| 4769 | 4807 | // Without streaming we'll be having a full message on continuation. Treat it as a last chunk. |
| 4770 | 4808 | if (originalType !== 'continue') { |
| 4771 | 4809 | ({ type, getMessage } = await saveReply(type, getMessage, false, title, swipes, reasoning)); |
| 4772 | 4810 | } |
| 4773 | 4811 | else { |
| 4774 | 4812 | ({ type, getMessage } = await saveReply('appendFinal', getMessage, false, title, swipes, reasoning)); |
| 4775 | 4813 | } |
| 4776 | 4814 | |
| 4777 | 4815 | // This relies on `saveReply` having been called to add the message to the chat, so it must be last. |
| @@ -5659,31 +5697,40 @@ function extractMessageFromData(data) { | ||
| 5659 | 5697 | return data; |
| 5660 | 5698 | } |
| 5661 | 5699 | |
| 5662 | 5700 | functionswitch getTextContext(main_api) { |
| 5663 | - switch (main_api) { | |
| 5701 | + case 'kobold': | |
| 5664 | - case 'kobold': | |
| 5702 | + return data.results[0].text; | |
| 5665 | - return data.results[0].text; | |
| 5703 | + case 'koboldhorde': | |
| 5666 | - case 'koboldhorde': | |
| 5704 | + return data.text; | |
| 5667 | - return data.text; | |
| 5705 | + case 'textgenerationwebui': | |
| 5668 | - case 'textgenerationwebui': | |
| 5706 | + return data.choices?.[0]?.text ?? data.content ?? data.response ?? ''; | |
| 5669 | - return data.choices?.[0]?.text ?? data.content ?? data.response ?? ''; | |
| 5707 | + case 'novel': | |
| 5670 | - case 'novel': | |
| 5708 | + return data.output; | |
| 5671 | - return data.output; | |
| 5709 | + case 'openai': | |
| 5672 | - case 'openai': | |
| 5710 | + return data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? data?.message?.content?.[0]?.text ?? data?.message?.tool_plan ?? ''; | |
| 5673 | - return data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? data?.message?.content?.[0]?.text ?? data?.message?.tool_plan ?? ''; | |
| 5711 | + default: | |
| 5674 | - default: | |
| 5712 | + return ''; | |
| 5675 | - return ''; | |
| 5676 | - } | |
| 5677 | 5713 | } |
| 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 | 5733 | return content''; |
| 5687 | 5734 | } |
| 5688 | 5735 | |
| 5689 | 5736 | /** |
| @@ -5866,7 +5913,7 @@ export function cleanUpMessage(getMessage, isImpersonate, isContinue, displayInc | ||
| 5866 | 5913 | return getMessage; |
| 5867 | 5914 | } |
| 5868 | 5915 | |
| 5869 | 5916 | export async function saveReply(type, getMessage, fromStreaming, title, swipes, reasoning) { |
| 5870 | 5917 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined || |
| 5871 | 5918 | chat[chat.length - 1]['is_user'])) { |
| 5872 | 5919 | type = 'normal'; |
| @@ -5891,8 +5938,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5891 | 5938 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5892 | 5939 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5893 | 5940 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5941 | + chat[chat.length - 1]['extra']['reasoning'] = reasoning; | |
| 5894 | 5942 | if (power_user.message_token_count_enabled) { |
| 5895 | 5943 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 5944 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5896 | 5945 | } |
| 5897 | 5946 | const chat_id = (chat.length - 1); |
| 5898 | 5947 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5911,8 +5960,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5911 | 5960 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5912 | 5961 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5913 | 5962 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5963 | + chat[chat.length - 1]['extra']['reasoning'] += reasoning; | |
| 5914 | 5964 | if (power_user.message_token_count_enabled) { |
| 5915 | 5965 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 5966 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5916 | 5967 | } |
| 5917 | 5968 | const chat_id = (chat.length - 1); |
| 5918 | 5969 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5928,8 +5979,10 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5928 | 5979 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5929 | 5980 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5930 | 5981 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 5982 | + chat[chat.length - 1]['extra']['reasoning'] += reasoning; | |
| 5931 | 5983 | if (power_user.message_token_count_enabled) { |
| 5932 | 5984 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 5985 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5933 | 5986 | } |
| 5934 | 5987 | const chat_id = (chat.length - 1); |
| 5935 | 5988 | await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id); |
| @@ -5945,6 +5998,7 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5945 | 5998 | chat[chat.length - 1]['send_date'] = getMessageTimeStamp(); |
| 5946 | 5999 | chat[chat.length - 1]['extra']['api'] = getGeneratingApi(); |
| 5947 | 6000 | chat[chat.length - 1]['extra']['model'] = getGeneratingModel(); |
| 6001 | + chat[chat.length - 1]['extra']['reasoning'] = reasoning; | |
| 5948 | 6002 | if (power_user.trim_spaces) { |
| 5949 | 6003 | getMessage = getMessage.trim(); |
| 5950 | 6004 | } |
| @@ -5954,7 +6008,8 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes) | ||
| 5954 | 6008 | chat[chat.length - 1]['gen_finished'] = generationFinished; |
| 5955 | 6009 | |
| 5956 | 6010 | if (power_user.message_token_count_enabled) { |
| 5957 | 6011 | chat[chat.lengthconst -tokenCountText 1]['extra'][= (reasoning || 'token_count'] =) await+ getTokenCountAsync(chat[chat.length - 1]['mes'], 0); |
| 6012 | + chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0); | |
| 5958 | 6013 | } |
| 5959 | 6014 | |
| 5960 | 6015 | if (selected_group) { |
| @@ -7997,9 +8052,23 @@ function updateEditArrowClasses() { | ||
| 7997 | 8052 | } |
| 7998 | 8053 | } |
| 7999 | 8054 | |
| 8000 | -function closeMessageEditor() { | |
| 8055 | +/** | |
| 8001 | - if (this_edit_mes_id) { | |
| 8056 | + * Closes the message editor. | |
| 8002 | - $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click(); | |
| 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') { | |
| 8061 | + if (this_edit_mes_id) { | |
| 8062 | + $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).click(); | |
| 8063 | + } | |
| 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 | + }); | |
| 8003 | 8072 | } |
| 8004 | 8073 | } |
| 8005 | 8074 | |
| @@ -8483,7 +8552,8 @@ function swipe_left() { // when we swipe left..but no generation. | ||
| 8483 | 8552 | } |
| 8484 | 8553 | |
| 8485 | 8554 | const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`); |
| 8486 | 8555 | const tokenCounttokenCountText = await getTokenCountAsync(chat[chat.length - 1]?.mes,extra?.reasoning 0|| '') + chat[chat.length - 1].mes; |
| 8556 | + const tokenCount = await getTokenCountAsync(tokenCountText, 0); | |
| 8487 | 8557 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8488 | 8558 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8489 | 8559 | } |
| @@ -8647,6 +8717,7 @@ const swipe_right = () => { | ||
| 8647 | 8717 | // resets the timer |
| 8648 | 8718 | swipeMessage.find('.mes_timer').html(''); |
| 8649 | 8719 | swipeMessage.find('.tokenCounterDisplay').text(''); |
| 8720 | + swipeMessage.find('.mes_reasoning').html(''); | |
| 8650 | 8721 | } else { |
| 8651 | 8722 | //console.log('showing previously generated swipe candidate, or "..."'); |
| 8652 | 8723 | //console.log('onclick right swipe calling addOneMessage'); |
| @@ -8657,7 +8728,8 @@ const swipe_right = () => { | ||
| 8657 | 8728 | chat[chat.length - 1].extra = {}; |
| 8658 | 8729 | } |
| 8659 | 8730 | |
| 8660 | 8731 | const tokenCounttokenCountText = await getTokenCountAsync(chat[chat.length - 1]?.mes,extra?.reasoning 0|| '') + chat[chat.length - 1].mes; |
| 8732 | + const tokenCount = await getTokenCountAsync(tokenCountText, 0); | |
| 8661 | 8733 | chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8662 | 8734 | swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8663 | 8735 | } |
| @@ -9459,7 +9531,8 @@ function addDebugFunctions() { | ||
| 9459 | 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 | 9538 | await saveChatConditional(); |
| @@ -11228,14 +11301,15 @@ jQuery(async function () { | ||
| 11228 | 11301 | |
| 11229 | 11302 | $(document).keyup(function (e) { |
| 11230 | 11303 | if (e.key === 'Escape') { |
| 11231 | 11304 | const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0; |
| 11232 | 11305 | if (isEditVisible && power_user.auto_save_msg_edits === false) { |
| 11233 | 11306 | closeMessageEditor('all'); |
| 11234 | 11307 | $('#send_textarea').focus(); |
| 11235 | 11308 | return; |
| 11236 | 11309 | } |
| 11237 | 11310 | if (isEditVisible && power_user.auto_save_msg_edits === true) { |
| 11238 | 11311 | $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).click(); |
| 11312 | + closeMessageEditor('reasoning'); | |
| 11239 | 11313 | $('#send_textarea').focus(); |
| 11240 | 11314 | return; |
| 11241 | 11315 | } |
| @@ -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 | } |
| @@ -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 | +} | |
| @@ -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 | } |
| @@ -1235,14 +1313,18 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter { | ||
| 1235 | 1313 | overflow-y: clip; |
| 1236 | 1314 | } |
| 1237 | 1315 | |
| 1238 | 1316 | .mes_text {, |
| 1317 | +.mes_reasoning { | |
| 1239 | 1318 | font-weight: 500; |
| 1240 | 1319 | line-height: calc(var(--mainFontSize) + .5rem); |
| 1320 | + max-width: 100%; | |
| 1321 | + overflow-wrap: anywhere; | |
| 1322 | +} | |
| 1323 | + | |
| 1324 | +.mes_text { | |
| 1241 | 1325 | padding-left: 0; |
| 1242 | 1326 | padding-top: 5px; |
| 1243 | 1327 | padding-bottom: 5px; |
| 1244 | - max-width: 100%; | |
| 1245 | - overflow-wrap: anywhere; | |
| 1246 | 1328 | } |
| 1247 | 1329 | |
| 1248 | 1330 | br { |
| @@ -4150,10 +4232,12 @@ input[type="range"]::-webkit-slider-thumb { | ||
| 4150 | 4232 | align-items: center; |
| 4151 | 4233 | } |
| 4152 | 4234 | |
| 4235 | +.mes_reasoning_edit_cancel, | |
| 4153 | 4236 | .mes_edit_cancel.menu_button { |
| 4154 | 4237 | background-color: var(--crimson70a); |
| 4155 | 4238 | } |
| 4156 | 4239 | |
| 4240 | +.mes_reasoning_edit_done, | |
| 4157 | 4241 | .mes_edit_done.menu_button { |
| 4158 | 4242 | background-color: var(--okGreen70a); |
| 4159 | 4243 | } |
| @@ -4162,6 +4246,7 @@ input[type="range"]::-webkit-slider-thumb { | ||
| 4162 | 4246 | opacity: 1; |
| 4163 | 4247 | } |
| 4164 | 4248 | |
| 4249 | +.reasoning_edit_textarea, | |
| 4165 | 4250 | .edit_textarea { |
| 4166 | 4251 | padding: 5px; |
| 4167 | 4252 | 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); |