Pass macro variables in to evaluateMacros

This doesn't cover *all* the variables yet, just the ones that were
previously passed in as arguments. I'll expand this later to separate
the macro parsing from the execution of the functions themselves.
This commit is contained in:
valadaptive 2024-01-12 05:19:37 -05:00
parent 4d534e3042
commit 71f47588cd
2 changed files with 42 additions and 31 deletions

View File

@ -2157,10 +2157,35 @@ function scrollChatToBottom() {
* @param {*} _name2 - The name of the character. Uses global name2 if not provided.
* @param {*} _original - The original message for {{original}} substitution.
* @param {*} _group - The group members list for {{group}} substitution.
* @param {boolean} _replaceCharacterCard - Whether to replace character card macros.
* @returns {string} The string with substituted parameters.
*/
function substituteParams(content, _name1, _name2, _original, _group, _replaceCharacterCard = true) {
return evaluateMacros(content, _name1 ?? name1, _name2 ?? name2, _original, _group ?? name2, _replaceCharacterCard);
const environment = {};
environment.user = _name1 ?? name1;
environment.char = _name2 ?? name2;
environment.group = environment.charIfNotGroup = _group ?? name2;
let substitutedOriginal = false;
environment.original = () => {
// Only substitute {{original}} on its first occurrence
if (substitutedOriginal || typeof _original !== 'string') return '';
return _original;
};
if (_replaceCharacterCard) {
const fields = getCharacterCardFields();
environment.charPrompt = fields.system || '';
environment.charJailbreak = fields.jailbreak || '';
environment.description = fields.description || '';
environment.personality = fields.personality || '';
environment.scenario = fields.scenario || '';
environment.persona = fields.persona || '';
environment.mesExamples = fields.mesExamples || '';
}
return evaluateMacros(content, environment);
}

View File

@ -1,4 +1,4 @@
import { chat, main_api, getMaxContextSize, getCharacterCardFields } from '../script.js';
import { chat, main_api, getMaxContextSize } from '../script.js';
import { timestampToMoment, isDigitsOnly } from './utils.js';
import { textgenerationwebui_banned_in_macros } from './textgen-settings.js';
import { replaceInstructMacros } from './instruct-mode.js';
@ -201,56 +201,42 @@ function diceRollReplace(input, invalidRollPlaceholder = '') {
/**
* Substitutes {{macro}} parameters in a string.
* @param {string} content - The string to substitute parameters in.
* @param {*} _name1 - The name of the user.
* @param {*} _name2 - The name of the character.
* @param {*} _original - The original message for {{original}} substitution.
* @param {*} _group - The group members list for {{group}} substitution.
* @param {boolean} _replaceCharacterCard - Whether to replace character card macros.
* @param {Object<string, *>} env - Map of macro names to the values they'll be substituted with. If the param
* values are functions, those functions will be called and their return values are used.
* @returns {string} The string with substituted parameters.
*/
export function evaluateMacros(content, _name1, _name2, _original, _group, _replaceCharacterCard = true) {
export function evaluateMacros(content, env) {
if (!content) {
return '';
}
// Replace {{original}} with the original message
// Note: only replace the first instance of {{original}}
// This will hopefully prevent the abuse
if (typeof _original === 'string') {
content = content.replace(/{{original}}/i, _original);
}
content = diceRollReplace(content);
content = replaceInstructMacros(content);
content = replaceVariableMacros(content);
content = content.replace(/{{newline}}/gi, '\n');
content = content.replace(/{{input}}/gi, String($('#send_textarea').val()));
if (_replaceCharacterCard) {
const fields = getCharacterCardFields();
content = content.replace(/{{charPrompt}}/gi, fields.system || '');
content = content.replace(/{{charJailbreak}}/gi, fields.jailbreak || '');
content = content.replace(/{{description}}/gi, fields.description || '');
content = content.replace(/{{personality}}/gi, fields.personality || '');
content = content.replace(/{{scenario}}/gi, fields.scenario || '');
content = content.replace(/{{persona}}/gi, fields.persona || '');
content = content.replace(/{{mesExamples}}/gi, fields.mesExamples || '');
// Substitute passed-in variables
for (const varName in env) {
if (!Object.hasOwn(env, varName)) continue;
const param = env[varName];
const paramValue = typeof param === 'function' ? param() : param;
content = content.replace(new RegExp(`{{${varName}}}`, 'gi'), paramValue);
}
content = content.replace(/{{maxPrompt}}/gi, () => String(getMaxContextSize()));
content = content.replace(/{{user}}/gi, _name1);
content = content.replace(/{{char}}/gi, _name2);
content = content.replace(/{{charIfNotGroup}}/gi, _group);
content = content.replace(/{{group}}/gi, _group);
content = content.replace(/{{lastMessage}}/gi, getLastMessage());
content = content.replace(/{{lastMessageId}}/gi, getLastMessageId());
content = content.replace(/{{firstIncludedMessageId}}/gi, getFirstIncludedMessageId());
content = content.replace(/{{lastSwipeId}}/gi, getLastSwipeId());
content = content.replace(/{{currentSwipeId}}/gi, getCurrentSwipeId());
content = content.replace(/<USER>/gi, _name1);
content = content.replace(/<BOT>/gi, _name2);
content = content.replace(/<CHARIFNOTGROUP>/gi, _group);
content = content.replace(/<GROUP>/gi, _group);
// Legacy non-macro substitutions
content = content.replace(/<USER>/gi, typeof env.user === 'function' ? env.user() : env.user);
content = content.replace(/<BOT>/gi, typeof env.char === 'function' ? env.char() : env.char);
content = content.replace(/<CHARIFNOTGROUP>/gi, typeof env.group === 'function' ? env.group() : env.group);
content = content.replace(/<GROUP>/gi, typeof env.group === 'function' ? env.group() : env.group);
content = content.replace(/\{\{\/\/([\s\S]*?)\}\}/gm, '');