OpenRouter: Support reasoning blocks

This commit is contained in:
Cohee 2025-01-24 00:56:44 +02:00
parent 7f9b139ae0
commit 03c98fb55a
7 changed files with 37 additions and 31 deletions

View File

@ -1977,7 +1977,7 @@
</span>
</div>
</div>
<div class="range-block" data-source="makersuite,deepseek">
<div class="range-block" data-source="makersuite,deepseek,openrouter">
<label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
<input id="openai_show_thoughts" type="checkbox" />
<span>

View File

@ -170,7 +170,7 @@ import {
isElementInViewport,
copyText,
} from './scripts/utils.js';
import { debounce_timeout, THINK_BREAK } from './scripts/constants.js';
import { debounce_timeout } from './scripts/constants.js';
import { doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
import { COMMENT_NAME_DEFAULT, executeSlashCommandsOnChatInput, getSlashCommandsHelp, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, processChatSlashCommands, stopScriptExecution } from './scripts/slash-commands.js';
@ -5700,10 +5700,6 @@ function getTextContextFromData(data) {
function extractMessageFromData(data){
const content = String(getTextContextFromData(data) ?? '');
if (content.includes(THINK_BREAK)) {
return content.split(THINK_BREAK)[1];
}
return content;
}
@ -5713,14 +5709,15 @@ function extractMessageFromData(data){
* @returns {string} Extracted reasoning
*/
function extractReasoningFromData(data) {
const content = String(getTextContextFromData(data) ?? '');
if (content.includes(THINK_BREAK)) {
return content.split(THINK_BREAK)[0];
}
if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) {
return data?.choices?.[0]?.message?.reasoning_content ?? '';
if (main_api === 'openai' && oai_settings.show_thoughts) {
switch (oai_settings.chat_completion_source) {
case chat_completion_sources.DEEPSEEK:
return data?.choices?.[0]?.message?.reasoning_content ?? '';
case chat_completion_sources.OPENROUTER:
return data?.choices?.[0]?.message?.reasoning ?? '';
case chat_completion_sources.MAKERSUITE:
return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
}
}
return '';

View File

@ -14,8 +14,3 @@ export const debounce_timeout = {
/** [5 sec] For delayed tasks, like auto-saving or completing batch operations that need a significant pause. */
extended: 5000,
};
/**
* Custom boundary for splitting the text between the model's reasoning and the actual response.
*/
export const THINK_BREAK = '##<23>THINK_BREAK<41>##';

View File

@ -2161,6 +2161,11 @@ function getStreamingReply(data, state) {
state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '');
}
return data.choices?.[0]?.delta?.content || '';
} else if (oai_settings.chat_completion_source === chat_completion_sources.OPENROUTER) {
if (oai_settings.show_thoughts) {
state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');
}
return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
} else {
return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
}

View File

@ -235,6 +235,21 @@ async function* parseStreamData(json) {
}
return;
}
else if (typeof json.choices[0].delta.reasoning === 'string' && json.choices[0].delta.reasoning.length > 0) {
for (let j = 0; j < json.choices[0].delta.reasoning.length; j++) {
const str = json.choices[0].delta.reasoning[j];
const isLastSymbol = j === json.choices[0].delta.reasoning.length - 1;
const choiceClone = structuredClone(json.choices[0]);
choiceClone.delta.reasoning = str;
choiceClone.delta.content = isLastSymbol ? choiceClone.delta.content : '';
const choices = [choiceClone];
yield {
data: { ...json, choices },
chunk: str,
};
}
return;
}
else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) {
for (let j = 0; j < json.choices[0].delta.content.length; j++) {
const str = json.choices[0].delta.content[j];

View File

@ -413,8 +413,3 @@ export const VLLM_KEYS = [
'guided_decoding_backend',
'guided_whitespace_pattern',
];
/**
* Custom boundary for splitting the text between the model's reasoning and the actual response.
*/
export const THINK_BREAK = '##<23>THINK_BREAK<41>##';

View File

@ -7,7 +7,6 @@ import {
CHAT_COMPLETION_SOURCES,
GEMINI_SAFETY,
OPENROUTER_HEADERS,
THINK_BREAK,
} from '../../constants.js';
import {
forwardFetchResponse,
@ -392,11 +391,7 @@ async function sendMakerSuiteRequest(request, response) {
const responseContent = candidates[0].content ?? candidates[0].output;
console.log('Google AI Studio response:', responseContent);
if (Array.isArray(responseContent?.parts) && isThinking && !showThoughts) {
responseContent.parts = responseContent.parts.filter(part => !part.thought);
}
const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join(THINK_BREAK);
const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n');
if (!responseText) {
let message = 'Google AI Studio Candidate text empty';
console.log(message, generateResponseJson);
@ -404,7 +399,7 @@ async function sendMakerSuiteRequest(request, response) {
}
// Wrap it back to OAI format
const reply = { choices: [{ 'message': { 'content': responseText } }] };
const reply = { choices: [{ 'message': { 'content': responseText } }], responseContent };
return response.send(reply);
}
} catch (error) {
@ -993,6 +988,10 @@ router.post('/generate', jsonParser, function (request, response) {
bodyParams['route'] = 'fallback';
}
if (request.body.show_thoughts) {
bodyParams['include_reasoning'] = true;
}
let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1);
if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) {
cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);