Add character card macros

This commit is contained in:
Cohee
2023-11-08 16:28:55 +02:00
parent d144831569
commit dbf995fd24
5 changed files with 99 additions and 56 deletions

View File

@ -1799,7 +1799,7 @@ function getLastMessageId() {
* @param {*} _group - The group members list for {{group}} substitution.
* @returns {string} The string with substituted parameters.
*/
function substituteParams(content, _name1, _name2, _original, _group) {
function substituteParams(content, _name1, _name2, _original, _group, _replaceCharacterCard = true) {
_name1 = _name1 ?? name1;
_name2 = _name2 ?? name2;
_group = _group ?? name2;
@ -1814,7 +1814,18 @@ function substituteParams(content, _name1, _name2, _original, _group) {
if (typeof _original === 'string') {
content = content.replace(/{{original}}/i, _original);
}
content = content.replace(/{{input}}/gi, String($('#send_textarea').val()));
if (_replaceCharacterCard) {
const fields = getCharacterCardFields();
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 || '');
}
content = content.replace(/{{user}}/gi, _name1);
content = content.replace(/{{char}}/gi, _name2);
content = content.replace(/{{charIfNotGroup}}/gi, _group);
@ -2180,7 +2191,8 @@ function getExtensionPrompt(position = 0, depth = undefined, separator = "\n") {
export function baseChatReplace(value, name1, name2) {
if (value !== undefined && value.length > 0) {
value = substituteParams(value, name1, name2);
const _ = undefined;
value = substituteParams(value, name1, name2, _, _, false);
if (power_user.collapse_newlines) {
value = collapseNewlines(value);
@ -2191,6 +2203,41 @@ export function baseChatReplace(value, name1, name2) {
return value;
}
/**
* Returns the character card fields for the current character.
* @returns {{system: string, mesExamples: string, description: string, personality: string, persona: string, scenario: string, jailbreak: string}}
*/
function getCharacterCardFields() {
const result = { system: '', mesExamples: '', description: '', personality: '', persona: '', scenario: '', jailbreak: '' };
const character = characters[this_chid];
if (!character) {
return result;
}
const scenarioText = chat_metadata['scenario'] || characters[this_chid].scenario;
result.description = baseChatReplace(characters[this_chid].description.trim(), name1, name2);
result.personality = baseChatReplace(characters[this_chid].personality.trim(), name1, name2);
result.scenario = baseChatReplace(scenarioText.trim(), name1, name2);
result.mesExamples = baseChatReplace(characters[this_chid].mes_example.trim(), name1, name2);
result.persona = baseChatReplace(power_user.persona_description.trim(), name1, name2);
result.system = power_user.prefer_character_prompt ? baseChatReplace(characters[this_chid].data?.system_prompt?.trim(), name1, name2) : '';
result.jailbreak = power_user.prefer_character_jailbreak ? baseChatReplace(characters[this_chid].data?.post_history_instructions?.trim(), name1, name2) : '';
if (selected_group) {
const groupCards = getGroupCharacterCards(selected_group, Number(this_chid));
if (groupCards) {
result.description = groupCards.description;
result.personality = groupCards.personality;
result.scenario = groupCards.scenario;
result.mesExamples = groupCards.mesExamples;
}
}
return result;
}
function isStreamingEnabled() {
const noStreamSources = [chat_completion_sources.SCALE, chat_completion_sources.AI21, chat_completion_sources.PALM];
return ((main_api == 'openai' && oai_settings.stream_openai && !noStreamSources.includes(oai_settings.chat_completion_source))
@ -2673,30 +2720,19 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
await sendMessageAsUser(oai_settings.send_if_empty.trim(), messageBias);
}
////////////////////////////////////
const scenarioText = chat_metadata['scenario'] || characters[this_chid].scenario;
let charDescription = baseChatReplace(characters[this_chid].description.trim(), name1, name2);
let charPersonality = baseChatReplace(characters[this_chid].personality.trim(), name1, name2);
let scenario = baseChatReplace(scenarioText.trim(), name1, name2);
let mesExamples = baseChatReplace(characters[this_chid].mes_example.trim(), name1, name2);
let systemPrompt = power_user.prefer_character_prompt ? baseChatReplace(characters[this_chid].data?.system_prompt?.trim(), name1, name2) : '';
let jailbreakPrompt = power_user.prefer_character_jailbreak ? baseChatReplace(characters[this_chid].data?.post_history_instructions?.trim(), name1, name2) : '';
let personaDescription = baseChatReplace(power_user.persona_description.trim(), name1, name2);
let {
description,
personality,
persona,
scenario,
mesExamples,
system,
jailbreak,
} = getCharacterCardFields();
if (isInstruct) {
systemPrompt = power_user.prefer_character_prompt && systemPrompt ? systemPrompt : baseChatReplace(power_user.instruct.system_prompt, name1, name2);
systemPrompt = formatInstructModeSystemPrompt(substituteParams(systemPrompt, name1, name2, power_user.instruct.system_prompt));
}
if (selected_group) {
const groupCards = getGroupCharacterCards(selected_group, Number(this_chid));
if (groupCards) {
charDescription = groupCards.description;
charPersonality = groupCards.personality;
scenario = groupCards.scenario;
mesExamples = groupCards.mesExample;
}
system = power_user.prefer_character_prompt && system ? system : baseChatReplace(power_user.instruct.system_prompt, name1, name2);
system = formatInstructModeSystemPrompt(substituteParams(system, name1, name2, power_user.instruct.system_prompt));
}
// Depth prompt (character-specific A/N)
@ -2850,11 +2886,11 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
let zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' ');
const storyStringParams = {
description: charDescription,
personality: charPersonality,
persona: personaDescription,
description: description,
personality: personality,
persona: persona,
scenario: scenario,
system: isInstruct ? systemPrompt : '',
system: isInstruct ? system : '',
char: name2,
user: name1,
wiBefore: worldInfoBefore,
@ -3282,8 +3318,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
else if (main_api == 'openai') {
let [prompt, counts] = prepareOpenAIMessages({
name2: name2,
charDescription: charDescription,
charPersonality: charPersonality,
charDescription: description,
charPersonality: personality,
Scenario: scenario,
worldInfoBefore: worldInfoBefore,
worldInfoAfter: worldInfoAfter,
@ -3292,9 +3328,9 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
type: type,
quietPrompt: quiet_prompt,
cyclePrompt: cyclePrompt,
systemPromptOverride: systemPrompt,
jailbreakPromptOverride: jailbreakPrompt,
personaDescription: personaDescription
systemPromptOverride: system,
jailbreakPromptOverride: jailbreak,
personaDescription: persona
}, dryRun);
generate_data = { prompt: prompt };
@ -3338,13 +3374,13 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
generatedPromptCache: generatedPromptCache,
promptBias: promptBias,
finalPrompt: finalPrompt,
charDescription: charDescription,
charPersonality: charPersonality,
scenarioText: scenarioText,
charDescription: description,
charPersonality: personality,
scenarioText: scenario,
this_max_context: this_max_context,
padding: power_user.token_padding,
main_api: main_api,
instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && systemPrompt ? systemPrompt : power_user.instruct.system_prompt) : '',
instruction: isInstruct ? substituteParams(power_user.prefer_character_prompt && system ? system : power_user.instruct.system_prompt) : '',
userPersona: (power_user.persona_description || ''),
};
@ -5888,7 +5924,7 @@ function select_rm_create() {
$("#scenario_pole").val(create_save.scenario);
$("#depth_prompt_prompt").val(create_save.depth_prompt_prompt);
$("#depth_prompt_depth").val(create_save.depth_prompt_depth);
$("#mes_example_textarea").val(create_save.mes_example.trim().length === 0 ? '<START>' : create_save.mes_example);
$("#mes_example_textarea").val(create_save.mes_example);
$('#character_json_data').val('');
$("#avatar_div").css("display", "flex");
$("#avatar_load_preview").attr("src", default_avatar);

View File

@ -2,7 +2,7 @@
import { callPopup, event_types, eventSource, is_send_press, main_api, substituteParams } from "../script.js";
import { is_group_generating } from "./group-chats.js";
import { TokenHandler } from "./openai.js";
import { Message, TokenHandler } from "./openai.js";
import { power_user } from "./power-user.js";
import { debounce, waitUntilCondition, escapeHtml } from "./utils.js";
@ -1141,12 +1141,10 @@ PromptManagerModule.prototype.loadMessagesIntoInspectForm = function (messages)
let drawerHTML = `
<div class="inline-drawer ${this.configuration.prefix}prompt_manager_prompt">
<div class="inline-drawer-toggle inline-drawer-header">
<span>Name: ${title}, Role: ${role}, Tokens: ${tokens}</span>
<span>Name: ${escapeHtml(title)}, Role: ${role}, Tokens: ${tokens}</span>
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
</div>
<div class="inline-drawer-content">
${content}
</div>
<div class="inline-drawer-content" style="white-space: pre-wrap;">${escapeHtml(content)}</div>
</div>
`;
@ -1157,9 +1155,11 @@ PromptManagerModule.prototype.loadMessagesIntoInspectForm = function (messages)
const messageList = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_inspect_list');
if (0 === messages.getCollection().length) messageList.innerHTML = `<span>This marker does not contain any prompts.</span>`;
const messagesCollection = messages instanceof Message ? [messages] : messages.getCollection();
messages.getCollection().forEach(message => {
if (0 === messagesCollection.length) messageList.innerHTML = `<span>This marker does not contain any prompts.</span>`;
messagesCollection.forEach(message => {
messageList.append(createInlineDrawer(message));
});
}

View File

@ -256,7 +256,7 @@ export function getGroupDepthPrompts(groupId, characterId) {
* Combines group members info a single string. Only for groups with generation mode set to APPEND.
* @param {string} groupId Group ID
* @param {number} characterId Current Character ID
* @returns {{description: string, personality: string, scenario: string, mesExample: string}} Group character cards combined
* @returns {{description: string, personality: string, scenario: string, mesExamples: string}} Group character cards combined
*/
export function getGroupCharacterCards(groupId, characterId) {
console.debug('getGroupCharacterCards entered for group: ', groupId);
@ -271,7 +271,7 @@ export function getGroupCharacterCards(groupId, characterId) {
let descriptions = [];
let personalities = [];
let scenarios = [];
let mesExamples = [];
let mesExamplesArray = [];
for (const member of group.members) {
const index = characters.findIndex(x => x.avatar === member);
@ -290,15 +290,15 @@ export function getGroupCharacterCards(groupId, characterId) {
descriptions.push(baseChatReplace(character.description.trim(), name1, character.name));
personalities.push(baseChatReplace(character.personality.trim(), name1, character.name));
scenarios.push(baseChatReplace(character.scenario.trim(), name1, character.name));
mesExamples.push(baseChatReplace(character.mes_example.trim(), name1, character.name));
mesExamplesArray.push(baseChatReplace(character.mes_example.trim(), name1, character.name));
}
const description = descriptions.join('\n');
const personality = personalities.join('\n');
const scenario = scenarioOverride?.trim() || scenarios.join('\n');
const mesExample = mesExamples.join('\n');
const mesExamples = mesExamplesArray.join('\n');
return { description, personality, scenario, mesExample };
return { description, personality, scenario, mesExamples };
}
function getFirstCharacterMessage(character) {

View File

@ -1033,7 +1033,7 @@ function prepareOpenAIMessages({
// Pass chat completion to prompt manager for inspection
promptManager.setChatCompletion(chatCompletion);
if (oai_settings.squash_system_messages) {
if (oai_settings.squash_system_messages && dryRun == false) {
chatCompletion.squashSystemMessages();
}

View File

@ -1,19 +1,26 @@
System-wide Replacement Macros:
System-wide Replacement Macros (in order of evaluation):
<ul>
<li><tt>&lcub;&lcub;original&rcub;&rcub;</tt> - global prompts defined in API settings. Only valid in Advanced Definitions prompt overrides.</li>
<li><tt>&lcub;&lcub;input&rcub;&rcub;</tt> - the user input</li>
<li><tt>&lcub;&lcub;description&rcub;&rcub;</tt> - the Character's Description</li>
<li><tt>&lcub;&lcub;personality&rcub;&rcub;</tt> - the Character's Personality</li>
<li><tt>&lcub;&lcub;scenario&rcub;&rcub;</tt> - the Character's Scenario</li>
<li><tt>&lcub;&lcub;persona&rcub;&rcub;</tt> - your current Persona Description</li>
<li><tt>&lcub;&lcub;mesExamples&rcub;&rcub;</tt> - the Character's Dialogue Examples</li>
<li><tt>&lcub;&lcub;user&rcub;&rcub;</tt> - your current Persona username</li>
<li><tt>&lcub;&lcub;char&rcub;&rcub;</tt> - the Character's name</li>
<li><tt>&lcub;&lcub;input&rcub;&rcub;</tt> - the user input</li>
<li><tt>&lcub;&lcub;// (note)&rcub;&rcub;</tt> - you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.</li>
<li><tt>&lcub;&lcub;lastMessageId&rcub;&rcub;</tt> - index # of the latest chat message. Useful for slash command batching.</li>
<li><tt>&lcub;&lcub;// (note)&rcub;&rcub;</tt> - you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.</li>
<li><tt>&lcub;&lcub;time&rcub;&rcub;</tt> - the current time</li>
<li><tt>&lcub;&lcub;date&rcub;&rcub;</tt> - the current date</li>
<li><tt>&lcub;&lcub;weekday&rcub;&rcub;</tt> - the current weekday</li>
<li><tt>&lcub;&lcub;isotime&rcub;&rcub;</tt> - the current ISO date (YYYY-MM-DD)</li>
<li><tt>&lcub;&lcub;isodate&rcub;&rcub;</tt> - the current ISO time (24-hour clock)</li>
<li><tt>&lcub;&lcub;datetimeformat &hellip;&rcub;&rcub;</tt> - the current date/time in the specified format, e. g. for German date/time: <tt>&lcub;&lcub;datetimeformat DD.MM.YYYY HH:mm&rcub;&rcub;</tt></li>
<li><tt>&lcub;&lcub;bias "text here"&rcub;&rcub;</tt> - sets a behavioral bias for the AI until the next user input. Quotes around the text are important.</li>
<li><tt>&lcub;&lcub;banned "text here"&rcub;&rcub;</tt> - dynamically add text in the quotes to banned words sequences, if Text Generation WebUI backend used. Do nothing for others backends. Can be used anywhere (Character description, WI, AN, etc.) Quotes around the text are important.</li>
<li><tt>&lcub;&lcub;time_UTC±#&rcub;&rcub;</tt> - the current time in the specified UTC time zone offset, e.g. UTC-4 or UTC+2</li>
<li><tt>&lcub;&lcub;idle_duration&rcub;&rcub;</tt> - the time since the last user message was sent</li>
<li><tt>&lcub;&lcub;bias "text here"&rcub;&rcub;</tt> - sets a behavioral bias for the AI until the next user input. Quotes around the text are important.</li>
<li><tt>&lcub;&lcub;random:(args)&rcub;&rcub;</tt> - returns a random item from the list. (ex: &lcub;&lcub;random:1,2,3,4&rcub;&rcub; will return 1 of the 4 numbers at random. Works with text lists too.</li>
<li><tt>&lcub;&lcub;roll:(formula)&rcub;&rcub;</tt> - rolls a dice. (ex: &lcub;&lcub;roll:1d6&rcub;&rcub; will roll a 6-sided dice and return a number between 1 and 6)</li>
<li><tt>&lcub;&lcub;banned "text here"&rcub;&rcub;</tt> - dynamically add text in the quotes to banned words sequences, if Text Generation WebUI backend used. Do nothing for others backends. Can be used anywhere (Character description, WI, AN, etc.) Quotes around the text are important.</li>
</ul>