mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
OpenRouter: Support reasoning blocks
This commit is contained in:
@ -1977,7 +1977,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
|
||||||
<input id="openai_show_thoughts" type="checkbox" />
|
<input id="openai_show_thoughts" type="checkbox" />
|
||||||
<span>
|
<span>
|
||||||
|
@ -170,7 +170,7 @@ import {
|
|||||||
isElementInViewport,
|
isElementInViewport,
|
||||||
copyText,
|
copyText,
|
||||||
} from './scripts/utils.js';
|
} 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 { 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';
|
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){
|
function extractMessageFromData(data){
|
||||||
const content = String(getTextContextFromData(data) ?? '');
|
const content = String(getTextContextFromData(data) ?? '');
|
||||||
|
|
||||||
if (content.includes(THINK_BREAK)) {
|
|
||||||
return content.split(THINK_BREAK)[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5713,14 +5709,15 @@ function extractMessageFromData(data){
|
|||||||
* @returns {string} Extracted reasoning
|
* @returns {string} Extracted reasoning
|
||||||
*/
|
*/
|
||||||
function extractReasoningFromData(data) {
|
function extractReasoningFromData(data) {
|
||||||
const content = String(getTextContextFromData(data) ?? '');
|
if (main_api === 'openai' && oai_settings.show_thoughts) {
|
||||||
|
switch (oai_settings.chat_completion_source) {
|
||||||
if (content.includes(THINK_BREAK)) {
|
case chat_completion_sources.DEEPSEEK:
|
||||||
return content.split(THINK_BREAK)[0];
|
return data?.choices?.[0]?.message?.reasoning_content ?? '';
|
||||||
}
|
case chat_completion_sources.OPENROUTER:
|
||||||
|
return data?.choices?.[0]?.message?.reasoning ?? '';
|
||||||
if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) {
|
case chat_completion_sources.MAKERSUITE:
|
||||||
return data?.choices?.[0]?.message?.reasoning_content ?? '';
|
return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -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. */
|
/** [5 sec] For delayed tasks, like auto-saving or completing batch operations that need a significant pause. */
|
||||||
extended: 5000,
|
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>##';
|
|
||||||
|
@ -2161,6 +2161,11 @@ function getStreamingReply(data, state) {
|
|||||||
state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '');
|
state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '');
|
||||||
}
|
}
|
||||||
return data.choices?.[0]?.delta?.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 {
|
} else {
|
||||||
return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
|
return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
|
||||||
}
|
}
|
||||||
|
@ -235,6 +235,21 @@ async function* parseStreamData(json) {
|
|||||||
}
|
}
|
||||||
return;
|
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) {
|
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++) {
|
for (let j = 0; j < json.choices[0].delta.content.length; j++) {
|
||||||
const str = json.choices[0].delta.content[j];
|
const str = json.choices[0].delta.content[j];
|
||||||
|
@ -413,8 +413,3 @@ export const VLLM_KEYS = [
|
|||||||
'guided_decoding_backend',
|
'guided_decoding_backend',
|
||||||
'guided_whitespace_pattern',
|
'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>##';
|
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
CHAT_COMPLETION_SOURCES,
|
CHAT_COMPLETION_SOURCES,
|
||||||
GEMINI_SAFETY,
|
GEMINI_SAFETY,
|
||||||
OPENROUTER_HEADERS,
|
OPENROUTER_HEADERS,
|
||||||
THINK_BREAK,
|
|
||||||
} from '../../constants.js';
|
} from '../../constants.js';
|
||||||
import {
|
import {
|
||||||
forwardFetchResponse,
|
forwardFetchResponse,
|
||||||
@ -392,11 +391,7 @@ async function sendMakerSuiteRequest(request, response) {
|
|||||||
const responseContent = candidates[0].content ?? candidates[0].output;
|
const responseContent = candidates[0].content ?? candidates[0].output;
|
||||||
console.log('Google AI Studio response:', responseContent);
|
console.log('Google AI Studio response:', responseContent);
|
||||||
|
|
||||||
if (Array.isArray(responseContent?.parts) && isThinking && !showThoughts) {
|
const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.filter(part => !part.thought)?.map(part => part.text)?.join('\n\n');
|
||||||
responseContent.parts = responseContent.parts.filter(part => !part.thought);
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join(THINK_BREAK);
|
|
||||||
if (!responseText) {
|
if (!responseText) {
|
||||||
let message = 'Google AI Studio Candidate text empty';
|
let message = 'Google AI Studio Candidate text empty';
|
||||||
console.log(message, generateResponseJson);
|
console.log(message, generateResponseJson);
|
||||||
@ -404,7 +399,7 @@ async function sendMakerSuiteRequest(request, response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wrap it back to OAI format
|
// Wrap it back to OAI format
|
||||||
const reply = { choices: [{ 'message': { 'content': responseText } }] };
|
const reply = { choices: [{ 'message': { 'content': responseText } }], responseContent };
|
||||||
return response.send(reply);
|
return response.send(reply);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -993,6 +988,10 @@ router.post('/generate', jsonParser, function (request, response) {
|
|||||||
bodyParams['route'] = 'fallback';
|
bodyParams['route'] = 'fallback';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.body.show_thoughts) {
|
||||||
|
bodyParams['include_reasoning'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1);
|
let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1);
|
||||||
if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) {
|
if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) {
|
||||||
cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);
|
cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);
|
||||||
|
Reference in New Issue
Block a user