Merge pull request #3396 from pcpthm/text-completion-include-reasoning Support reasoning for OpenRouter Text Completion

a969d34c0364e294b4f8bedfccc275284a8cc9d9

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
7 files changed, +39 -17Ignore whitespace
public/index.html+4 -0
@@ -1587,6 +1587,10 @@
15871587 <input type="checkbox" id="skip_special_tokens_textgenerationwebui" />
15881588 <small data-i18n="Skip Special Tokens">Skip Special Tokens</small>
15891589 </label>
1590+ <label data-tg-type="openrouter" class="checkbox_label flexGrow flexShrink" for="include_reasoning_textgenerationwebui">
1591+ <input type="checkbox" id="include_reasoning_textgenerationwebui" />
1592+ <small data-i18n="Request Model Reasoning">Request Model Reasoning</small>
1593+ </label>
15901594 <label data-tg-type="ooba, aphrodite, tabby" class="checkbox_label flexGrow flexShrink" for="temperature_last_textgenerationwebui">
15911595 <input type="checkbox" id="temperature_last_textgenerationwebui" />
15921596 <label>
public/script.js+20 -9
@@ -5706,15 +5706,26 @@ function extractMessageFromData(data) {
57065706 * @returns {string} Extracted reasoning
57075707 */
57085708function extractReasoningFromData(data) {
5709- if (main_api === 'openai' && oai_settings.show_thoughts) {
5709+ switch (main_api) {
5710- switch (oai_settings.chat_completion_source) {
5710+ case 'textgenerationwebui':
5711- case chat_completion_sources.DEEPSEEK:
5711+ switch (textgen_settings.type) {
5712- return data?.choices?.[0]?.message?.reasoning_content ?? '';
5712+ case textgen_types.OPENROUTER:
5713- case chat_completion_sources.OPENROUTER:
5713+ return data?.choices?.[0]?.reasoning ?? '';
5714- return data?.choices?.[0]?.message?.reasoning ?? '';
5714+ }
5715- case chat_completion_sources.MAKERSUITE:
5715+ break;
5716- return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
5716+
5717- }
5717+ case 'openai':
5718+ if (!oai_settings.show_thoughts) break;
5719+
5720+ switch (oai_settings.chat_completion_source) {
5721+ case chat_completion_sources.DEEPSEEK:
5722+ return data?.choices?.[0]?.message?.reasoning_content ?? '';
5723+ case chat_completion_sources.OPENROUTER:
5724+ return data?.choices?.[0]?.message?.reasoning ?? '';
5725+ case chat_completion_sources.MAKERSUITE:
5726+ return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
5727+ }
5728+ break;
57185729 }
57195730
57205731 return '';
public/scripts/openai.js+5 -5
@@ -298,7 +298,7 @@ const default_settings = {
298298 names_behavior: character_names_behavior.DEFAULT,
299299 continue_postfix: continue_postfix_types.SPACE,
300300 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
301301 show_thoughts: falsetrue,
302302 seed: -1,
303303 n: 1,
304304};
@@ -377,7 +377,7 @@ const oai_settings = {
377377 names_behavior: character_names_behavior.DEFAULT,
378378 continue_postfix: continue_postfix_types.SPACE,
379379 custom_prompt_post_processing: custom_prompt_post_processing_types.NONE,
380380 show_thoughts: falsetrue,
381381 seed: -1,
382382 n: 1,
383383};
@@ -1913,7 +1913,7 @@ async function sendOpenAIRequest(type, messages, signal) {
19131913 'user_name': name1,
19141914 'char_name': name2,
19151915 'group_names': getGroupNames(),
19161916 'show_thoughtsinclude_reasoning': Boolean(oai_settings.show_thoughts),
19171917 };
19181918
19191919 // Empty array will produce a validation error
@@ -2151,7 +2151,7 @@ function getStreamingReply(data, state) {
21512151 return data?.delta?.text || '';
21522152 } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
21532153 if (oai_settings.show_thoughts) {
21542154 state.reasoning += (data?.candidates?.[0]?.content?.parts?.filter(x => x.thought)?.map(x => x.text)?.[0] || '');
21552155 }
21562156 return data?.candidates?.[0]?.content?.parts?.filter(x => !x.thought)?.map(x => x.text)?.[0] || '';
21572157 } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) {
@@ -2166,7 +2166,7 @@ function getStreamingReply(data, state) {
21662166 state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');
21672167 }
21682168 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
21692169 } else {
21702170 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
21712171 }
21722172}
public/scripts/preset-manager.js+1 -0
@@ -586,6 +586,7 @@ class PresetManager {
586586 'tabby_model',
587587 'derived',
588588 'generic_model',
589+ 'include_reasoning',
589590 ];
590591 const settings = Object.assign({}, getSettingsByApiId(this.apiId));
591592
public/scripts/textgen-settings.js+6 -1
@@ -172,6 +172,7 @@ const settings = {
172172 //truncation_length: 2048,
173173 ban_eos_token: false,
174174 skip_special_tokens: true,
175+ include_reasoning: true,
175176 streaming: false,
176177 mirostat_mode: 0,
177178 mirostat_tau: 5,
@@ -263,6 +264,7 @@ export const setting_names = [
263264 'add_bos_token',
264265 'ban_eos_token',
265266 'skip_special_tokens',
267+ 'include_reasoning',
266268 'streaming',
267269 'mirostat_mode',
268270 'mirostat_tau',
@@ -740,6 +742,7 @@ jQuery(function () {
740742 'add_bos_token_textgenerationwebui': true,
741743 'temperature_last_textgenerationwebui': true,
742744 'skip_special_tokens_textgenerationwebui': true,
745+ 'include_reasoning_textgenerationwebui': true,
743746 'top_a_textgenerationwebui': 0,
744747 'top_a_counter_textgenerationwebui': 0,
745748 'mirostat_mode_textgenerationwebui': 0,
@@ -986,7 +989,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) {
986989 let logprobs = null;
987990 const swipes = [];
988991 const toolCalls = [];
989992 const state = { reasoning: '' };
990993 while (true) {
991994 const { done, value } = await reader.read();
992995 if (done) return;
@@ -1003,6 +1006,7 @@ export async function generateTextGenWithStreaming(generate_data, signal) {
10031006 const newText = data?.choices?.[0]?.text || data?.content || '';
10041007 text += newText;
10051008 logprobs = parseTextgenLogprobs(newText, data.choices?.[0]?.logprobs || data?.completion_probabilities);
1009+ state.reasoning += data?.choices?.[0]?.reasoning ?? '';
10061010 }
10071011
10081012 yield { text, swipes, logprobs, toolCalls, state };
@@ -1266,6 +1270,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
12661270 'truncation_length': max_context,
12671271 'ban_eos_token': settings.ban_eos_token,
12681272 'skip_special_tokens': settings.skip_special_tokens,
1273+ 'include_reasoning': settings.include_reasoning,
12691274 'top_a': settings.top_a,
12701275 'tfs': settings.tfs,
12711276 'epsilon_cutoff': [OOBA, MANCER].includes(settings.type) ? settings.epsilon_cutoff : undefined,
src/constants.js+1 -0
@@ -369,6 +369,7 @@ export const OPENROUTER_KEYS = [
369369 'prompt',
370370 'stop',
371371 'provider',
372+ 'include_reasoning',
372373];
373374
374375// https://github.com/vllm-project/vllm/blob/0f8a91401c89ac0a8018def3756829611b57727f/vllm/entrypoints/openai/protocol.py#L220
src/endpoints/backends/chat-completions.js+2 -2
@@ -288,7 +288,7 @@ async function sendMakerSuiteRequest(request, response) {
288288
289289 const model = String(request.body.model);
290290 const stream = Boolean(request.body.stream);
291291 const showThoughts = Boolean(request.body.show_thoughtsinclude_reasoning);
292292 const isThinking = model.includes('thinking');
293293
294294 const generationConfig = {
@@ -998,7 +998,7 @@ router.post('/generate', jsonParser, function (request, response) {
998998 bodyParams['route'] = 'fallback';
999999 }
10001000
10011001 if (request.body.show_thoughtsinclude_reasoning) {
10021002 bodyParams['include_reasoning'] = true;
10031003 }
10041004