diff --git a/public/scripts/openai.js b/public/scripts/openai.js
index c14df4566..f0cde3173 100644
--- a/public/scripts/openai.js
+++ b/public/scripts/openai.js
@@ -1743,8 +1743,8 @@ async function sendOpenAIRequest(type, messages, signal) {
delete generate_data.stop;
}
- // Proxy is only supported for Claude, OpenAI and Mistral
- if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI].includes(oai_settings.chat_completion_source)) {
+ // Proxy is only supported for Claude, OpenAI, Mistral, and Google MakerSuite
+ if (oai_settings.reverse_proxy && [chat_completion_sources.CLAUDE, chat_completion_sources.OPENAI, chat_completion_sources.MISTRALAI, chat_completion_sources.MAKERSUITE].includes(oai_settings.chat_completion_source)) {
validateReverseProxy();
generate_data['reverse_proxy'] = oai_settings.reverse_proxy;
generate_data['proxy_password'] = oai_settings.proxy_password;
@@ -3857,6 +3857,9 @@ async function onModelChange() {
else if (['command-r', 'command-r-plus'].includes(oai_settings.cohere_model)) {
$('#openai_max_context').attr('max', max_128k);
}
+ else if(['c4ai-aya-23'].includes(oai_settings.cohere_model)) {
+ $('#openai_max_context').attr('max', max_8k);
+ }
else {
$('#openai_max_context').attr('max', max_4k);
}
@@ -4035,7 +4038,7 @@ async function onConnectButtonClick(e) {
await writeSecret(SECRET_KEYS.MAKERSUITE, api_key_makersuite);
}
- if (!secret_state[SECRET_KEYS.MAKERSUITE]) {
+ if (!secret_state[SECRET_KEYS.MAKERSUITE] && !oai_settings.reverse_proxy) {
console.log('No secret key saved for MakerSuite');
return;
}
@@ -4087,7 +4090,7 @@ async function onConnectButtonClick(e) {
await writeSecret(SECRET_KEYS.MISTRALAI, api_key_mistralai);
}
- if (!secret_state[SECRET_KEYS.MISTRALAI]) {
+ if (!secret_state[SECRET_KEYS.MISTRALAI] && !oai_settings.reverse_proxy) {
console.log('No secret key saved for MistralAI');
return;
}
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index 0e647269c..39aef63f2 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -678,6 +678,9 @@ async function CreateZenSliders(elmnt) {
sliderID == 'top_k' ||
sliderID == 'mirostat_mode_kobold' ||
sliderID == 'rep_pen_range' ||
+ sliderID == 'dry_allowed_length_textgenerationwebui' ||
+ sliderID == 'rep_pen_decay_textgenerationwebui' ||
+ sliderID == 'dry_penalty_last_n_textgenerationwebui' ||
sliderID == 'max_tokens_second_textgenerationwebui') {
decimals = 0;
}
@@ -685,7 +688,9 @@ async function CreateZenSliders(elmnt) {
sliderID == 'max_temp_textgenerationwebui' ||
sliderID == 'dynatemp_exponent_textgenerationwebui' ||
sliderID == 'smoothing_curve_textgenerationwebui' ||
- sliderID == 'smoothing_factor_textgenerationwebui') {
+ sliderID == 'smoothing_factor_textgenerationwebui' ||
+ sliderID == 'dry_multiplier_textgenerationwebui' ||
+ sliderID == 'dry_base_textgenerationwebui') {
decimals = 2;
}
if (sliderID == 'eta_cutoff_textgenerationwebui' ||
@@ -746,6 +751,8 @@ async function CreateZenSliders(elmnt) {
sliderID == 'rep_pen_slope' ||
sliderID == 'smoothing_factor_textgenerationwebui' ||
sliderID == 'smoothing_curve_textgenerationwebui' ||
+ sliderID == 'skew_textgenerationwebui' ||
+ sliderID == 'dry_multiplier_textgenerationwebui' ||
sliderID == 'min_length_textgenerationwebui') {
offVal = 0;
}
@@ -1754,11 +1761,24 @@ function loadMaxContextUnlocked() {
}
function switchMaxContextSize() {
- const elements = [$('#max_context'), $('#max_context_counter'), $('#rep_pen_range'), $('#rep_pen_range_counter'), $('#rep_pen_range_textgenerationwebui'), $('#rep_pen_range_counter_textgenerationwebui')];
+ const elements = [
+ $('#max_context'),
+ $('#max_context_counter'),
+ $('#rep_pen_range'),
+ $('#rep_pen_range_counter'),
+ $('#rep_pen_range_textgenerationwebui'),
+ $('#rep_pen_range_counter_textgenerationwebui'),
+ $('#dry_penalty_last_n_textgenerationwebui'),
+ $('#dry_penalty_last_n_counter_textgenerationwebui'),
+ $('#rep_pen_decay_textgenerationwebui'),
+ $('#rep_pen_decay_counter_textgenerationwebui'),
+ ];
const maxValue = power_user.max_context_unlocked ? MAX_CONTEXT_UNLOCKED : MAX_CONTEXT_DEFAULT;
const minValue = power_user.max_context_unlocked ? maxContextMin : maxContextMin;
const steps = power_user.max_context_unlocked ? unlockedMaxContextStep : maxContextStep;
$('#rep_pen_range_textgenerationwebui_zenslider').remove(); //unsure why, but this is necessary.
+ $('#dry_penalty_last_n_textgenerationwebui_zenslider').remove();
+ $('#rep_pen_decay_textgenerationwebui_zenslider').remove();
for (const element of elements) {
const id = element.attr('id');
element.attr('max', maxValue);
@@ -1787,6 +1807,10 @@ function switchMaxContextSize() {
CreateZenSliders($('#max_context'));
$('#rep_pen_range_textgenerationwebui_zenslider').remove();
CreateZenSliders($('#rep_pen_range_textgenerationwebui'));
+ $('#dry_penalty_last_n_textgenerationwebui_zenslider').remove();
+ CreateZenSliders($('#dry_penalty_last_n_textgenerationwebui'));
+ $('#rep_pen_decay_textgenerationwebui_zenslider').remove();
+ CreateZenSliders($('#rep_pen_decay_textgenerationwebui'));
}
}
@@ -3009,7 +3033,7 @@ $(document).ready(() => {
var coreTruthWinHeight = window.innerHeight;
$(window).on('resize', async () => {
- console.log(`Window resize: ${coreTruthWinWidth}x${coreTruthWinHeight} -> ${window.innerWidth}x${window.innerHeight}`)
+ console.log(`Window resize: ${coreTruthWinWidth}x${coreTruthWinHeight} -> ${window.innerWidth}x${window.innerHeight}`);
adjustAutocompleteDebounced();
setHotswapsDebounced();
@@ -3062,7 +3086,7 @@ $(document).ready(() => {
}
}
} else {
- console.log('aborting MUI reset', Object.keys(power_user.movingUIState).length)
+ console.log('aborting MUI reset', Object.keys(power_user.movingUIState).length);
}
saveSettingsDebounced();
coreTruthWinWidth = window.innerWidth;
diff --git a/public/scripts/secrets.js b/public/scripts/secrets.js
index 4d7a6ebf1..cb4477a78 100644
--- a/public/scripts/secrets.js
+++ b/public/scripts/secrets.js
@@ -27,6 +27,7 @@ export const SECRET_KEYS = {
COHERE: 'api_key_cohere',
PERPLEXITY: 'api_key_perplexity',
GROQ: 'api_key_groq',
+ AZURE_TTS: 'api_key_azure_tts',
};
const INPUT_MAP = {
diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js
index d273130fa..5baa11435 100644
--- a/public/scripts/slash-commands.js
+++ b/public/scripts/slash-commands.js
@@ -1112,6 +1112,9 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
new SlashCommandNamedArgument(
'role', 'role for in-chat injections', [ARGUMENT_TYPE.STRING], false, false, 'system', ['system', 'user', 'assistant'],
),
+ new SlashCommandNamedArgument(
+ 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', ['true', 'false'],
+ ),
],
unnamedArgumentList: [
new SlashCommandArgument(
@@ -1173,6 +1176,7 @@ function injectCallback(args, value) {
};
const id = resolveVariable(args?.id);
+ const ephemeral = isTrueBoolean(args?.ephemeral);
if (!id) {
console.warn('WARN: No ID provided for /inject command');
@@ -1206,6 +1210,23 @@ function injectCallback(args, value) {
setExtensionPrompt(prefixedId, value, position, depth, scan, role);
saveMetadataDebounced();
+
+ if (ephemeral) {
+ let deleted = false;
+ const unsetInject = () => {
+ if (deleted) {
+ return;
+ }
+ console.log('Removing ephemeral script injection', id);
+ delete chat_metadata.script_injects[id];
+ setExtensionPrompt(prefixedId, '', position, depth, scan, role);
+ saveMetadataDebounced();
+ deleted = true;
+ };
+ eventSource.once(event_types.GENERATION_ENDED, unsetInject);
+ eventSource.once(event_types.GENERATION_STOPPED, unsetInject);
+ }
+
return '';
}
diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js
index 8b7a438d6..9d2cd6214 100644
--- a/public/scripts/textgen-settings.js
+++ b/public/scripts/textgen-settings.js
@@ -100,6 +100,7 @@ const settings = {
min_p: 0,
rep_pen: 1.2,
rep_pen_range: 0,
+ rep_pen_decay: 0,
no_repeat_ngram_size: 0,
penalty_alpha: 0,
num_beams: 1,
@@ -108,6 +109,7 @@ const settings = {
encoder_rep_pen: 1,
freq_pen: 0,
presence_pen: 0,
+ skew: 0,
do_sample: true,
early_stopping: false,
dynatemp: false,
@@ -116,6 +118,11 @@ const settings = {
dynatemp_exponent: 1.0,
smoothing_factor: 0.0,
smoothing_curve: 1.0,
+ dry_allowed_length: 2,
+ dry_multiplier: 0.0,
+ dry_base: 1.75,
+ dry_sequence_breakers: '["\\n", ":", "\\"", "*"]',
+ dry_penalty_last_n: 0,
max_tokens_second: 0,
seed: -1,
preset: 'Default',
@@ -139,6 +146,7 @@ const settings = {
//best_of_aphrodite: 1,
ignore_eos_token: false,
spaces_between_special_tokens: true,
+ speculative_ngram: false,
//logits_processors_aphrodite: [],
//log_probs_aphrodite: 0,
//prompt_log_probs_aphrodite: 0,
@@ -171,6 +179,7 @@ export const setting_names = [
'temperature_last',
'rep_pen',
'rep_pen_range',
+ 'rep_pen_decay',
'no_repeat_ngram_size',
'top_k',
'top_p',
@@ -190,10 +199,16 @@ export const setting_names = [
'dynatemp_exponent',
'smoothing_factor',
'smoothing_curve',
+ 'dry_allowed_length',
+ 'dry_multiplier',
+ 'dry_base',
+ 'dry_sequence_breakers',
+ 'dry_penalty_last_n',
'max_tokens_second',
'encoder_rep_pen',
'freq_pen',
'presence_pen',
+ 'skew',
'do_sample',
'early_stopping',
'seed',
@@ -214,6 +229,7 @@ export const setting_names = [
//'best_of_aphrodite',
'ignore_eos_token',
'spaces_between_special_tokens',
+ 'speculative_ngram',
//'logits_processors_aphrodite',
//'log_probs_aphrodite',
//'prompt_log_probs_aphrodite'
@@ -638,6 +654,7 @@ jQuery(function () {
'min_p_textgenerationwebui': 0,
'rep_pen_textgenerationwebui': 1,
'rep_pen_range_textgenerationwebui': 0,
+ 'rep_pen_decay_textgenerationwebui': 0,
'dynatemp_textgenerationwebui': false,
'seed_textgenerationwebui': -1,
'ban_eos_token_textgenerationwebui': false,
@@ -656,7 +673,9 @@ jQuery(function () {
'encoder_rep_pen_textgenerationwebui': 1,
'freq_pen_textgenerationwebui': 0,
'presence_pen_textgenerationwebui': 0,
+ 'skew_textgenerationwebui': 0,
'no_repeat_ngram_size_textgenerationwebui': 0,
+ 'speculative_ngram_textgenerationwebui': false,
'min_length_textgenerationwebui': 0,
'num_beams_textgenerationwebui': 1,
'length_penalty_textgenerationwebui': 1,
@@ -665,6 +684,10 @@ jQuery(function () {
'guidance_scale_textgenerationwebui': 1,
'smoothing_factor_textgenerationwebui': 0,
'smoothing_curve_textgenerationwebui': 1,
+ 'dry_allowed_length_textgenerationwebui': 2,
+ 'dry_multiplier_textgenerationwebui': 0,
+ 'dry_base_textgenerationwebui': 1.75,
+ 'dry_penalty_last_n_textgenerationwebui': 0,
};
for (const [id, value] of Object.entries(inputs)) {
@@ -1014,6 +1037,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
'frequency_penalty': settings.freq_pen,
'presence_penalty': settings.presence_pen,
'top_k': settings.top_k,
+ 'skew': settings.skew,
'min_length': settings.type === OOBA ? settings.min_length : undefined,
'minimum_message_content_tokens': settings.type === DREAMGEN ? settings.min_length : undefined,
'min_tokens': settings.min_length,
@@ -1028,6 +1052,11 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
'dynatemp_exponent': settings.dynatemp ? settings.dynatemp_exponent : undefined,
'smoothing_factor': settings.smoothing_factor,
'smoothing_curve': settings.smoothing_curve,
+ 'dry_allowed_length': settings.dry_allowed_length,
+ 'dry_multiplier': settings.dry_multiplier,
+ 'dry_base': settings.dry_base,
+ 'dry_sequence_breakers': settings.dry_sequence_breakers,
+ 'dry_penalty_last_n': settings.dry_penalty_last_n,
'max_tokens_second': settings.max_tokens_second,
'sampler_priority': settings.type === OOBA ? settings.sampler_priority : undefined,
'samplers': settings.type === LLAMACPP ? settings.samplers : undefined,
@@ -1055,11 +1084,13 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate,
const nonAphroditeParams = {
'rep_pen': settings.rep_pen,
'rep_pen_range': settings.rep_pen_range,
+ 'repetition_decay': settings.type === TABBY ? settings.rep_pen_decay : undefined,
'repetition_penalty_range': settings.rep_pen_range,
'encoder_repetition_penalty': settings.type === OOBA ? settings.encoder_rep_pen : undefined,
'no_repeat_ngram_size': settings.type === OOBA ? settings.no_repeat_ngram_size : undefined,
'penalty_alpha': settings.type === OOBA ? settings.penalty_alpha : undefined,
'temperature_last': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.temperature_last : undefined,
+ 'speculative_ngram': settings.type === TABBY ? settings.speculative_ngram : undefined,
'do_sample': settings.type === OOBA ? settings.do_sample : undefined,
'seed': settings.seed,
'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1,
diff --git a/public/scripts/tokenizers.js b/public/scripts/tokenizers.js
index 3318cac98..39a19d0ca 100644
--- a/public/scripts/tokenizers.js
+++ b/public/scripts/tokenizers.js
@@ -560,7 +560,7 @@ export function countTokensOpenAI(messages, full = false) {
if (shouldTokenizeAI21) {
tokenizerEndpoint = '/api/tokenizers/ai21/count';
} else if (shouldTokenizeGoogle) {
- tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}`;
+ tokenizerEndpoint = `/api/tokenizers/google/count?model=${getTokenizerModel()}&reverse_proxy=${oai_settings.reverse_proxy}&proxy_password=${oai_settings.proxy_password}`;
} else {
tokenizerEndpoint = `/api/tokenizers/openai/count?model=${getTokenizerModel()}`;
}
diff --git a/public/scripts/utils.js b/public/scripts/utils.js
index 63539513a..516a70412 100644
--- a/public/scripts/utils.js
+++ b/public/scripts/utils.js
@@ -1,5 +1,5 @@
import { getContext } from './extensions.js';
-import { getRequestHeaders } from '../script.js';
+import { callPopup, getRequestHeaders } from '../script.js';
import { isMobile } from './RossAscends-mods.js';
import { collapseNewlines } from './power-user.js';
import { debounce_timeout } from './constants.js';
@@ -139,6 +139,7 @@ export function download(content, fileName, contentType) {
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
+ URL.revokeObjectURL(a.href);
}
/**
@@ -1020,6 +1021,36 @@ export function extractDataFromPng(data, identifier = 'chara') {
}
}
+/**
+ * Sends a request to the server to sanitize a given filename
+ *
+ * @param {string} fileName - The name of the file to sanitize
+ * @returns {Promise
} A Promise that resolves to the sanitized filename if successful, or rejects with an error message if unsuccessful
+ */
+export async function getSanitizedFilename(fileName) {
+ try {
+ const result = await fetch('/api/files/sanitize-filename', {
+ method: 'POST',
+ headers: getRequestHeaders(),
+ body: JSON.stringify({
+ fileName: fileName,
+ }),
+ });
+
+ if (!result.ok) {
+ const error = await result.text();
+ throw new Error(error);
+ }
+
+ const responseData = await result.json();
+ return responseData.fileName;
+ } catch (error) {
+ toastr.error(String(error), 'Could not sanitize fileName');
+ console.error('Could not sanitize fileName', error);
+ throw error;
+ }
+}
+
/**
* Sends a base64 encoded image to the backend to be saved as a file.
*
@@ -1468,23 +1499,47 @@ export function flashHighlight(element, timespan = 2000) {
setTimeout(() => element.removeClass('flash animated'), timespan);
}
+/**
+ * A common base function for case-insensitive and accent-insensitive string comparisons.
+ *
+ * @param {string} a - The first string to compare.
+ * @param {string} b - The second string to compare.
+ * @param {(a:string,b:string)=>boolean} comparisonFunction - The function to use for the comparison.
+ * @returns {*} - The result of the comparison.
+ */
+export function compareIgnoreCaseAndAccents(a, b, comparisonFunction) {
+ if (!a || !b) return comparisonFunction(a, b); // Return the comparison result if either string is empty
+
+ // Normalize and remove diacritics, then convert to lower case
+ const normalizedA = a.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
+ const normalizedB = b.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
+
+ // Check if the normalized strings are equal
+ return comparisonFunction(normalizedA, normalizedB);
+}
+
/**
* Performs a case-insensitive and accent-insensitive substring search.
* This function normalizes the strings to remove diacritical marks and converts them to lowercase to ensure the search is insensitive to case and accents.
*
- * @param {string} text - The text in which to search for the substring.
- * @param {string} searchTerm - The substring to search for in the text.
- * @returns {boolean} - Returns true if the searchTerm is found within the text, otherwise returns false.
+ * @param {string} text - The text in which to search for the substring
+ * @param {string} searchTerm - The substring to search for in the text
+ * @returns {boolean} true if the searchTerm is found within the text, otherwise returns false
*/
export function includesIgnoreCaseAndAccents(text, searchTerm) {
- if (!text || !searchTerm) return false; // Return false if either string is empty
+ return compareIgnoreCaseAndAccents(text, searchTerm, (a, b) => a?.includes(b) === true);
+}
- // Normalize and remove diacritics, then convert to lower case
- const normalizedText = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
- const normalizedSearchTerm = searchTerm.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
-
- // Check if the normalized text includes the normalized search term
- return normalizedText.includes(normalizedSearchTerm);
+/**
+ * Performs a case-insensitive and accent-insensitive equality check.
+ * This function normalizes the strings to remove diacritical marks and converts them to lowercase to ensure the search is insensitive to case and accents.
+ *
+ * @param {string} a - The first string to compare
+ * @param {string} b - The second string to compare
+ * @returns {boolean} true if the strings are equal, otherwise returns false
+ */
+export function equalsIgnoreCaseAndAccents(a, b) {
+ return compareIgnoreCaseAndAccents(a, b, (a, b) => a === b);
}
/**
@@ -1665,3 +1720,38 @@ export function highlightRegex(regexStr) {
return `${regexStr}`;
}
+
+/**
+ * Confirms if the user wants to overwrite an existing data object (like character, world info, etc) if one exists.
+ * If no data with the name exists, this simply returns true.
+ *
+ * @param {string} type - The type of the check ("World Info", "Character", etc)
+ * @param {string[]} existingNames - The list of existing names to check against
+ * @param {string} name - The new name
+ * @param {object} options - Optional parameters
+ * @param {boolean} [options.interactive=false] - Whether to show a confirmation dialog when needing to overwrite an existing data object
+ * @param {string} [options.actionName='overwrite'] - The action name to display in the confirmation dialog
+ * @param {(existingName:string)=>void} [options.deleteAction=null] - Optional action to execute wen deleting an existing data object on overwrite
+ * @returns {Promise} True if the user confirmed the overwrite or there is no overwrite needed, false otherwise
+ */
+export async function checkOverwriteExistingData(type, existingNames, name, { interactive = false, actionName = 'Overwrite', deleteAction = null } = {}) {
+ const existing = existingNames.find(x => equalsIgnoreCaseAndAccents(x, name));
+ if (!existing) {
+ return true;
+ }
+
+ const overwrite = interactive ? await callPopup(`${type} ${actionName}
A ${type.toLowerCase()} with the same name already exists:
${existing}
Do you want to overwrite it?`, 'confirm') : false;
+ if (!overwrite) {
+ toastr.warning(`${type} ${actionName.toLowerCase()} cancelled. A ${type.toLowerCase()} with the same name already exists:
${existing}`, `${type} ${actionName}`, { escapeHtml: false });
+ return false;
+ }
+
+ toastr.info(`Overwriting Existing ${type}:
${existing}`, `${type} ${actionName}`, { escapeHtml: false });
+
+ // If there is an action to delete the existing data, do it, as the name might be slightly different so file name would not be the same
+ if (deleteAction) {
+ deleteAction(existing);
+ }
+
+ return true;
+}
diff --git a/public/scripts/variables.js b/public/scripts/variables.js
index 5f3229692..c8e47c77f 100644
--- a/public/scripts/variables.js
+++ b/public/scripts/variables.js
@@ -795,27 +795,26 @@ function letCallback(args, value) {
/**
* Set or retrieve a variable in the current scope or nearest ancestor scope.
- * @param {{_scope:SlashCommandScope, key?:string, index?:String|Number}} args Named arguments.
- * @param {String|[String, SlashCommandClosure]} value Name and optional value for the variable.
+ * @param {{_scope:SlashCommandScope, key?:string, index?:string|number}} args Named arguments.
+ * @param {string|SlashCommandClosure|(string|SlashCommandClosure)[]} value Name and optional value for the variable.
* @returns The variable's value
*/
function varCallback(args, value) {
- if (Array.isArray(value)) {
- args._scope.setVariable(value[0], typeof value[1] == 'string' ? value.slice(1).join(' ') : value[1], args.index);
- return value[1];
- }
+ if (!Array.isArray(value)) value = [value];
if (args.key !== undefined) {
const key = args.key;
- const val = value;
- args._scope.setVariable(key, val, args.index);
- return val;
- } else if (value.includes(' ')) {
- const key = value.split(' ')[0];
- const val = value.split(' ').slice(1).join(' ');
+ const val = value.join(' ');
args._scope.setVariable(key, val, args.index);
return val;
}
- return args._scope.getVariable(args.key ?? value, args.index);
+ const key = value.shift();
+ if (value.length > 0) {
+ const val = value.join(' ');
+ args._scope.setVariable(key, val, args.index);
+ return val;
+ } else {
+ return args._scope.getVariable(key, args.index);
+ }
}
export function registerVariableCommands() {
@@ -1733,7 +1732,7 @@ export function registerVariableCommands() {
returns: 'the variable value',
namedArgumentList: [
new SlashCommandNamedArgument(
- 'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], false,
+ 'key', 'variable name; forces setting the variable, even if no value is provided', [ARGUMENT_TYPE.VARIABLE_NAME], false,
),
new SlashCommandNamedArgument(
'index',
@@ -1769,7 +1768,7 @@ export function registerVariableCommands() {
/let x foo | /var x foo bar | /var x | /echo
- /let x foo | /var key=x foo bar | /var key=x | /echo
+ /let x foo | /var key=x foo bar | /var x | /echo
diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js
index 256821b3f..1fa9afd0f 100644
--- a/public/scripts/world-info.js
+++ b/public/scripts/world-info.js
@@ -1,5 +1,5 @@
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js';
-import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean } from './utils.js';
+import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, equalsIgnoreCaseAndAccents, getSanitizedFilename, checkOverwriteExistingData } from './utils.js';
import { extension_settings, getContext } from './extensions.js';
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
import { isMobile } from './RossAscends-mods.js';
@@ -50,6 +50,7 @@ const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry');
let world_info = {};
let selected_world_info = [];
+/** @type {string[]} */
let world_names;
let world_info_depth = 2;
let world_info_min_activations = 0; // if > 0, will continue seeking chat until minimum world infos are activated
@@ -1057,6 +1058,34 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
return;
}
+ // Regardless of whether success is displayed or not. Make sure the delete button is available.
+ // Do not put this code behind.
+ $('#world_popup_delete').off('click').on('click', async () => {
+ const confirmation = await callPopup(`
This action is irreversible!`, 'confirm');
+
+ if (!confirmation) {
+ return;
+ }
+
+ if (world_info.charLore) {
+ world_info.charLore.forEach((charLore, index) => {
+ if (charLore.extraBooks?.includes(name)) {
+ const tempCharLore = charLore.extraBooks.filter((e) => e !== name);
+ if (tempCharLore.length === 0) {
+ world_info.charLore.splice(index, 1);
+ } else {
+ charLore.extraBooks = tempCharLore;
+ }
+ }
+ });
+
+ saveSettingsDebounced();
+ }
+
+ // Selected world_info automatically refreshes
+ await deleteWorldInfo(name);
+ });
+
// Before printing the WI, we check if we should enable/disable search sorting
verifyWorldInfoSearchSortRule();
@@ -1225,32 +1254,6 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
}
});
- $('#world_popup_delete').off('click').on('click', async () => {
- const confirmation = await callPopup(`
This action is irreversible!`, 'confirm');
-
- if (!confirmation) {
- return;
- }
-
- if (world_info.charLore) {
- world_info.charLore.forEach((charLore, index) => {
- if (charLore.extraBooks?.includes(name)) {
- const tempCharLore = charLore.extraBooks.filter((e) => e !== name);
- if (tempCharLore.length === 0) {
- world_info.charLore.splice(index, 1);
- } else {
- charLore.extraBooks = tempCharLore;
- }
- }
- });
-
- saveSettingsDebounced();
- }
-
- // Selected world_info automatically refreshes
- await deleteWorldInfo(name);
- });
-
// Check if a sortable instance exists
if (worldEntriesList.sortable('instance') !== undefined) {
// Destroy the instance
@@ -2542,9 +2545,15 @@ async function renameWorldInfo(name, data) {
}
}
+/**
+ * Deletes a world info with the given name
+ *
+ * @param {string} worldInfoName - The name of the world info to delete
+ * @returns {Promise