mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-24 07:50:10 +01:00
Add example extension for chat variables. Allow registering custom text processing functions for extensions
This commit is contained in:
parent
0ffb3a8dda
commit
41cc86af9f
@ -21,6 +21,7 @@
|
||||
"showdown-katex",
|
||||
"droll",
|
||||
"handlebars",
|
||||
"highlight.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ import {
|
||||
PAGINATION_TEMPLATE,
|
||||
} from "./scripts/utils.js";
|
||||
|
||||
import { extension_settings, getContext, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js";
|
||||
import { extension_settings, getContext, loadExtensionSettings, registerExtensionHelper, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js";
|
||||
import { executeSlashCommands, getSlashCommandsHelp, registerSlashCommand } from "./scripts/slash-commands.js";
|
||||
import {
|
||||
tag_map,
|
||||
@ -2087,14 +2087,14 @@ class StreamingProcessor {
|
||||
$(`#chat .mes[mesid="${messageId}"] .mes_buttons`).css({ 'display': 'flex' });
|
||||
}
|
||||
|
||||
onStartStreaming(text) {
|
||||
async onStartStreaming(text) {
|
||||
let messageId = -1;
|
||||
|
||||
if (this.type == "impersonate") {
|
||||
$('#send_textarea').val('').trigger('input');
|
||||
}
|
||||
else {
|
||||
saveReply(this.type, text);
|
||||
await saveReply(this.type, text);
|
||||
messageId = count_view_mes - 1;
|
||||
this.showMessageButtons(messageId);
|
||||
}
|
||||
@ -2170,8 +2170,13 @@ class StreamingProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
onFinishStreaming(messageId, text) {
|
||||
async onFinishStreaming(messageId, text) {
|
||||
this.hideMessageButtons(this.messageId);
|
||||
|
||||
const eventType = this.type !== 'impersonate' ? event_types.MESSAGE_RECEIVED : event_types.IMPERSONATE_READY;
|
||||
const eventData = this.type !== 'impersonate' ? this.messageId : text;
|
||||
await eventSource.emit(eventType, eventData);
|
||||
|
||||
this.onProgressStreaming(messageId, text, true);
|
||||
addCopyToCodeBlocks($(`#chat .mes[mesid="${messageId}"]`));
|
||||
saveChatConditional();
|
||||
@ -2213,10 +2218,6 @@ class StreamingProcessor {
|
||||
}
|
||||
}
|
||||
playMessageSound();
|
||||
|
||||
const eventType = this.type !== 'impersonate' ? event_types.MESSAGE_RECEIVED : event_types.IMPERSONATE_READY;
|
||||
const eventData = this.type !== 'impersonate' ? this.messageId : text;
|
||||
eventSource.emit(eventType, eventData);
|
||||
}
|
||||
|
||||
onErrorStreaming() {
|
||||
@ -2241,7 +2242,7 @@ class StreamingProcessor {
|
||||
this.onErrorStreaming();
|
||||
}
|
||||
|
||||
nullStreamingGeneration() {
|
||||
*nullStreamingGeneration() {
|
||||
throw new Error('Generation function for streaming is not hooked up');
|
||||
}
|
||||
|
||||
@ -2260,7 +2261,7 @@ class StreamingProcessor {
|
||||
|
||||
async generate() {
|
||||
if (this.messageId == -1) {
|
||||
this.messageId = this.onStartStreaming(this.firstMessageText);
|
||||
this.messageId = await this.onStartStreaming(this.firstMessageText);
|
||||
await delay(1); // delay for message to be rendered
|
||||
scrollLock = false;
|
||||
}
|
||||
@ -3026,7 +3027,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||
}
|
||||
|
||||
if (streamingProcessor && !streamingProcessor.isStopped && streamingProcessor.isFinished) {
|
||||
streamingProcessor.onFinishStreaming(streamingProcessor.messageId, getMessage);
|
||||
await streamingProcessor.onFinishStreaming(streamingProcessor.messageId, getMessage);
|
||||
streamingProcessor = null;
|
||||
}
|
||||
}
|
||||
@ -3059,11 +3060,11 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||
if (!isImpersonate) {
|
||||
if (tokens_already_generated == 0) {
|
||||
console.debug("New message");
|
||||
({ type, getMessage } = saveReply(type, getMessage, this_mes_is_name, title));
|
||||
({ type, getMessage } = await saveReply(type, getMessage, this_mes_is_name, title));
|
||||
}
|
||||
else {
|
||||
console.debug("Should append message");
|
||||
({ type, getMessage } = saveReply('append', getMessage, this_mes_is_name, title));
|
||||
({ type, getMessage } = await saveReply('append', getMessage, this_mes_is_name, title));
|
||||
}
|
||||
} else {
|
||||
let chunk = cleanUpMessage(message_already_generated, true, isContinue, true);
|
||||
@ -3112,12 +3113,11 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||
else {
|
||||
// Without streaming we'll be having a full message on continuation. Treat it as a multigen last chunk.
|
||||
if (!isMultigenEnabled() && originalType !== 'continue') {
|
||||
({ type, getMessage } = saveReply(type, getMessage, this_mes_is_name, title));
|
||||
({ type, getMessage } = await saveReply(type, getMessage, this_mes_is_name, title));
|
||||
}
|
||||
else {
|
||||
({ type, getMessage } = saveReply('appendFinal', getMessage, this_mes_is_name, title));
|
||||
({ type, getMessage } = await saveReply('appendFinal', getMessage, this_mes_is_name, title));
|
||||
}
|
||||
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
||||
}
|
||||
activateSendButtons();
|
||||
|
||||
@ -3294,9 +3294,9 @@ export async function sendMessageAsUser(textareaText, messageBias) {
|
||||
chat[chat.length - 1]['extra']['bias'] = messageBias;
|
||||
}
|
||||
statMesProcess(chat[chat.length - 1], 'user', characters, this_chid, '');
|
||||
addOneMessage(chat[chat.length - 1]);
|
||||
// Wait for all handlers to finish before continuing with the prompt
|
||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||
addOneMessage(chat[chat.length - 1]);
|
||||
console.debug('message sent as user');
|
||||
}
|
||||
|
||||
@ -3814,10 +3814,7 @@ function cleanUpMessage(getMessage, isImpersonate, isContinue, displayIncomplete
|
||||
return getMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function saveReply(type, getMessage, this_mes_is_name, title) {
|
||||
async function saveReply(type, getMessage, this_mes_is_name, title) {
|
||||
if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined ||
|
||||
chat[chat.length - 1]['is_user'])) {
|
||||
type = 'normal';
|
||||
@ -3838,6 +3835,7 @@ function saveReply(type, getMessage, this_mes_is_name, title) {
|
||||
chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
|
||||
chat[chat.length - 1]['extra']['api'] = getGeneratingApi();
|
||||
chat[chat.length - 1]['extra']['model'] = getGeneratingModel();
|
||||
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
||||
addOneMessage(chat[chat.length - 1], { type: 'swipe' });
|
||||
} else {
|
||||
chat[chat.length - 1]['mes'] = getMessage;
|
||||
@ -3852,6 +3850,7 @@ function saveReply(type, getMessage, this_mes_is_name, title) {
|
||||
chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
|
||||
chat[chat.length - 1]["extra"]["api"] = getGeneratingApi();
|
||||
chat[chat.length - 1]["extra"]["model"] = getGeneratingModel();
|
||||
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
||||
addOneMessage(chat[chat.length - 1], { type: 'swipe' });
|
||||
} else if (type === 'appendFinal') {
|
||||
oldMessage = chat[chat.length - 1]['mes'];
|
||||
@ -3863,6 +3862,7 @@ function saveReply(type, getMessage, this_mes_is_name, title) {
|
||||
chat[chat.length - 1]['send_date'] = getMessageTimeStamp();
|
||||
chat[chat.length - 1]["extra"]["api"] = getGeneratingApi();
|
||||
chat[chat.length - 1]["extra"]["model"] = getGeneratingModel();
|
||||
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
||||
addOneMessage(chat[chat.length - 1], { type: 'swipe' });
|
||||
|
||||
} else {
|
||||
@ -3896,6 +3896,7 @@ function saveReply(type, getMessage, this_mes_is_name, title) {
|
||||
}
|
||||
|
||||
saveImageToMessage(img, chat[chat.length - 1]);
|
||||
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
||||
addOneMessage(chat[chat.length - 1]);
|
||||
}
|
||||
|
||||
@ -6503,8 +6504,8 @@ async function createOrEditCharacter(e) {
|
||||
|
||||
add_mes_without_animation = true;
|
||||
//console.log('form create submission calling addOneMessage');
|
||||
addOneMessage(chat[0]);
|
||||
await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1));
|
||||
addOneMessage(chat[0]);
|
||||
}
|
||||
}
|
||||
$("#create_button").removeAttr("disabled");
|
||||
@ -6558,6 +6559,7 @@ window["SillyTavern"].getContext = function () {
|
||||
deactivateSendButtons,
|
||||
saveReply,
|
||||
registerSlashCommand: registerSlashCommand,
|
||||
registerHelper: registerExtensionHelper,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders } from "../script.js";
|
||||
import { isSubsetOf, debounce } from "./utils.js";
|
||||
import { isSubsetOf, debounce, waitUntilCondition } from "./utils.js";
|
||||
export {
|
||||
getContext,
|
||||
getApiUrl,
|
||||
@ -16,6 +16,42 @@ let manifests = [];
|
||||
const defaultUrl = "http://localhost:5100";
|
||||
export const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
|
||||
|
||||
export const extensionsHandlebars = Handlebars.create();
|
||||
|
||||
/**
|
||||
* Registers a Handlebars helper for use in extensions.
|
||||
* @param {string} name Handlebars helper name
|
||||
* @param {function} helper Handlebars helper function
|
||||
*/
|
||||
export function registerExtensionHelper(name, helper) {
|
||||
extensionsHandlebars.registerHelper(name, helper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies handlebars extension helpers to a message.
|
||||
* @param {number} messageId Message index in the chat.
|
||||
*/
|
||||
function processExtensionHelpers(messageId) {
|
||||
const context = getContext();
|
||||
const message = context.chat[messageId];
|
||||
|
||||
if (!message?.mes || typeof message.mes !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't waste time if there are no mustaches
|
||||
if (!message.mes.includes('{{')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const template = extensionsHandlebars.compile(message.mes, { noEscape: true });
|
||||
message.mes = template({});
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Disables parallel updates
|
||||
class ModuleWorkerWrapper {
|
||||
constructor(callback) {
|
||||
@ -629,10 +665,13 @@ async function runGenerationInterceptors(chat, contextSize) {
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(async function () {
|
||||
setTimeout(function () {
|
||||
jQuery(function () {
|
||||
setTimeout(async function () {
|
||||
addExtensionsButtonAndMenu();
|
||||
$("#extensionsMenuButton").css("display", "flex");
|
||||
await waitUntilCondition(() => eventSource !== undefined, 1000, 100);
|
||||
eventSource.on(event_types.MESSAGE_RECEIVED, processExtensionHelpers);
|
||||
eventSource.on(event_types.MESSAGE_SENT, processExtensionHelpers);
|
||||
}, 100)
|
||||
|
||||
$("#extensions_connect").on('click', connectClickHandler);
|
||||
|
62
public/scripts/extensions/variables/index.js
Normal file
62
public/scripts/extensions/variables/index.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { getContext } from "../../extensions.js";
|
||||
|
||||
/**
|
||||
* Gets a chat variable from the current chat metadata.
|
||||
* @param {string} name The name of the variable to get.
|
||||
* @returns {string} The value of the variable.
|
||||
*/
|
||||
function getChatVariable(name) {
|
||||
const metadata = getContext().chatMetadata;
|
||||
|
||||
if (!metadata) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!metadata.variables) {
|
||||
metadata.variables = {};
|
||||
return '';
|
||||
}
|
||||
|
||||
return metadata.variables[name] || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a chat variable in the current chat metadata.
|
||||
* @param {string} name The name of the variable to set.
|
||||
* @param {any} value The value of the variable to set.
|
||||
*/
|
||||
function setChatVariable(name, value) {
|
||||
const metadata = getContext().chatMetadata;
|
||||
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!metadata.variables) {
|
||||
metadata.variables = {};
|
||||
}
|
||||
|
||||
metadata.variables[name] = value;
|
||||
}
|
||||
|
||||
function listChatVariables() {
|
||||
const metadata = getContext().chatMetadata;
|
||||
|
||||
if (!metadata) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!metadata.variables) {
|
||||
metadata.variables = {};
|
||||
return '';
|
||||
}
|
||||
|
||||
return Object.keys(metadata.variables).map(key => `${key}=${metadata.variables[key]}`).join(';');
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
const context = getContext();
|
||||
context.registerHelper('getvar', getChatVariable);
|
||||
context.registerHelper('setvar', setChatVariable);
|
||||
context.registerHelper('listvar', listChatVariables);
|
||||
});
|
11
public/scripts/extensions/variables/manifest.json
Normal file
11
public/scripts/extensions/variables/manifest.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"display_name": "Chat Variables",
|
||||
"loading_order": 100,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
@ -1088,8 +1088,8 @@ async function sendAltScaleRequest(openai_msgs_tosend, logit_bias, signal) {
|
||||
const generate_url = '/generate_altscale';
|
||||
|
||||
let firstSysMsgs = []
|
||||
for(let msg of openai_msgs_tosend){
|
||||
if(msg.role === 'system') {
|
||||
for (let msg of openai_msgs_tosend) {
|
||||
if (msg.role === 'system') {
|
||||
firstSysMsgs.push(substituteParams(msg.name ? msg.name + ": " + msg.content : msg.content));
|
||||
} else {
|
||||
break;
|
||||
@ -2587,7 +2587,7 @@ function onSettingsPresetChange() {
|
||||
use_ai21_tokenizer: ['#use_ai21_tokenizer', 'use_ai21_tokenizer', false],
|
||||
exclude_assistant: ['#exclude_assistant', 'exclude_assistant', false],
|
||||
use_alt_scale: ['#use_alt_scale', 'use_alt_scale', false],
|
||||
};
|
||||
};
|
||||
|
||||
const presetName = $('#settings_perset_openai').find(":selected").text();
|
||||
oai_settings.preset_settings_openai = presetName;
|
||||
|
@ -338,8 +338,8 @@ async function sendMessageAs(_, text) {
|
||||
};
|
||||
|
||||
chat.push(message);
|
||||
addOneMessage(message);
|
||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||
addOneMessage(message);
|
||||
saveChatConditional();
|
||||
}
|
||||
|
||||
@ -369,8 +369,8 @@ async function sendNarratorMessage(_, text) {
|
||||
};
|
||||
|
||||
chat.push(message);
|
||||
addOneMessage(message);
|
||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||
addOneMessage(message);
|
||||
saveChatConditional();
|
||||
}
|
||||
|
||||
@ -394,8 +394,8 @@ async function sendCommentMessage(_, text) {
|
||||
};
|
||||
|
||||
chat.push(message);
|
||||
addOneMessage(message);
|
||||
await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
|
||||
addOneMessage(message);
|
||||
saveChatConditional();
|
||||
}
|
||||
|
||||
|
@ -3301,7 +3301,7 @@ async function sendScaleRequest(request, response) {
|
||||
}
|
||||
|
||||
app.post("/generate_altscale", jsonParser, function (request, response_generate_scale) {
|
||||
if(!request.body) return response_generate_scale.sendStatus(400);
|
||||
if (!request.body) return response_generate_scale.sendStatus(400);
|
||||
|
||||
fetch('https://dashboard.scale.com/spellbook/api/trpc/v2.variant.run', {
|
||||
method: 'POST',
|
||||
@ -3357,11 +3357,11 @@ app.post("/generate_altscale", jsonParser, function (request, response_generate_
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log(data.result.data.json.outputs[0])
|
||||
return response_generate_scale.send({output: data.result.data.json.outputs[0]});
|
||||
return response_generate_scale.send({ output: data.result.data.json.outputs[0] });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error)
|
||||
return response_generate_scale.send({error: true})
|
||||
return response_generate_scale.send({ error: true })
|
||||
});
|
||||
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user