Implement Gemini thought signatures (#4886)
* Implement Gemini thought signatures * Implement streaming support for Gemini thought signatures * Implement OR support for Gemini thought signatures * Remove unnecessary extraction of thought sigs from response parts * Update thought sig comments to remove explicit Gemini mention * Fix thought_signature naming convention in message.extra * Add thought_signatures to ReasoningMessageExtra typedef * Prevent thought sigs being sent to incompatible endpoints * Move signatures to populateChatHistory, update for consistent casing * Code clean-up * Only send thought signatures if target model and API match original * Implement content-hash thought signature mapping * Change the data model + split for text/functions * Don't include signature to invocations if the model doesn't match * Fix function description * Remove misleading comment * Handle OpenRouter signatures * Improve message extra types * Prevent modifying original invocations when removing signatures * Fix return of openrouter non-streaming signatures * Remove redundant array check --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
+20
-5
@@ -267,7 +267,7 @@ import { initServerHistory } from './scripts/server-history.js';
|
||||
import { initSettingsSearch } from './scripts/setting-search.js';
|
||||
import { initBulkEdit } from './scripts/bulk-edit.js';
|
||||
import { getContext } from './scripts/st-context.js';
|
||||
import { extractReasoningFromData, initReasoning, parseReasoningInSwipes, PromptReasoning, ReasoningHandler, removeReasoningFromString, updateReasoningUI } from './scripts/reasoning.js';
|
||||
import { extractReasoningFromData, extractReasoningSignatureFromData, initReasoning, parseReasoningInSwipes, PromptReasoning, ReasoningHandler, removeReasoningFromString, updateReasoningUI } from './scripts/reasoning.js';
|
||||
import { accountStorage } from './scripts/util/AccountStorage.js';
|
||||
import { initWelcomeScreen, openPermanentAssistantChat, openPermanentAssistantCard, getPermanentAssistantAvatar } from './scripts/welcome-screen.js';
|
||||
import { initDataMaid } from './scripts/data-maid.js';
|
||||
@@ -3388,6 +3388,8 @@ class StreamingProcessor {
|
||||
this.promptReasoning = promptReasoning;
|
||||
/** @type {string[]} */
|
||||
this.images = [];
|
||||
/** @type {string?} */
|
||||
this.reasoningSignature = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3581,6 +3583,12 @@ class StreamingProcessor {
|
||||
appendMediaToMessage(message, $(this.messageDom));
|
||||
}
|
||||
|
||||
// Store reasoning signature for models that support multi-turn context
|
||||
if (this.reasoningSignature) {
|
||||
message.extra = message.extra || {};
|
||||
message.extra.reasoning_signature = this.reasoningSignature;
|
||||
}
|
||||
|
||||
this.markUIGenStopped();
|
||||
|
||||
if (this.type !== 'impersonate') {
|
||||
@@ -3675,6 +3683,7 @@ class StreamingProcessor {
|
||||
// Get the updated reasoning string into the handler
|
||||
this.reasoningHandler.updateReasoning(this.messageId, state?.reasoning);
|
||||
this.images = state?.images ?? [];
|
||||
this.reasoningSignature = state?.signature ?? null;
|
||||
await eventSource.emit(event_types.STREAM_TOKEN_RECEIVED, text);
|
||||
await sw.tick(async () => await this.onProgressStreaming(this.messageId, this.continueMessage + text));
|
||||
}
|
||||
@@ -5237,6 +5246,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
|
||||
let title = extractTitleFromData(data);
|
||||
let reasoning = extractReasoningFromData(data);
|
||||
let imageUrls = extractImagesFromData(data);
|
||||
const reasoningSignature = extractReasoningSignatureFromData(data);
|
||||
kobold_horde_model = title;
|
||||
|
||||
const swipes = extractMultiSwipes(data, type);
|
||||
@@ -5280,10 +5290,10 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
|
||||
else {
|
||||
// Without streaming we'll be having a full message on continuation. Treat it as a last chunk.
|
||||
if (originalType !== 'continue') {
|
||||
({ type, getMessage } = await saveReply({ type, getMessage, title, swipes, reasoning, imageUrls }));
|
||||
({ type, getMessage } = await saveReply({ type, getMessage, title, swipes, reasoning, imageUrls, reasoningSignature }));
|
||||
}
|
||||
else {
|
||||
({ type, getMessage } = await saveReply({ type: 'appendFinal', getMessage, title, swipes, reasoning, imageUrls }));
|
||||
({ type, getMessage } = await saveReply({ type: 'appendFinal', getMessage, title, swipes, reasoning, imageUrls, reasoningSignature }));
|
||||
}
|
||||
|
||||
// This relies on `saveReply` having been called to add the message to the chat, so it must be last.
|
||||
@@ -6332,16 +6342,17 @@ async function processImageAttachment(message, { imageUrls }) {
|
||||
* @property {string[]} [swipes] Extra swipes
|
||||
* @property {string} [reasoning] Message reasoning
|
||||
* @property {string[]} [imageUrls] Links to images
|
||||
* @property {string?} [reasoningSignature] Encrypted signature of the reasoning text
|
||||
*
|
||||
* @typedef {object} SaveReplyResult
|
||||
* @property {string} type Type of generation
|
||||
* @property {string} getMessage Generated message
|
||||
*/
|
||||
export async function saveReply({ type, getMessage, fromStreaming = false, title = '', swipes = [], reasoning = '', imageUrls = [] }) {
|
||||
export async function saveReply({ type, getMessage, fromStreaming = false, title = '', swipes = [], reasoning = '', imageUrls = [], reasoningSignature = null }) {
|
||||
// Backward compatibility
|
||||
if (arguments.length > 1 && typeof arguments[0] !== 'object') {
|
||||
console.trace('saveReply called with positional arguments. Please use an object instead.');
|
||||
[type, getMessage, fromStreaming, title, swipes, reasoning, imageUrls] = arguments;
|
||||
[type, getMessage, fromStreaming, title, swipes, reasoning, imageUrls, reasoningSignature] = arguments;
|
||||
}
|
||||
|
||||
if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined ||
|
||||
@@ -6377,6 +6388,7 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
|
||||
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
|
||||
chat[chat.length - 1]['extra']['reasoning'] = reasoning;
|
||||
chat[chat.length - 1]['extra']['reasoning_duration'] = null;
|
||||
chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;
|
||||
await processImageAttachment(chat[chat.length - 1], { imageUrls });
|
||||
if (power_user.message_token_count_enabled) {
|
||||
const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
|
||||
@@ -6401,6 +6413,7 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
|
||||
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
|
||||
chat[chat.length - 1]['extra']['reasoning'] = reasoning;
|
||||
chat[chat.length - 1]['extra']['reasoning_duration'] = null;
|
||||
chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;
|
||||
await processImageAttachment(chat[chat.length - 1], { imageUrls });
|
||||
if (power_user.message_token_count_enabled) {
|
||||
const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];
|
||||
@@ -6421,6 +6434,7 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
|
||||
chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
|
||||
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
|
||||
chat[chat.length - 1]['extra']['reasoning'] += reasoning;
|
||||
chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;
|
||||
await processImageAttachment(chat[chat.length - 1], { imageUrls });
|
||||
// We don't know if the reasoning duration extended, so we don't update it here on purpose.
|
||||
if (power_user.message_token_count_enabled) {
|
||||
@@ -6443,6 +6457,7 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
|
||||
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
|
||||
chat[chat.length - 1]['extra']['reasoning'] = reasoning;
|
||||
chat[chat.length - 1]['extra']['reasoning_duration'] = null;
|
||||
chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;
|
||||
if (power_user.trim_spaces) {
|
||||
getMessage = getMessage.trim();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user