mirror of
				https://github.com/SillyTavern/SillyTavern.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	Merge branch 'staging' into smol-tag-improvements
This commit is contained in:
		@@ -1,11 +1,11 @@
 | 
			
		||||
<div id="assets_ui">
 | 
			
		||||
    <div class="inline-drawer">
 | 
			
		||||
        <div class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
            <b>Download Extensions & Assets</b>
 | 
			
		||||
            <b data-i18n="Download Extensions & Assets">Download Extensions & Assets</b>
 | 
			
		||||
            <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="inline-drawer-content">
 | 
			
		||||
            <label for="assets-json-url-field">Assets URL</label>
 | 
			
		||||
            <label for="assets-json-url-field" data-i18n="Assets URL">Assets URL</label>
 | 
			
		||||
            <div class="assets-connect-div">
 | 
			
		||||
                <input id="assets-json-url-field" class="text_pole widthUnset flex1">
 | 
			
		||||
                <i id="assets-connect-button" class="menu_button fa-solid fa-plug-circle-exclamation fa-xl redOverlayGlow"></i>
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
                <input id="assets_search" class="text_pole flex1" placeholder="Search" type="search">
 | 
			
		||||
                <div id="assets-characters-button" class="menu_button menu_button_icon">
 | 
			
		||||
                    <i class="fa-solid fa-image-portrait"></i>
 | 
			
		||||
                    Characters
 | 
			
		||||
                    <span data-i18n="Characters">Characters</span>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="inline-drawer-content" id="assets_menu">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { getBase64Async, isTrueBoolean, saveBase64AsFile } from '../../utils.js';
 | 
			
		||||
import { getContext, getApiUrl, doExtrasFetch, extension_settings, modules, renderExtensionTemplateAsync } from '../../extensions.js';
 | 
			
		||||
import { callPopup, getRequestHeaders, saveSettingsDebounced, substituteParams } from '../../../script.js';
 | 
			
		||||
import { callPopup, getRequestHeaders, saveSettingsDebounced, substituteParamsExtended } from '../../../script.js';
 | 
			
		||||
import { getMessageTimeStamp } from '../../RossAscends-mods.js';
 | 
			
		||||
import { SECRET_KEYS, secret_state } from '../../secrets.js';
 | 
			
		||||
import { getMultimodalCaption } from '../shared.js';
 | 
			
		||||
@@ -95,7 +95,7 @@ async function sendCaptionedMessage(caption, image) {
 | 
			
		||||
        template += ' {{caption}}';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let messageText = substituteParams(template).replace(/{{caption}}/i, caption);
 | 
			
		||||
    let messageText = substituteParamsExtended(template, { caption: caption });
 | 
			
		||||
 | 
			
		||||
    if (extension_settings.caption.refine_mode) {
 | 
			
		||||
        messageText = await callPopup(
 | 
			
		||||
 
 | 
			
		||||
@@ -2,34 +2,34 @@
 | 
			
		||||
<div class="caption_settings">
 | 
			
		||||
    <div class="inline-drawer">
 | 
			
		||||
        <div class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
            <b>Image Captioning</b>
 | 
			
		||||
            <b data-i18n="Image Captioning">Image Captioning</b>
 | 
			
		||||
            <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="inline-drawer-content">
 | 
			
		||||
            <label for="caption_source">Source</label>
 | 
			
		||||
            <label for="caption_source" data-i18n="Source">Source</label>
 | 
			
		||||
            <select id="caption_source" class="text_pole">
 | 
			
		||||
                <option value="local">Local</option>
 | 
			
		||||
                <option value="multimodal">Multimodal (OpenAI / Anthropic / llama / Google)</option>
 | 
			
		||||
                <option value="extras">Extras</option>
 | 
			
		||||
                <option value="horde">Horde</option>
 | 
			
		||||
                <option value="local" data-i18n="Local">Local</option>
 | 
			
		||||
                <option value="multimodal" data-i18n="Multimodal (OpenAI / Anthropic / llama / Google)">Multimodal (OpenAI / Anthropic / llama / Google)</option>
 | 
			
		||||
                <option value="extras" data-i18n="Extras">Extras</option>
 | 
			
		||||
                <option value="horde" data-i18n="Horde">Horde</option>
 | 
			
		||||
            </select>
 | 
			
		||||
            <div id="caption_multimodal_block" class="flex-container wide100p">
 | 
			
		||||
                <div class="flex1 flex-container flexFlowColumn flexNoGap">
 | 
			
		||||
                    <label for="caption_multimodal_api">API</label>
 | 
			
		||||
                    <label for="caption_multimodal_api" data-i18n="API">API</label>
 | 
			
		||||
                    <select id="caption_multimodal_api" class="flex1 text_pole">
 | 
			
		||||
                        <option value="anthropic">Anthropic</option>
 | 
			
		||||
                        <option value="custom">Custom (OpenAI-compatible)</option>
 | 
			
		||||
                        <option value="custom" data-i18n="Custom (OpenAI-compatible)">Custom (OpenAI-compatible)</option>
 | 
			
		||||
                        <option value="google">Google MakerSuite</option>
 | 
			
		||||
                        <option value="koboldcpp">KoboldCpp</option>
 | 
			
		||||
                        <option value="llamacpp">llama.cpp</option>
 | 
			
		||||
                        <option value="ollama">Ollama</option>
 | 
			
		||||
                        <option value="openai">OpenAI</option>
 | 
			
		||||
                        <option value="openrouter">OpenRouter</option>
 | 
			
		||||
                        <option value="ooba">Text Generation WebUI (oobabooga)</option>
 | 
			
		||||
                        <option value="ooba" data-i18n="Text Generation WebUI (oobabooga)">Text Generation WebUI (oobabooga)</option>
 | 
			
		||||
                    </select>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex1 flex-container flexFlowColumn flexNoGap">
 | 
			
		||||
                    <label for="caption_multimodal_model">Model</label>
 | 
			
		||||
                    <label for="caption_multimodal_model" data-i18n="Model">Model</label>
 | 
			
		||||
                    <select id="caption_multimodal_model" class="flex1 text_pole">
 | 
			
		||||
                        <option data-type="openai" value="gpt-4-vision-preview">gpt-4-vision-preview</option>
 | 
			
		||||
                        <option data-type="openai" value="gpt-4-turbo">gpt-4-turbo</option>
 | 
			
		||||
@@ -54,36 +54,36 @@
 | 
			
		||||
                        <option data-type="openrouter" value="google/gemini-pro-vision">google/gemini-pro-vision</option>
 | 
			
		||||
                        <option data-type="openrouter" value="google/gemini-flash-1.5">google/gemini-flash-1.5</option>
 | 
			
		||||
                        <option data-type="openrouter" value="liuhaotian/llava-yi-34b">liuhaotian/llava-yi-34b</option>
 | 
			
		||||
                        <option data-type="ollama" value="ollama_current">[Currently selected]</option>
 | 
			
		||||
                        <option data-type="ollama" value="ollama_current" data-i18n="currently_selected">[Currently selected]</option>
 | 
			
		||||
                        <option data-type="ollama" value="bakllava:latest">bakllava:latest</option>
 | 
			
		||||
                        <option data-type="ollama" value="llava:latest">llava:latest</option>
 | 
			
		||||
                        <option data-type="llamacpp" value="llamacpp_current">[Currently loaded]</option>
 | 
			
		||||
                        <option data-type="ooba" value="ooba_current">[Currently loaded]</option>
 | 
			
		||||
                        <option data-type="koboldcpp" value="koboldcpp_current">[Currently loaded]</option>
 | 
			
		||||
                        <option data-type="custom" value="custom_current">[Currently selected]</option>
 | 
			
		||||
                        <option data-type="llamacpp" value="llamacpp_current" data-i18n="currently_loaded">[Currently loaded]</option>
 | 
			
		||||
                        <option data-type="ooba" value="ooba_current" data-i18n="currently_loaded">[Currently loaded]</option>
 | 
			
		||||
                        <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option>
 | 
			
		||||
                        <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option>
 | 
			
		||||
                    </select>
 | 
			
		||||
                </div>
 | 
			
		||||
                <label data-type="openai,anthropic,google" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">
 | 
			
		||||
                    <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">
 | 
			
		||||
                    Allow reverse proxy
 | 
			
		||||
                    <span data-i18n="Allow reverse proxy">Allow reverse proxy</span>
 | 
			
		||||
                </label>
 | 
			
		||||
                <div class="flexBasis100p m-b-1">
 | 
			
		||||
                    <small><b>Hint:</b> Set your API keys and endpoints in the 'API Connections' tab first.</small>
 | 
			
		||||
                    <small><b data-i18n="Hint:">Hint:</b> <span data-i18n="Set your API keys and endpoints in the 'API Connections' tab first.">Set your API keys and endpoints in the 'API Connections' tab first.</span></small>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="caption_prompt_block">
 | 
			
		||||
                <label for="caption_prompt">Caption Prompt</label>
 | 
			
		||||
                <label for="caption_prompt" data-i18n="Caption Prompt">Caption Prompt</label>
 | 
			
		||||
                <textarea id="caption_prompt" class="text_pole" rows="1" placeholder="< Use default >">${PROMPT_DEFAULT}</textarea>
 | 
			
		||||
                <label class="checkbox_label margin-bot-10px" for="caption_prompt_ask" title="Ask for a custom prompt every time an image is captioned.">
 | 
			
		||||
                    <input id="caption_prompt_ask" type="checkbox" class="checkbox">
 | 
			
		||||
                    Ask every time
 | 
			
		||||
                    <span data-i18n="Ask every time">Ask every time</span>
 | 
			
		||||
                </label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <label for="caption_template">Message Template <small>(use <code>{{caption}}</code> macro)</small></label>
 | 
			
		||||
            <label for="caption_template"><span data-i18n="Message Template">Message Template</span> <small><span data-i18n="(use _space">(use </span> <code>{{caption}}</code> <span data-i18n="macro)">macro)</span></small></label>
 | 
			
		||||
            <textarea id="caption_template" class="text_pole" rows="2" placeholder="< Use default >">${TEMPLATE_DEFAULT}</textarea>
 | 
			
		||||
            <label class="checkbox_label margin-bot-10px" for="caption_refine_mode">
 | 
			
		||||
                <input id="caption_refine_mode" type="checkbox" class="checkbox">
 | 
			
		||||
                Edit captions before saving
 | 
			
		||||
                <span data-i18n="Edit captions before saving">Edit captions before saving</span>
 | 
			
		||||
            </label>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { callPopup, eventSource, event_types, generateQuietPrompt, getRequestHeaders, online_status, saveSettingsDebounced, substituteParams } from '../../../script.js';
 | 
			
		||||
import { callPopup, eventSource, event_types, generateQuietPrompt, getRequestHeaders, online_status, saveSettingsDebounced, substituteParams, substituteParamsExtended, system_message_types } from '../../../script.js';
 | 
			
		||||
import { dragElement, isMobile } from '../../RossAscends-mods.js';
 | 
			
		||||
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplateAsync } from '../../extensions.js';
 | 
			
		||||
import { loadMovingUIState, power_user } from '../../power-user.js';
 | 
			
		||||
@@ -10,6 +10,7 @@ import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
 | 
			
		||||
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
 | 
			
		||||
import { ARGUMENT_TYPE, SlashCommandArgument } from '../../slash-commands/SlashCommandArgument.js';
 | 
			
		||||
import { isFunctionCallingSupported } from '../../openai.js';
 | 
			
		||||
import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
 | 
			
		||||
export { MODULE_NAME };
 | 
			
		||||
 | 
			
		||||
const MODULE_NAME = 'expressions';
 | 
			
		||||
@@ -87,6 +88,7 @@ function getFallbackExpression() {
 | 
			
		||||
 */
 | 
			
		||||
function toggleTalkingHeadCommand(_) {
 | 
			
		||||
    setTalkingHeadState(!extension_settings.expressions.talkinghead);
 | 
			
		||||
    return String(extension_settings.expressions.talkinghead);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isVisualNovelMode() {
 | 
			
		||||
@@ -914,6 +916,7 @@ async function setSpriteSetCommand(_, folder) {
 | 
			
		||||
    // moduleWorker();
 | 
			
		||||
    const vnMode = isVisualNovelMode();
 | 
			
		||||
    await sendExpressionCall(folder, lastExpression, true, vnMode);
 | 
			
		||||
    return '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function classifyCommand(_, text) {
 | 
			
		||||
@@ -935,7 +938,7 @@ async function classifyCommand(_, text) {
 | 
			
		||||
async function setSpriteSlashCommand(_, spriteId) {
 | 
			
		||||
    if (!spriteId) {
 | 
			
		||||
        console.log('No sprite id provided');
 | 
			
		||||
        return;
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    spriteId = spriteId.trim().toLowerCase();
 | 
			
		||||
@@ -955,7 +958,7 @@ async function setSpriteSlashCommand(_, spriteId) {
 | 
			
		||||
 | 
			
		||||
        if (!spriteItem) {
 | 
			
		||||
            console.log('No sprite found for search term ' + spriteId);
 | 
			
		||||
            return;
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        label = spriteItem.label;
 | 
			
		||||
@@ -963,6 +966,7 @@ async function setSpriteSlashCommand(_, spriteId) {
 | 
			
		||||
 | 
			
		||||
    const vnMode = isVisualNovelMode();
 | 
			
		||||
    await sendExpressionCall(spriteFolderName, label, true, vnMode);
 | 
			
		||||
    return label;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -1008,8 +1012,7 @@ async function getLlmPrompt(labels) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const labelsString = labels.map(x => `"${x}"`).join(', ');
 | 
			
		||||
    const prompt = substituteParams(String(extension_settings.expressions.llmPrompt))
 | 
			
		||||
        .replace(/{{labels}}/gi, labelsString);
 | 
			
		||||
    const prompt = substituteParamsExtended(String(extension_settings.expressions.llmPrompt), { labels: labelsString });
 | 
			
		||||
    return prompt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1187,7 +1190,7 @@ function getLastCharacterMessage() {
 | 
			
		||||
    const reversedChat = context.chat.slice().reverse();
 | 
			
		||||
 | 
			
		||||
    for (let mes of reversedChat) {
 | 
			
		||||
        if (mes.is_user || mes.is_system) {
 | 
			
		||||
        if (mes.is_user || mes.is_system || mes.extra?.type === system_message_types.NARRATOR) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1326,10 +1329,18 @@ async function renderFallbackExpressionPicker() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getCachedExpressions() {
 | 
			
		||||
    if (!Array.isArray(expressionsList)) {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [...expressionsList, ...extension_settings.expressions.custom].filter(onlyUnique);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getExpressionsList() {
 | 
			
		||||
    // Return cached list if available
 | 
			
		||||
    if (Array.isArray(expressionsList)) {
 | 
			
		||||
        return [...expressionsList, ...extension_settings.expressions.custom].filter(onlyUnique);
 | 
			
		||||
        return getCachedExpressions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -2033,17 +2044,23 @@ function migrateSettings() {
 | 
			
		||||
    });
 | 
			
		||||
    eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
 | 
			
		||||
    eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sprite',
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({
 | 
			
		||||
        name: 'sprite',
 | 
			
		||||
        aliases: ['emote'],
 | 
			
		||||
        callback: setSpriteSlashCommand,
 | 
			
		||||
        unnamedArgumentList: [
 | 
			
		||||
            new SlashCommandArgument(
 | 
			
		||||
                'spriteId', [ARGUMENT_TYPE.STRING], true,
 | 
			
		||||
            ),
 | 
			
		||||
            SlashCommandArgument.fromProps({
 | 
			
		||||
                description: 'spriteId',
 | 
			
		||||
                typeList: [ARGUMENT_TYPE.STRING],
 | 
			
		||||
                isRequired: true,
 | 
			
		||||
                enumProvider: () => getCachedExpressions().map((x) => new SlashCommandEnumValue(x)),
 | 
			
		||||
            }),
 | 
			
		||||
        ],
 | 
			
		||||
        helpString: 'Force sets the sprite for the current character.',
 | 
			
		||||
        returns: 'label',
 | 
			
		||||
    }));
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'spriteoverride',
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({
 | 
			
		||||
        name: 'spriteoverride',
 | 
			
		||||
        aliases: ['costume'],
 | 
			
		||||
        callback: setSpriteSetCommand,
 | 
			
		||||
        unnamedArgumentList: [
 | 
			
		||||
@@ -2053,8 +2070,9 @@ function migrateSettings() {
 | 
			
		||||
        ],
 | 
			
		||||
        helpString: 'Sets an override sprite folder for the current character. If the name starts with a slash or a backslash, selects a sub-folder in the character-named folder. Empty value to reset to default.',
 | 
			
		||||
    }));
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lastsprite',
 | 
			
		||||
        callback: (_, value) => lastExpression[value.trim()] ?? '',
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({
 | 
			
		||||
        name: 'lastsprite',
 | 
			
		||||
        callback: (_, value) => lastExpression[String(value).trim()] ?? '',
 | 
			
		||||
        returns: 'sprite',
 | 
			
		||||
        unnamedArgumentList: [
 | 
			
		||||
            new SlashCommandArgument(
 | 
			
		||||
@@ -2063,12 +2081,15 @@ function migrateSettings() {
 | 
			
		||||
        ],
 | 
			
		||||
        helpString: 'Returns the last set sprite / expression for the named character.',
 | 
			
		||||
    }));
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'th',
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({
 | 
			
		||||
        name: 'th',
 | 
			
		||||
        callback: toggleTalkingHeadCommand,
 | 
			
		||||
        aliases: ['talkinghead'],
 | 
			
		||||
        helpString: 'Character Expressions: toggles <i>Image Type - talkinghead (extras)</i> on/off.',
 | 
			
		||||
        returns: ARGUMENT_TYPE.BOOLEAN,
 | 
			
		||||
    }));
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'classify',
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({
 | 
			
		||||
        name: 'classify',
 | 
			
		||||
        callback: classifyCommand,
 | 
			
		||||
        unnamedArgumentList: [
 | 
			
		||||
            new SlashCommandArgument(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,65 +1,65 @@
 | 
			
		||||
<div class="expression_settings">
 | 
			
		||||
    <div class="inline-drawer">
 | 
			
		||||
        <div class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
            <b>Character Expressions</b>
 | 
			
		||||
            <b data-i18n="Character Expressions">Character Expressions</b>
 | 
			
		||||
            <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="inline-drawer-content">
 | 
			
		||||
            <label class="checkbox_label" for="expression_translate" title="Use the selected API from Chat Translation extension settings.">
 | 
			
		||||
                <input id="expression_translate" type="checkbox">
 | 
			
		||||
                <span>Translate text to English before classification</span>
 | 
			
		||||
                <span data-i18n="Translate text to English before classification">Translate text to English before classification</span>
 | 
			
		||||
            </label>
 | 
			
		||||
            <label class="checkbox_label" for="expressions_show_default">
 | 
			
		||||
                <input id="expressions_show_default" type="checkbox">
 | 
			
		||||
                <span>Show default images (emojis) if sprite missing</span>
 | 
			
		||||
                <span data-i18n="Show default images (emojis) if sprite missing">Show default images (emojis) if sprite missing</span>
 | 
			
		||||
            </label>
 | 
			
		||||
            <label id="image_type_block" class="checkbox_label" for="image_type_toggle">
 | 
			
		||||
                <input id="image_type_toggle" type="checkbox">
 | 
			
		||||
                <span>Image Type - talkinghead (extras)</span>
 | 
			
		||||
                <span data-i18n="Image Type - talkinghead (extras)">Image Type - talkinghead (extras)</span>
 | 
			
		||||
            </label>
 | 
			
		||||
            <div class="expression_api_block m-b-1 m-t-1">
 | 
			
		||||
                <label for="expression_api">Classifier API</label>
 | 
			
		||||
                <small>Select the API for classifying expressions.</small>
 | 
			
		||||
                <label for="expression_api" data-i18n="Classifier API">Classifier API</label>
 | 
			
		||||
                <small data-i18n="Select the API for classifying expressions.">Select the API for classifying expressions.</small>
 | 
			
		||||
                <select id="expression_api" class="flex1 margin0" data-i18n="Expression API" placeholder="Expression API">
 | 
			
		||||
                    <option value="0">Local</option>
 | 
			
		||||
                    <option value="1">Extras</option>
 | 
			
		||||
                    <option value="2">LLM</option>
 | 
			
		||||
                    <option value="0" data-i18n="Local">Local</option>
 | 
			
		||||
                    <option value="1" data-i18n="Extras">Extras</option>
 | 
			
		||||
                    <option value="2" data-i18n="LLM">LLM</option>
 | 
			
		||||
                </select>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="expression_llm_prompt_block m-b-1 m-t-1">
 | 
			
		||||
                <label for="expression_llm_prompt" class="title_restorable">
 | 
			
		||||
                    <span>LLM Prompt</span>
 | 
			
		||||
                    <span data-i18n="LLM Prompt">LLM Prompt</span>
 | 
			
		||||
                    <div id="expression_llm_prompt_restore" title="Restore default value" class="right_menu_button">
 | 
			
		||||
                        <i class="fa-solid fa-clock-rotate-left fa-sm"></i>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </label>
 | 
			
		||||
                <small>Will be used if the API doesn't support JSON schemas or function calling.</small>
 | 
			
		||||
                <small data-i18n="Will be used if the API doesn't support JSON schemas or function calling.">Will be used if the API doesn't support JSON schemas or function calling.</small>
 | 
			
		||||
                <textarea id="expression_llm_prompt" type="text" class="text_pole textarea_compact" rows="2" placeholder="Use {{labels}} special macro."></textarea>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="expression_fallback_block m-b-1 m-t-1">
 | 
			
		||||
                <label for="expression_fallback">Default / Fallback Expression</label>
 | 
			
		||||
                <small>Set the default and fallback expression being used when no matching expression is found.</small>
 | 
			
		||||
                <label for="expression_fallback" data-i18n="Default / Fallback Expression">Default / Fallback Expression</label>
 | 
			
		||||
                <small data-i18n="Set the default and fallback expression being used when no matching expression is found.">Set the default and fallback expression being used when no matching expression is found.</small>
 | 
			
		||||
                <select id="expression_fallback" class="flex1 margin0" data-i18n="Fallback Expression" placeholder="Fallback Expression"></select>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="expression_custom_block m-b-1 m-t-1">
 | 
			
		||||
                <label for="expression_custom">Custom Expressions</label>
 | 
			
		||||
                <small>Can be set manually or with an <tt>/emote</tt> slash command.</small>
 | 
			
		||||
                <label for="expression_custom" data-i18n="Custom Expressions">Custom Expressions</label>
 | 
			
		||||
                <small><span data-i18n="Can be set manually or with an _space">Can be set manually or with an </span><tt>/emote</tt><span data-i18n="space_ slash command."> slash command.</span></small>
 | 
			
		||||
                <div class="flex-container">
 | 
			
		||||
                    <select id="expression_custom" class="flex1 margin0"><select>
 | 
			
		||||
                    <i id="expression_custom_add" class="menu_button fa-solid fa-plus margin0" title="Add"></i>
 | 
			
		||||
                    <i id="expression_custom_remove" class="menu_button fa-solid fa-xmark margin0" title="Remove"></i>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="no_chat_expressions">
 | 
			
		||||
            <div id="no_chat_expressions" data-i18n="Open a chat to see the character expressions.">
 | 
			
		||||
                Open a chat to see the character expressions.
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="open_chat_expressions">
 | 
			
		||||
                <div class="offline_mode">
 | 
			
		||||
                    <small>You are in offline mode. Click on the image below to set the expression.</small>
 | 
			
		||||
                    <small data-i18n="You are in offline mode. Click on the image below to set the expression.">You are in offline mode. Click on the image below to set the expression.</small>
 | 
			
		||||
                </div>
 | 
			
		||||
                <label for="expression_override">Sprite Folder Override</label>
 | 
			
		||||
                <small>Use a forward slash to specify a subfolder. Example: <tt>Bob/formal</tt></small>
 | 
			
		||||
                <label for="expression_override" data-i18n="Sprite Folder Override">Sprite Folder Override</label>
 | 
			
		||||
                <small><span data-i18n="Use a forward slash to specify a subfolder. Example: _space">Use a forward slash to specify a subfolder. Example: </span><tt>Bob/formal</tt></small>
 | 
			
		||||
                <div class="flex-container flexnowrap">
 | 
			
		||||
                    <input id="expression_override" type="text" class="text_pole" placeholder="Override folder name" />
 | 
			
		||||
                    <input id="expression_override_button" class="menu_button" type="submit" value="Submit" />
 | 
			
		||||
@@ -67,17 +67,17 @@
 | 
			
		||||
                <div class="expression_buttons flex-container spaceEvenly">
 | 
			
		||||
                    <div id="expression_upload_pack_button" class="menu_button">
 | 
			
		||||
                        <i class="fa-solid fa-file-zipper"></i>
 | 
			
		||||
                        <span>Upload sprite pack (ZIP)</span>
 | 
			
		||||
                        <span data-i18n="Upload sprite pack (ZIP)">Upload sprite pack (ZIP)</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div id="expression_override_cleanup_button" class="menu_button">
 | 
			
		||||
                        <i class="fa-solid fa-trash-can"></i>
 | 
			
		||||
                        <span>Remove all image overrides</span>
 | 
			
		||||
                        <span data-i18n="Remove all image overrides">Remove all image overrides</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <p class="hint"><b>Hint:</b> <i>Create new folder in the <b>/characters/</b> folder of your user data directory and name it as the name of the character.
 | 
			
		||||
                Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p>
 | 
			
		||||
                <p class="hint"><b data-i18n="Hint:">Hint:</b> <i><span data-i18n="Create new folder in the _space">Create new folder in the </span><b>/characters/</b> <span data-i18n="folder of your user data directory and name it as the name of the character.">folder of your user data directory and name it as the name of the character.</span>
 | 
			
		||||
                <span data-i18n="Put images with expressions there. File names should follow the pattern:">Put images with expressions there. File names should follow the pattern: </span><tt data-i18n="expression_label_pattern">[expression_label].[image_format]</tt></i></p>
 | 
			
		||||
                <h3 id="image_list_header">
 | 
			
		||||
                    <strong>Sprite set:</strong> <span id="image_list_header_name"></span>
 | 
			
		||||
                    <strong data-i18n="Sprite set:">Sprite set:</strong> <span id="image_list_header_name"></span>
 | 
			
		||||
                </h3>
 | 
			
		||||
                <div id="image_list"></div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import {
 | 
			
		||||
    generateQuietPrompt,
 | 
			
		||||
    is_send_press,
 | 
			
		||||
    saveSettingsDebounced,
 | 
			
		||||
    substituteParams,
 | 
			
		||||
    substituteParamsExtended,
 | 
			
		||||
    generateRaw,
 | 
			
		||||
    getMaxContextSize,
 | 
			
		||||
} from '../../../script.js';
 | 
			
		||||
@@ -43,8 +43,7 @@ const formatMemoryValue = function (value) {
 | 
			
		||||
    value = value.trim();
 | 
			
		||||
 | 
			
		||||
    if (extension_settings.memory.template) {
 | 
			
		||||
        let result = extension_settings.memory.template.replace(/{{summary}}/i, value);
 | 
			
		||||
        return substituteParams(result);
 | 
			
		||||
        return substituteParamsExtended(extension_settings.memory.template, { summary: value });
 | 
			
		||||
    } else {
 | 
			
		||||
        return `Summary: ${value}`;
 | 
			
		||||
    }
 | 
			
		||||
@@ -447,7 +446,7 @@ async function summarizeCallback(args, text) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const source = args.source || extension_settings.memory.source;
 | 
			
		||||
    const prompt = substituteParams((resolveVariable(args.prompt) || extension_settings.memory.prompt)?.replace(/{{words}}/gi, extension_settings.memory.promptWords));
 | 
			
		||||
    const prompt = substituteParamsExtended((resolveVariable(args.prompt) || extension_settings.memory.prompt), { words: extension_settings.memory.promptWords });
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        switch (source) {
 | 
			
		||||
@@ -534,7 +533,7 @@ async function summarizeChatMain(context, force, skipWIAN) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log('Summarizing chat, messages since last summary: ' + messagesSinceLastSummary, 'words since last summary: ' + wordsSinceLastSummary);
 | 
			
		||||
    const prompt = extension_settings.memory.prompt?.replace(/{{words}}/gi, extension_settings.memory.promptWords);
 | 
			
		||||
    const prompt = substituteParamsExtended(extension_settings.memory.prompt, { words: extension_settings.memory.promptWords });
 | 
			
		||||
 | 
			
		||||
    if (!prompt) {
 | 
			
		||||
        console.debug('Summarization prompt is empty. Skipping summarization.');
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
    <div class="inline-drawer">
 | 
			
		||||
        <div class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
            <div class="flex-container alignitemscenter margin0">
 | 
			
		||||
                <b>Summarize</b>
 | 
			
		||||
                <b data-i18n="ext_sum_title">Summarize</b>
 | 
			
		||||
                <i id="summaryExtensionPopoutButton" class="fa-solid fa-window-restore menu_button margin0"></i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
 | 
			
		||||
                <textarea id="memory_contents" class="text_pole textarea_compact" rows="6" data-i18n="[placeholder]ext_sum_memory_placeholder" placeholder="Summary will be generated here..."></textarea>
 | 
			
		||||
                <div class="memory_contents_controls">
 | 
			
		||||
                    <div id="memory_force_summarize" data-summary-source="main" class="menu_button menu_button_icon" data-i18n="[title]ext_sum_force_tip" title="Trigger a summary update right now." data-i18n="Trigger a summary update right now.">
 | 
			
		||||
                    <div id="memory_force_summarize" data-summary-source="main" class="menu_button menu_button_icon" title="Trigger a summary update right now." data-i18n="[title]ext_sum_force_tip">
 | 
			
		||||
                        <i class="fa-solid fa-database"></i>
 | 
			
		||||
                        <span data-i18n="ext_sum_force_text">Summarize now</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,33 @@
 | 
			
		||||
<div id="qr--modalEditor">
 | 
			
		||||
	<div id="qr--main">
 | 
			
		||||
		<h3>Labels and Message</h3>
 | 
			
		||||
		<h3 data-i18n="Labels and Message">Labels and Message</h3>
 | 
			
		||||
		<div class="qr--labels">
 | 
			
		||||
			<label>
 | 
			
		||||
				<span class="qr--labelText">Label</span>
 | 
			
		||||
				<span class="qr--labelText" data-i18n="Label">Label</span>
 | 
			
		||||
				<input type="text" class="text_pole" id="qr--modal-label">
 | 
			
		||||
			</label>
 | 
			
		||||
			<label>
 | 
			
		||||
				<span class="qr--labelText">Title</span>
 | 
			
		||||
				<small class="qr--labelHint">(tooltip, leave empty to show message or /command)</small>
 | 
			
		||||
				<span class="qr--labelText" data-i18n="Title">Title</span>
 | 
			
		||||
				<small class="qr--labelHint" data-i18n="(tooltip, leave empty to show message or /command)">(tooltip, leave empty to show message or /command)</small>
 | 
			
		||||
				<input type="text" class="text_pole" id="qr--modal-title">
 | 
			
		||||
			</label>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="qr--modal-messageContainer">
 | 
			
		||||
			<label for="qr--modal-message">
 | 
			
		||||
			<label for="qr--modal-message" data-i18n="Message / Command:">
 | 
			
		||||
				Message / Command:
 | 
			
		||||
			</label>
 | 
			
		||||
			<div class="qr--modal-editorSettings">
 | 
			
		||||
				<label class="checkbox_label">
 | 
			
		||||
					<input type="checkbox" id="qr--modal-wrap">
 | 
			
		||||
					<span>Word wrap</span>
 | 
			
		||||
					<span data-i18n="Word wrap">Word wrap</span>
 | 
			
		||||
				</label>
 | 
			
		||||
				<label class="checkbox_label">
 | 
			
		||||
					<span>Tab size:</span>
 | 
			
		||||
					<span data-i18n="Tab size:">Tab size:</span>
 | 
			
		||||
					<input type="number" min="1" max="9" id="qr--modal-tabSize" class="text_pole">
 | 
			
		||||
				</label>
 | 
			
		||||
				<label class="checkbox_label">
 | 
			
		||||
					<input type="checkbox" id="qr--modal-executeShortcut">
 | 
			
		||||
					<span>Ctrl+Enter to execute</span>
 | 
			
		||||
					<span data-i18n="Ctrl+Enter to execute">Ctrl+Enter to execute</span>
 | 
			
		||||
				</label>
 | 
			
		||||
				<label class="checkbox_label">
 | 
			
		||||
					<input type="checkbox" id="qr--modal-syntax">
 | 
			
		||||
@@ -44,14 +44,14 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<div id="qr--qrOptions">
 | 
			
		||||
		<h3>Context Menu</h3>
 | 
			
		||||
		<h3 data-i18n="Context Menu">Context Menu</h3>
 | 
			
		||||
		<div id="qr--ctxEditor">
 | 
			
		||||
			<template id="qr--ctxItem">
 | 
			
		||||
				<div class="qr--ctxItem" data-order="0">
 | 
			
		||||
					<div class="drag-handle ui-sortable-handle">☰</div>
 | 
			
		||||
					<select class="qr--set"></select>
 | 
			
		||||
					<label class="qr--isChainedLabel checkbox_label" title="When enabled, the current Quick Reply will be sent together with (before) the clicked QR from the context menu.">
 | 
			
		||||
						Chaining:
 | 
			
		||||
						<span data-i18n="Chaining:">Chaining:</span>
 | 
			
		||||
						<input type="checkbox" class="qr--isChained">
 | 
			
		||||
					</label>
 | 
			
		||||
					<div class="qr--delete menu_button menu_button_icon fa-solid fa-trash-can" title="Remove entry"></div>
 | 
			
		||||
@@ -63,48 +63,48 @@
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<h3>Auto-Execute</h3>
 | 
			
		||||
		<h3 data-i18n="Auto-Execute">Auto-Execute</h3>
 | 
			
		||||
		<div class="flex-container flexFlowColumn">
 | 
			
		||||
			<label class="checkbox_label" title="Prevent this quick reply from triggering other auto-executed quick replies while auto-executing (i.e., prevent recursive auto-execution)">
 | 
			
		||||
				<input type="checkbox" id="qr--preventAutoExecute" >
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-plane-slash"></i> Don't trigger auto-execute</span>
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-plane-slash"></i><span data-i18n="Don't trigger auto-execute">Don't trigger auto-execute</span></span>
 | 
			
		||||
			</label>
 | 
			
		||||
			<label class="checkbox_label">
 | 
			
		||||
				<input type="checkbox" id="qr--isHidden" >
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-eye-slash"></i> Invisible (auto-execute only)</span>
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-eye-slash"></i><span data-i18n="Invisible (auto-execute only)">Invisible (auto-execute only)</span></span>
 | 
			
		||||
			</label>
 | 
			
		||||
			<label class="checkbox_label">
 | 
			
		||||
				<input type="checkbox" id="qr--executeOnStartup" >
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-rocket"></i> Execute on app startup</span>
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-rocket"></i><span data-i18n="Execute on startup">Execute on startup</span></span>
 | 
			
		||||
			</label>
 | 
			
		||||
			<label class="checkbox_label">
 | 
			
		||||
				<input type="checkbox" id="qr--executeOnUser" >
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-user"></i> Execute on user message</span>
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-user"></i><span data-i18n="Execute on user message">Execute on user message</span></span>
 | 
			
		||||
			</label>
 | 
			
		||||
			<label class="checkbox_label">
 | 
			
		||||
				<input type="checkbox" id="qr--executeOnAi" >
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-robot"></i> Execute on AI message</span>
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-robot"></i><span data-i18n="Execute on AI message">Execute on AI message</span></span>
 | 
			
		||||
			</label>
 | 
			
		||||
			<label class="checkbox_label">
 | 
			
		||||
				<input type="checkbox" id="qr--executeOnChatChange" >
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-message"></i> Execute on opening chat</span>
 | 
			
		||||
				<span><i class="fa-solid fa-fw fa-message"></i><span data-i18n="Execute on chat change">Execute on chat change</span></span>
 | 
			
		||||
			</label>
 | 
			
		||||
            <label class="checkbox_label">
 | 
			
		||||
                <input type="checkbox" id="qr--executeOnGroupMemberDraft">
 | 
			
		||||
                <span><i class="fa-solid fa-fw fa-people-group"></i> Execute before group member message</span>
 | 
			
		||||
                <span><i class="fa-solid fa-fw fa-people-group"></i><span data-i18n="Execute on group member draft">Execute on group member draft</span></span>
 | 
			
		||||
            </label>
 | 
			
		||||
            <div class="flex-container alignItemsBaseline flexFlowColumn flexNoGap" title="Activate this quick reply when a World Info entry with the same Automation ID is triggered.">
 | 
			
		||||
                <small>Automation ID</small>
 | 
			
		||||
                <small data-i18n="Automation ID:">Automation ID</small>
 | 
			
		||||
                <input type="text" id="qr--automationId" class="text_pole flex1" placeholder="( None )">
 | 
			
		||||
            </div>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<h3>Testing</h3>
 | 
			
		||||
		<h3 data-i18n="Testing">Testing</h3>
 | 
			
		||||
		<div id="qr--modal-executeButtons">
 | 
			
		||||
			<div id="qr--modal-execute" class="qr--modal-executeButton menu_button" title="Execute the quick reply now">
 | 
			
		||||
				<i class="fa-solid fa-play"></i>
 | 
			
		||||
				Execute
 | 
			
		||||
				<span data-i18n="Execute">Execute</span>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div id="qr--modal-pause" class="qr--modal-executeButton menu_button" title="Pause / continue execution">
 | 
			
		||||
				<span class="qr--modal-executeComboIcon">
 | 
			
		||||
@@ -119,7 +119,7 @@
 | 
			
		||||
		<div id="qr--modal-executeProgress"></div>
 | 
			
		||||
		<label class="checkbox_label">
 | 
			
		||||
			<input type="checkbox" id="qr--modal-executeHide">
 | 
			
		||||
			<span> Hide editor while executing</span>
 | 
			
		||||
			<span title="Hide editor while executing"> Hide editor while executing</span>
 | 
			
		||||
		</label>
 | 
			
		||||
		<div id="qr--modal-executeErrors"></div>
 | 
			
		||||
		<div id="qr--modal-executeResult"></div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,22 @@
 | 
			
		||||
<div id="qr--settings">
 | 
			
		||||
	<div class="inline-drawer">
 | 
			
		||||
		<div class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
			<strong>Quick Reply</strong>
 | 
			
		||||
			<strong data-i18n="Quick Reply">Quick Reply</strong>
 | 
			
		||||
			<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="inline-drawer-content">
 | 
			
		||||
			<label class="flex-container">
 | 
			
		||||
				<input type="checkbox" id="qr--isEnabled"> Enable Quick Replies
 | 
			
		||||
				<input type="checkbox" id="qr--isEnabled"><span data-i18n="Enable Quick Replies">Enable Quick Replies</span>
 | 
			
		||||
			</label>
 | 
			
		||||
			<label class="flex-container">
 | 
			
		||||
				<input type="checkbox" id="qr--isCombined"> Combine buttons from all active sets
 | 
			
		||||
				<input type="checkbox" id="qr--isCombined"><span data-i18n="Combine Quick Replies">Combine Quick Replies</span>
 | 
			
		||||
			</label>
 | 
			
		||||
 | 
			
		||||
			<hr>
 | 
			
		||||
 | 
			
		||||
			<div id="qr--global">
 | 
			
		||||
				<div class="qr--head">
 | 
			
		||||
					<div class="qr--title">Global Quick Reply Sets</div>
 | 
			
		||||
					<div class="qr--title" data-i18n="Global Quick Reply Sets">Global Quick Reply Sets</div>
 | 
			
		||||
					<div class="qr--actions">
 | 
			
		||||
						<div class="qr--setListAdd menu_button menu_button_icon fa-solid fa-plus" id="qr--global-setListAdd" title="Add quick reply set"></div>
 | 
			
		||||
					</div>
 | 
			
		||||
@@ -28,7 +28,7 @@
 | 
			
		||||
 | 
			
		||||
			<div id="qr--chat">
 | 
			
		||||
				<div class="qr--head">
 | 
			
		||||
					<div class="qr--title">Chat Quick Reply Sets</div>
 | 
			
		||||
					<div class="qr--title" data-i18n="Chat Quick Reply Sets">Chat Quick Reply Sets</div>
 | 
			
		||||
					<div class="qr--actions">
 | 
			
		||||
						<div class="qr--setListAdd menu_button menu_button_icon fa-solid fa-plus" id="qr--chat-setListAdd" title="Add quick reply set"></div>
 | 
			
		||||
					</div>
 | 
			
		||||
@@ -40,7 +40,7 @@
 | 
			
		||||
 | 
			
		||||
			<div id="qr--editor">
 | 
			
		||||
				<div class="qr--head">
 | 
			
		||||
					<div class="qr--title">Edit Quick Replies</div>
 | 
			
		||||
					<div class="qr--title" data-i18n="Edit Quick Replies">Edit Quick Replies</div>
 | 
			
		||||
					<div class="qr--actions">
 | 
			
		||||
						<select id="qr--set" class="text_pole"></select>
 | 
			
		||||
						<div class="qr--add menu_button menu_button_icon fa-solid fa-plus" id="qr--set-new" title="Create new quick reply set"></div>
 | 
			
		||||
@@ -52,13 +52,13 @@
 | 
			
		||||
				</div>
 | 
			
		||||
				<div id="qr--set-settings">
 | 
			
		||||
					<label class="flex-container">
 | 
			
		||||
						<input type="checkbox" id="qr--disableSend"> <span>Disable send (insert into input field)</span>
 | 
			
		||||
						<input type="checkbox" id="qr--disableSend"> <span data-i18n="Disable Send (Insert Into Input Field)">Disable send (insert into input field)</span>
 | 
			
		||||
					</label>
 | 
			
		||||
					<label class="flex-container">
 | 
			
		||||
						<input type="checkbox" id="qr--placeBeforeInput"> <span>Place quick reply before input</span>
 | 
			
		||||
						<input type="checkbox" id="qr--placeBeforeInput"> <span data-i18n="Place Quick Reply Before Input">Place quick reply before input</span>
 | 
			
		||||
					</label>
 | 
			
		||||
					<label class="flex-container" id="qr--injectInputContainer">
 | 
			
		||||
						<input type="checkbox" id="qr--injectInput"> <span>Inject user input automatically <small>(if disabled, use <code>{{input}}</code> macro for manual injection)</small></span>
 | 
			
		||||
						<input type="checkbox" id="qr--injectInput"> <span><span data-i18n="Inject user input automatically">Inject user input automatically</span> <small><span data-i18n="(if disabled, use ">(if disabled, use</span><code>{{input}}</code> <span data-i18n="macro for manual injection)">macro for manual injection)</span></small></span>
 | 
			
		||||
					</label>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div id="qr--set-qrList" class="qr--qrList"></div>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,11 +8,11 @@
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="inline-drawer-content">
 | 
			
		||||
            <div class="flex-container">
 | 
			
		||||
                <div id="open_regex_editor" class="menu_button menu_button_icon" title="New global regex script">
 | 
			
		||||
                <div id="open_regex_editor" class="menu_button menu_button_icon" data-i18n="[title]ext_regex_new_global_script_desc" title="New global regex script">
 | 
			
		||||
                    <i class="fa-solid fa-pen-to-square"></i>
 | 
			
		||||
                    <small data-i18n="ext_regex_new_global_script">+ Global</small>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div id="open_scoped_editor" class="menu_button menu_button_icon" title="New scoped regex script">
 | 
			
		||||
                <div id="open_scoped_editor" class="menu_button menu_button_icon" data-i18n="[title]ext_regex_new_scoped_script_desc" title="New scoped regex script">
 | 
			
		||||
                    <i class="fa-solid fa-address-card"></i>
 | 
			
		||||
                    <small data-i18n="ext_regex_new_scoped_script">+ Scoped</small>
 | 
			
		||||
                </div>
 | 
			
		||||
@@ -39,7 +39,7 @@
 | 
			
		||||
                    <label id="toggle_scoped_regex" class="checkbox flex-container" for="regex_scoped_toggle">
 | 
			
		||||
                        <input type="checkbox" id="regex_scoped_toggle" class="enable_scoped" />
 | 
			
		||||
                        <span class="regex-toggle-on fa-solid fa-toggle-on fa-lg" title="Disallow using scoped regex"></span>
 | 
			
		||||
                        <span class="regex-toggle-off fa-solid fa-toggle-off fa-lg" title="Allow using scoped regex"></span>
 | 
			
		||||
                        <span class="regex-toggle-off fa-solid fa-toggle-off fa-lg" data-i18n="[title]ext_regex_allow_scoped" title="Allow using scoped regex"></span>
 | 
			
		||||
                    </label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <small data-i18n="ext_regex_scoped_scripts_desc">
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import {
 | 
			
		||||
    getCharacterAvatar,
 | 
			
		||||
    formatCharacterAvatar,
 | 
			
		||||
    substituteParams,
 | 
			
		||||
    substituteParamsExtended,
 | 
			
		||||
} from '../../../script.js';
 | 
			
		||||
import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js';
 | 
			
		||||
import { selected_group } from '../../group-chats.js';
 | 
			
		||||
@@ -50,7 +51,15 @@ const sources = {
 | 
			
		||||
    pollinations: 'pollinations',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const initiators = {
 | 
			
		||||
    command: 'command',
 | 
			
		||||
    action: 'action',
 | 
			
		||||
    interactive: 'interactive',
 | 
			
		||||
    wand: 'wand',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const generationMode = {
 | 
			
		||||
    MESSAGE: -1,
 | 
			
		||||
    CHARACTER: 0,
 | 
			
		||||
    USER: 1,
 | 
			
		||||
    SCENARIO: 2,
 | 
			
		||||
@@ -62,6 +71,7 @@ const generationMode = {
 | 
			
		||||
    CHARACTER_MULTIMODAL: 8,
 | 
			
		||||
    USER_MULTIMODAL: 9,
 | 
			
		||||
    FACE_MULTIMODAL: 10,
 | 
			
		||||
    FREE_EXTENDED: 11,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const multimodalMap = {
 | 
			
		||||
@@ -71,6 +81,7 @@ const multimodalMap = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const modeLabels = {
 | 
			
		||||
    [generationMode.MESSAGE]: 'Chat Message Template',
 | 
			
		||||
    [generationMode.CHARACTER]: 'Character ("Yourself")',
 | 
			
		||||
    [generationMode.FACE]: 'Portrait ("Your Face")',
 | 
			
		||||
    [generationMode.USER]: 'User ("Me")',
 | 
			
		||||
@@ -81,6 +92,7 @@ const modeLabels = {
 | 
			
		||||
    [generationMode.CHARACTER_MULTIMODAL]: 'Character (Multimodal Mode)',
 | 
			
		||||
    [generationMode.FACE_MULTIMODAL]: 'Portrait (Multimodal Mode)',
 | 
			
		||||
    [generationMode.USER_MULTIMODAL]: 'User (Multimodal Mode)',
 | 
			
		||||
    [generationMode.FREE_EXTENDED]: 'Free Mode (LLM-Extended)',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const triggerWords = {
 | 
			
		||||
@@ -94,7 +106,7 @@ const triggerWords = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const messageTrigger = {
 | 
			
		||||
    activationRegex: /\b(send|mail|imagine|generate|make|create|draw|paint|render)\b.{0,10}\b(pic|picture|image|drawing|painting|photo|photograph)\b(?:\s+of)?(?:\s+(?:a|an|the|this|that|those)?)?(.+)/i,
 | 
			
		||||
    activationRegex: /\b(send|mail|imagine|generate|make|create|draw|paint|render|show)\b.{0,10}\b(pic|picture|image|drawing|painting|photo|photograph)\b(?:\s+of)?(?:\s+(?:a|an|the|this|that|those|your)?)?(.+)/i,
 | 
			
		||||
    specialCases: {
 | 
			
		||||
        [generationMode.CHARACTER]: ['you', 'yourself'],
 | 
			
		||||
        [generationMode.USER]: ['me', 'myself'],
 | 
			
		||||
@@ -106,6 +118,8 @@ const messageTrigger = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const promptTemplates = {
 | 
			
		||||
    // Not really a prompt template, rather an outcome message template
 | 
			
		||||
    [generationMode.MESSAGE]: '[{{char}} sends a picture that contains: {{prompt}}].',
 | 
			
		||||
    /*OLD:     [generationMode.CHARACTER]: "Pause your roleplay and provide comma-delimited list of phrases and keywords which describe {{char}}'s physical appearance and clothing. Ignore {{char}}'s personality traits, and chat history when crafting this description. End your response once the comma-delimited list is complete. Do not roleplay when writing this description, and do not attempt to continue the story.", */
 | 
			
		||||
    [generationMode.CHARACTER]: '[In the next response I want you to provide only a detailed comma-delimited list of keywords and phrases which describe {{char}}. The list must include all of the following items in this order: name, species and race, gender, age, clothing, occupation, physical features and appearances. Do not include descriptions of non-visual qualities such as personality, movements, scents, mental traits, or anything which could not be seen in a still photograph. Do not write in full sentences. Prefix your description with the phrase \'full body portrait,\']',
 | 
			
		||||
    //face-specific prompt
 | 
			
		||||
@@ -143,6 +157,7 @@ const promptTemplates = {
 | 
			
		||||
    [generationMode.FACE_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "close-up portrait".',
 | 
			
		||||
    [generationMode.CHARACTER_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "full body portrait".',
 | 
			
		||||
    [generationMode.USER_MULTIMODAL]: 'Provide an exhaustive comma-separated list of tags describing the appearance of the character on this image in great detail. Start with "full body portrait".',
 | 
			
		||||
    [generationMode.FREE_EXTENDED]: 'Pause your roleplay and provide an exhaustive comma-separated list of tags describing the appearance of "{0}" in great detail. Start with {{charPrefix}} (sic) if the subject is associated with {{char}}.',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const defaultPrefix = 'best quality, absurdres, aesthetic,';
 | 
			
		||||
@@ -204,6 +219,7 @@ const defaultSettings = {
 | 
			
		||||
    interactive_mode: false,
 | 
			
		||||
    multimodal_captioning: false,
 | 
			
		||||
    snap: false,
 | 
			
		||||
    free_extend: false,
 | 
			
		||||
 | 
			
		||||
    prompts: promptTemplates,
 | 
			
		||||
 | 
			
		||||
@@ -261,6 +277,11 @@ const defaultSettings = {
 | 
			
		||||
    // Pollinations settings
 | 
			
		||||
    pollinations_enhance: false,
 | 
			
		||||
    pollinations_refine: false,
 | 
			
		||||
 | 
			
		||||
    // Visibility toggles
 | 
			
		||||
    wand_visible: false,
 | 
			
		||||
    command_visible: false,
 | 
			
		||||
    interactive_visible: false,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const writePromptFieldsDebounced = debounce(writePromptFields, debounce_timeout.relaxed);
 | 
			
		||||
@@ -312,7 +333,7 @@ function processTriggers(chat, _, abort) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        abort(true);
 | 
			
		||||
        setTimeout(() => generatePicture('sd', subject, message), 1);
 | 
			
		||||
        setTimeout(() => generatePicture(initiators.interactive, {}, subject, message), 1);
 | 
			
		||||
    } catch {
 | 
			
		||||
        console.log('SD: Failed to process triggers.');
 | 
			
		||||
        return;
 | 
			
		||||
@@ -419,6 +440,10 @@ async function loadSettings() {
 | 
			
		||||
    $('#sd_clip_skip').val(extension_settings.sd.clip_skip);
 | 
			
		||||
    $('#sd_clip_skip_value').text(extension_settings.sd.clip_skip);
 | 
			
		||||
    $('#sd_seed').val(extension_settings.sd.seed);
 | 
			
		||||
    $('#sd_free_extend').prop('checked', extension_settings.sd.free_extend);
 | 
			
		||||
    $('#sd_wand_visible').prop('checked', extension_settings.sd.wand_visible);
 | 
			
		||||
    $('#sd_command_visible').prop('checked', extension_settings.sd.command_visible);
 | 
			
		||||
    $('#sd_interactive_visible').prop('checked', extension_settings.sd.interactive_visible);
 | 
			
		||||
 | 
			
		||||
    for (const style of extension_settings.sd.styles) {
 | 
			
		||||
        const option = document.createElement('option');
 | 
			
		||||
@@ -476,7 +501,7 @@ async function loadSettingOptions() {
 | 
			
		||||
function addPromptTemplates() {
 | 
			
		||||
    $('#sd_prompt_templates').empty();
 | 
			
		||||
 | 
			
		||||
    for (const [name, prompt] of Object.entries(extension_settings.sd.prompts)) {
 | 
			
		||||
    for (const [name, prompt] of Object.entries(extension_settings.sd.prompts).sort((a, b) => Number(a[0]) - Number(b[0]))) {
 | 
			
		||||
        const label = $('<label></label>')
 | 
			
		||||
            .text(modeLabels[name])
 | 
			
		||||
            .attr('for', `sd_prompt_${name}`)
 | 
			
		||||
@@ -683,6 +708,18 @@ function onChatChanged() {
 | 
			
		||||
    $('#sd_character_prompt').val(characterPrompt);
 | 
			
		||||
    $('#sd_character_negative_prompt').val(negativePrompt);
 | 
			
		||||
    $('#sd_character_prompt_share').prop('checked', hasSharedData);
 | 
			
		||||
    adjustElementScrollHeight();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function adjustElementScrollHeight(){
 | 
			
		||||
    if (!$('.sd_settings').is(':visible')) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resetScrollHeight($('#sd_prompt_prefix'));
 | 
			
		||||
    resetScrollHeight($('#sd_negative_prompt'));
 | 
			
		||||
    resetScrollHeight($('#sd_character_prompt'));
 | 
			
		||||
    resetScrollHeight($('#sd_character_negative_prompt'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onCharacterPromptInput() {
 | 
			
		||||
@@ -762,6 +799,26 @@ function onRefineModeInput() {
 | 
			
		||||
    saveSettingsDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onFreeExtendInput() {
 | 
			
		||||
    extension_settings.sd.free_extend = !!$('#sd_free_extend').prop('checked');
 | 
			
		||||
    saveSettingsDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onWandVisibleInput() {
 | 
			
		||||
    extension_settings.sd.wand_visible = !!$('#sd_wand_visible').prop('checked');
 | 
			
		||||
    saveSettingsDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onCommandVisibleInput() {
 | 
			
		||||
    extension_settings.sd.command_visible = !!$('#sd_command_visible').prop('checked');
 | 
			
		||||
    saveSettingsDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onInteractiveVisibleInput() {
 | 
			
		||||
    extension_settings.sd.interactive_visible = !!$('#sd_interactive_visible').prop('checked');
 | 
			
		||||
    saveSettingsDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onClipSkipInput() {
 | 
			
		||||
    extension_settings.sd.clip_skip = Number($('#sd_clip_skip').val());
 | 
			
		||||
    $('#sd_clip_skip_value').text(extension_settings.sd.clip_skip);
 | 
			
		||||
@@ -1026,6 +1083,7 @@ async function changeComfyWorkflow(_, name) {
 | 
			
		||||
    } else {
 | 
			
		||||
        toastr.error(`ComfyUI Workflow "${name}" does not exist.`);
 | 
			
		||||
    }
 | 
			
		||||
    return '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function validateAutoUrl() {
 | 
			
		||||
@@ -2016,6 +2074,10 @@ function getGenerationType(prompt) {
 | 
			
		||||
        mode = multimodalMap[mode];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (mode === generationMode.FREE && extension_settings.sd.free_extend) {
 | 
			
		||||
        mode = generationMode.FREE_EXTENDED;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2080,7 +2142,16 @@ function getRawLastMessage() {
 | 
			
		||||
    return `((${processReply(lastMessage.mes)})), (${processReply(character.scenario)}:0.7), (${processReply(character.description)}:0.5)`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function generatePicture(args, trigger, message, callback) {
 | 
			
		||||
/**
 | 
			
		||||
 * Generates an image based on the given trigger word.
 | 
			
		||||
 * @param {string} initiator The initiator of the image generation
 | 
			
		||||
 * @param {Record<string, object>} args Command arguments
 | 
			
		||||
 * @param {string} trigger Subject trigger word
 | 
			
		||||
 * @param {string} [message] Chat message
 | 
			
		||||
 * @param {function} [callback] Callback function
 | 
			
		||||
 * @returns {Promise<string>} Image path
 | 
			
		||||
 */
 | 
			
		||||
async function generatePicture(initiator, args, trigger, message, callback) {
 | 
			
		||||
    if (!trigger || trigger.trim().length === 0) {
 | 
			
		||||
        console.log('Trigger word empty, aborting');
 | 
			
		||||
        return;
 | 
			
		||||
@@ -2111,9 +2182,9 @@ async function generatePicture(args, trigger, message, callback) {
 | 
			
		||||
            eventSource.emit(event_types.FORCE_SET_BACKGROUND, { url: imgUrl, path: imagePath });
 | 
			
		||||
 | 
			
		||||
            if (typeof callbackOriginal === 'function') {
 | 
			
		||||
                callbackOriginal(prompt, imagePath, generationType, negativePromptPrefix);
 | 
			
		||||
                callbackOriginal(prompt, imagePath, generationType, negativePromptPrefix, initiator);
 | 
			
		||||
            } else {
 | 
			
		||||
                sendMessage(prompt, imagePath, generationType, negativePromptPrefix);
 | 
			
		||||
                sendMessage(prompt, imagePath, generationType, negativePromptPrefix, initiator);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
@@ -2122,18 +2193,19 @@ async function generatePicture(args, trigger, message, callback) {
 | 
			
		||||
        callback = () => { };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const negativePromptPrefix = resolveVariable(args?.negative) || '';
 | 
			
		||||
    const dimensions = setTypeSpecificDimensions(generationType);
 | 
			
		||||
    let negativePromptPrefix = resolveVariable(args?.negative) || '';
 | 
			
		||||
    let imagePath = '';
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        const prompt = await getPrompt(generationType, message, trigger, quietPrompt);
 | 
			
		||||
        const combineNegatives = (prefix) => { negativePromptPrefix = combinePrefixes(negativePromptPrefix, prefix); };
 | 
			
		||||
        const prompt = await getPrompt(generationType, message, trigger, quietPrompt, combineNegatives);
 | 
			
		||||
        console.log('Processed image prompt:', prompt);
 | 
			
		||||
 | 
			
		||||
        context.deactivateSendButtons();
 | 
			
		||||
        hideSwipeButtons();
 | 
			
		||||
 | 
			
		||||
        imagePath = await sendGenerationRequest(generationType, prompt, negativePromptPrefix, characterName, callback);
 | 
			
		||||
        imagePath = await sendGenerationRequest(generationType, prompt, negativePromptPrefix, characterName, callback, initiator);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        console.trace(err);
 | 
			
		||||
        throw new Error('SD prompt text generation failed.');
 | 
			
		||||
@@ -2196,7 +2268,16 @@ function restoreOriginalDimensions(savedParams) {
 | 
			
		||||
    extension_settings.sd.width = savedParams.width;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getPrompt(generationType, message, trigger, quietPrompt) {
 | 
			
		||||
/**
 | 
			
		||||
 * Generates a prompt for image generation.
 | 
			
		||||
 * @param {number} generationType The type of image generation to perform.
 | 
			
		||||
 * @param {string} message A message text to use for the image generation.
 | 
			
		||||
 * @param {string} trigger A trigger string to use for the image generation.
 | 
			
		||||
 * @param {string} quietPrompt A quiet prompt to use for the image generation.
 | 
			
		||||
 * @param {function} combineNegatives A function that combines the negative prompt with other prompts.
 | 
			
		||||
 * @returns {Promise<string>} - A promise that resolves when the prompt generation completes.
 | 
			
		||||
 */
 | 
			
		||||
async function getPrompt(generationType, message, trigger, quietPrompt, combineNegatives) {
 | 
			
		||||
    let prompt;
 | 
			
		||||
 | 
			
		||||
    switch (generationType) {
 | 
			
		||||
@@ -2204,7 +2285,7 @@ async function getPrompt(generationType, message, trigger, quietPrompt) {
 | 
			
		||||
            prompt = message || getRawLastMessage();
 | 
			
		||||
            break;
 | 
			
		||||
        case generationMode.FREE:
 | 
			
		||||
            prompt = generateFreeModePrompt(trigger.trim());
 | 
			
		||||
            prompt = generateFreeModePrompt(trigger.trim(), combineNegatives);
 | 
			
		||||
            break;
 | 
			
		||||
        case generationMode.FACE_MULTIMODAL:
 | 
			
		||||
        case generationMode.CHARACTER_MULTIMODAL:
 | 
			
		||||
@@ -2216,6 +2297,10 @@ async function getPrompt(generationType, message, trigger, quietPrompt) {
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (generationType === generationMode.FREE_EXTENDED) {
 | 
			
		||||
        prompt = generateFreeModePrompt(prompt.trim(), combineNegatives);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (generationType !== generationMode.FREE) {
 | 
			
		||||
        prompt = await refinePrompt(prompt, true);
 | 
			
		||||
    }
 | 
			
		||||
@@ -2226,9 +2311,10 @@ async function getPrompt(generationType, message, trigger, quietPrompt) {
 | 
			
		||||
/**
 | 
			
		||||
 * Generates a free prompt with a character-specific prompt prefix support.
 | 
			
		||||
 * @param {string} trigger - The prompt to use for the image generation.
 | 
			
		||||
 * @param {function} combineNegatives - A function that combines the negative prompt with other prompts.
 | 
			
		||||
 * @returns {string}
 | 
			
		||||
 */
 | 
			
		||||
function generateFreeModePrompt(trigger) {
 | 
			
		||||
function generateFreeModePrompt(trigger, combineNegatives) {
 | 
			
		||||
    return trigger
 | 
			
		||||
        .replace(/(?:^char(\s|,)|\{\{charPrefix\}\})/gi, (_, suffix) => {
 | 
			
		||||
            const getLastCharacterKey = () => {
 | 
			
		||||
@@ -2249,7 +2335,9 @@ function generateFreeModePrompt(trigger) {
 | 
			
		||||
 | 
			
		||||
            const key = getLastCharacterKey();
 | 
			
		||||
            const value = (extension_settings.sd.character_prompts[key] || '').trim();
 | 
			
		||||
            return value ? value + (suffix || '') : '';
 | 
			
		||||
            const negativeValue = (extension_settings.sd.character_negative_prompts[key] || '').trim();
 | 
			
		||||
            typeof combineNegatives === 'function' && negativeValue ? combineNegatives(negativeValue) : void 0;
 | 
			
		||||
            return value ? combinePrefixes(value, (suffix || '')) : '';
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2335,12 +2423,13 @@ async function generatePrompt(quietPrompt) {
 | 
			
		||||
 * @param {number} generationType Type of image generation
 | 
			
		||||
 * @param {string} prompt Prompt to be used for image generation
 | 
			
		||||
 * @param {string} additionalNegativePrefix Additional negative prompt to be used for image generation
 | 
			
		||||
 * @param {string} [characterName] Name of the character
 | 
			
		||||
 * @param {function} [callback] Callback function to be called after image generation
 | 
			
		||||
 * @param {string} characterName Name of the character
 | 
			
		||||
 * @param {function} callback Callback function to be called after image generation
 | 
			
		||||
 * @param {string} initiator The initiator of the image generation
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
async function sendGenerationRequest(generationType, prompt, additionalNegativePrefix, characterName = null, callback) {
 | 
			
		||||
    const noCharPrefix = [generationMode.FREE, generationMode.BACKGROUND, generationMode.USER, generationMode.USER_MULTIMODAL];
 | 
			
		||||
async function sendGenerationRequest(generationType, prompt, additionalNegativePrefix, characterName, callback, initiator) {
 | 
			
		||||
    const noCharPrefix = [generationMode.FREE, generationMode.BACKGROUND, generationMode.USER, generationMode.USER_MULTIMODAL, generationMode.FREE_EXTENDED];
 | 
			
		||||
    const prefix = noCharPrefix.includes(generationType)
 | 
			
		||||
        ? extension_settings.sd.prompt_prefix
 | 
			
		||||
        : combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix());
 | 
			
		||||
@@ -2405,7 +2494,7 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP
 | 
			
		||||
 | 
			
		||||
    const filename = `${characterName}_${humanizedDateTime()}`;
 | 
			
		||||
    const base64Image = await saveBase64AsFile(result.data, characterName, filename, result.format);
 | 
			
		||||
    callback ? callback(prompt, base64Image, generationType, additionalNegativePrefix) : sendMessage(prompt, base64Image, generationType, additionalNegativePrefix);
 | 
			
		||||
    callback ? callback(prompt, base64Image, generationType, additionalNegativePrefix, initiator) : sendMessage(prompt, base64Image, generationType, additionalNegativePrefix, initiator);
 | 
			
		||||
    return base64Image;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -3009,15 +3098,17 @@ async function onComfyDeleteWorkflowClick() {
 | 
			
		||||
 * @param {string} image Base64 encoded image
 | 
			
		||||
 * @param {number} generationType Generation type of the image
 | 
			
		||||
 * @param {string} additionalNegativePrefix Additional negative prompt used for the image generation
 | 
			
		||||
 * @param {string} initiator The initiator of the image generation
 | 
			
		||||
 */
 | 
			
		||||
async function sendMessage(prompt, image, generationType, additionalNegativePrefix) {
 | 
			
		||||
async function sendMessage(prompt, image, generationType, additionalNegativePrefix, initiator) {
 | 
			
		||||
    const context = getContext();
 | 
			
		||||
    const name = context.groupId ? systemUserName : context.name2;
 | 
			
		||||
    const messageText = `[${name} sends a picture that contains: ${prompt}]`;
 | 
			
		||||
    const template = extension_settings.sd.prompts[generationMode.MESSAGE] || '{{prompt}}';
 | 
			
		||||
    const messageText = substituteParamsExtended(template, { char: name, prompt: prompt });
 | 
			
		||||
    const message = {
 | 
			
		||||
        name: name,
 | 
			
		||||
        is_user: false,
 | 
			
		||||
        is_system: true,
 | 
			
		||||
        is_system: !getVisibilityByInitiator(initiator),
 | 
			
		||||
        send_date: getMessageTimeStamp(),
 | 
			
		||||
        mes: messageText,
 | 
			
		||||
        extra: {
 | 
			
		||||
@@ -3033,6 +3124,24 @@ async function sendMessage(prompt, image, generationType, additionalNegativePref
 | 
			
		||||
    context.saveChat();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the visibility of the resulting message based on the initiator.
 | 
			
		||||
 * @param {string} initiator Generation initiator
 | 
			
		||||
 * @returns {boolean} Is resulting message visible
 | 
			
		||||
 */
 | 
			
		||||
function getVisibilityByInitiator(initiator) {
 | 
			
		||||
    switch (initiator) {
 | 
			
		||||
        case initiators.interactive:
 | 
			
		||||
            return !!extension_settings.sd.interactive_visible;
 | 
			
		||||
        case initiators.wand:
 | 
			
		||||
            return !!extension_settings.sd.wand_visible;
 | 
			
		||||
        case initiators.command:
 | 
			
		||||
            return !!extension_settings.sd.command_visible;
 | 
			
		||||
        default:
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function addSDGenButtons() {
 | 
			
		||||
    const buttonHtml = await renderExtensionTemplateAsync('stable-diffusion', 'button');
 | 
			
		||||
    const dropdownHtml = await renderExtensionTemplateAsync('stable-diffusion', 'dropdown');
 | 
			
		||||
@@ -3082,7 +3191,7 @@ async function addSDGenButtons() {
 | 
			
		||||
 | 
			
		||||
        if (param) {
 | 
			
		||||
            console.log('doing /sd ' + param);
 | 
			
		||||
            generatePicture('sd', param);
 | 
			
		||||
            generatePicture(initiators.wand, {}, param);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -3159,11 +3268,11 @@ async function sdMessageButton(e) {
 | 
			
		||||
            const generationType = message?.extra?.generationType ?? generationMode.FREE;
 | 
			
		||||
            console.log('Regenerating an image, using existing prompt:', prompt);
 | 
			
		||||
            dimensions = setTypeSpecificDimensions(generationType);
 | 
			
		||||
            await sendGenerationRequest(generationType, prompt, negative, characterFileName, saveGeneratedImage);
 | 
			
		||||
            await sendGenerationRequest(generationType, prompt, negative, characterFileName, saveGeneratedImage, initiators.action);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            console.log('doing /sd raw last');
 | 
			
		||||
            await generatePicture('sd', 'raw_last', messageText, saveGeneratedImage);
 | 
			
		||||
            await generatePicture(initiators.action, {}, 'raw_last', messageText, saveGeneratedImage);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    catch (error) {
 | 
			
		||||
@@ -3226,7 +3335,7 @@ jQuery(async () => {
 | 
			
		||||
 | 
			
		||||
    SlashCommandParser.addCommandObject(SlashCommand.fromProps({
 | 
			
		||||
        name: 'imagine',
 | 
			
		||||
        callback: generatePicture,
 | 
			
		||||
        callback: (args, trigger) => generatePicture(initiators.command, args, String(trigger)),
 | 
			
		||||
        aliases: ['sd', 'img', 'image'],
 | 
			
		||||
        namedArgumentList: [
 | 
			
		||||
            new SlashCommandNamedArgument(
 | 
			
		||||
@@ -3326,6 +3435,10 @@ jQuery(async () => {
 | 
			
		||||
    $('#sd_clip_skip').on('input', onClipSkipInput);
 | 
			
		||||
    $('#sd_seed').on('input', onSeedInput);
 | 
			
		||||
    $('#sd_character_prompt_share').on('input', onCharacterPromptShareInput);
 | 
			
		||||
    $('#sd_free_extend').on('input', onFreeExtendInput);
 | 
			
		||||
    $('#sd_wand_visible').on('input', onWandVisibleInput);
 | 
			
		||||
    $('#sd_command_visible').on('input', onCommandVisibleInput);
 | 
			
		||||
    $('#sd_interactive_visible').on('input', onInteractiveVisibleInput);
 | 
			
		||||
 | 
			
		||||
    $('.sd_settings .inline-drawer-toggle').on('click', function () {
 | 
			
		||||
        initScrollHeight($('#sd_prompt_prefix'));
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,11 @@
 | 
			
		||||
                <input id="sd_multimodal_captioning" type="checkbox" />
 | 
			
		||||
                <span data-i18n="sd_multimodal_captioning_txt">Use multimodal captioning for portraits</span>
 | 
			
		||||
            </label>
 | 
			
		||||
            <label for="sd_free_extend" class="checkbox_label" data-i18n="[title]sd_free_extend" title="Automatically extend free mode subject prompts (not portraits or backgrounds) using a currently selected LLM.">
 | 
			
		||||
                <input id="sd_free_extend" type="checkbox" />
 | 
			
		||||
                <span data-i18n="sd_free_extend_txt">Extend free mode prompts</span>
 | 
			
		||||
                <small data-i18n="sd_free_extend_small">(interactive/commands)</small>
 | 
			
		||||
            </label>
 | 
			
		||||
            <label for="sd_expand" class="checkbox_label" data-i18n="[title]sd_expand" title="Automatically extend prompts using text generation model">
 | 
			
		||||
                <input id="sd_expand" type="checkbox" />
 | 
			
		||||
                <span data-i18n="sd_expand_txt">Auto-enhance prompts</span>
 | 
			
		||||
@@ -279,16 +284,16 @@
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <label for="sd_prompt_prefix" data-i18n="Common prompt prefix">Common prompt prefix</label>
 | 
			
		||||
            <textarea id="sd_prompt_prefix" class="text_pole textarea_compact" rows="3" data-i18n="[placeholder]sd_prompt_prefix_placeholder" placeholder="Use {prompt} to specify where the generated prompt will be inserted"></textarea>
 | 
			
		||||
            <textarea id="sd_prompt_prefix" class="text_pole textarea_compact" data-i18n="[placeholder]sd_prompt_prefix_placeholder" placeholder="Use {prompt} to specify where the generated prompt will be inserted"></textarea>
 | 
			
		||||
            <label for="sd_negative_prompt" data-i18n="Negative common prompt prefix">Negative common prompt prefix</label>
 | 
			
		||||
            <textarea id="sd_negative_prompt" class="text_pole textarea_compact" rows="3"></textarea>
 | 
			
		||||
            <textarea id="sd_negative_prompt" class="text_pole textarea_compact"></textarea>
 | 
			
		||||
            <div id="sd_character_prompt_block">
 | 
			
		||||
                <label for="sd_character_prompt" data-i18n="Character-specific prompt prefix">Character-specific prompt prefix</label>
 | 
			
		||||
                <small data-i18n="Won't be used in groups.">Won't be used in groups.</small>
 | 
			
		||||
                <textarea id="sd_character_prompt" class="text_pole textarea_compact" rows="3" data-i18n="[placeholder]sd_character_prompt_placeholder" placeholder="Any characteristics that describe the currently selected character. Will be added after a common prompt prefix.
Example: female, green eyes, brown hair, pink shirt"></textarea>
 | 
			
		||||
                <textarea id="sd_character_prompt" class="text_pole textarea_compact" data-i18n="[placeholder]sd_character_prompt_placeholder" placeholder="Any characteristics that describe the currently selected character. Will be added after a common prompt prefix.
Example: female, green eyes, brown hair, pink shirt"></textarea>
 | 
			
		||||
                <label for="sd_character_negative_prompt" data-i18n="Character-specific negative prompt prefix">Character-specific negative prompt prefix</label>
 | 
			
		||||
                <small data-i18n="Won't be used in groups.">Won't be used in groups.</small>
 | 
			
		||||
                <textarea id="sd_character_negative_prompt" class="text_pole textarea_compact" rows="3" data-i18n="[placeholder]sd_character_negative_prompt_placeholder" placeholder="Any characteristics that should not appear for the selected character. Will be added after a negative common prompt prefix.
Example: jewellery, shoes, glasses"></textarea>
 | 
			
		||||
                <textarea id="sd_character_negative_prompt" class="text_pole textarea_compact" data-i18n="[placeholder]sd_character_negative_prompt_placeholder" placeholder="Any characteristics that should not appear for the selected character. Will be added after a negative common prompt prefix.
Example: jewellery, shoes, glasses"></textarea>
 | 
			
		||||
                <label for="sd_character_prompt_share" class="checkbox_label flexWrap marginTop5">
 | 
			
		||||
                    <input id="sd_character_prompt_share" type="checkbox" />
 | 
			
		||||
                    <span data-i18n="Shareable">
 | 
			
		||||
@@ -299,6 +304,36 @@
 | 
			
		||||
                    </small>
 | 
			
		||||
                </label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <hr>
 | 
			
		||||
            <h4 data-i18n="Chat Message Visibility (by source)">
 | 
			
		||||
                Chat Message Visibility (by source)
 | 
			
		||||
            </h4>
 | 
			
		||||
            <small data-i18n="Uncheck to hide the extension's messages in chat prompts.">
 | 
			
		||||
                Uncheck to hide the extension's messages in chat prompts.
 | 
			
		||||
            </small>
 | 
			
		||||
            <div class="flex-container flexFlowColumn marginTopBot5 flexGap10">
 | 
			
		||||
                <label for="sd_wand_visible" class="checkbox_label">
 | 
			
		||||
                    <span class="flex1 flex-container alignItemsCenter">
 | 
			
		||||
                        <i class="fa-solid fa-wand-magic-sparkles"></i>
 | 
			
		||||
                        <span data-i18n="Extensions Menu">Extensions Menu</span>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <input id="sd_wand_visible" type="checkbox" />
 | 
			
		||||
                </label>
 | 
			
		||||
                <label for="sd_command_visible" class="checkbox_label">
 | 
			
		||||
                    <span class="flex1 flex-container alignItemsCenter">
 | 
			
		||||
                        <i class="fa-solid fa-terminal"></i>
 | 
			
		||||
                        <span data-i18n="Slash Command">Slash Command</span>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <input id="sd_command_visible" type="checkbox" />
 | 
			
		||||
                </label>
 | 
			
		||||
                <label for="sd_interactive_visible" class="checkbox_label">
 | 
			
		||||
                    <span class="flex1 flex-container alignItemsCenter">
 | 
			
		||||
                        <i class="fa-solid fa-message"></i>
 | 
			
		||||
                        <span data-i18n="Interactive Mode">Interactive Mode</span>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <input id="sd_interactive_visible" type="checkbox" />
 | 
			
		||||
                </label>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="inline-drawer">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								public/scripts/extensions/translate/buttons.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								public/scripts/extensions/translate/buttons.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
<div id="translate_chat" class="list-group-item flex-container flexGap5">
 | 
			
		||||
    <div class="fa-solid fa-language extensionsMenuExtensionButton" /></div>
 | 
			
		||||
    <span data-i18n="ext_translate_btn_chat">Translate Chat</span>
 | 
			
		||||
</div>
 | 
			
		||||
<div id="translate_input_message" class="list-group-item flex-container flexGap5">
 | 
			
		||||
    <div class="fa-solid fa-keyboard extensionsMenuExtensionButton" /></div>
 | 
			
		||||
    <span data-i18n="ext_translate_btn_input">Translate Input</span>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
<h3 data-i18n="ext_translate_delete_confirm_1">Are you sure?</h3><span data-i18n="ext_translate_delete_confirm_2">This will remove translated text from all messages in the current chat. This action cannot be undone.</span>
 | 
			
		||||
							
								
								
									
										38
									
								
								public/scripts/extensions/translate/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								public/scripts/extensions/translate/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
<div class="translation_settings">
 | 
			
		||||
    <div class="inline-drawer">
 | 
			
		||||
        <div class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
            <b data-i18n="ext_translate_title">Chat Translation</b>
 | 
			
		||||
            <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="inline-drawer-content">
 | 
			
		||||
            <label for="translation_auto_mode" class="checkbox_label" data-i18n="ext_translate_auto_mode">Auto-mode</label>
 | 
			
		||||
            <select id="translation_auto_mode">
 | 
			
		||||
                <option data-i18n="ext_translate_mode_none" value="none">None</option>
 | 
			
		||||
                <option data-i18n="ext_translate_mode_responses" value="responses">Translate responses</option>
 | 
			
		||||
                <option data-i18n="ext_translate_mode_inputs" value="inputs">Translate inputs</option>
 | 
			
		||||
                <option data-i18n="ext_translate_mode_both" value="both">Translate both</option>
 | 
			
		||||
            </select>
 | 
			
		||||
            <label data-i18n="ext_translate_mode_provider" for="translation_provider">Provider</label>
 | 
			
		||||
            <div class="flex-container gap5px flexnowrap marginBot5">
 | 
			
		||||
                <select id="translation_provider" name="provider" class="margin0">
 | 
			
		||||
                    <option value="libre">Libre</option>
 | 
			
		||||
                    <option value="google">Google</option>
 | 
			
		||||
                    <option value="lingva">Lingva</option>
 | 
			
		||||
                    <option value="deepl">DeepL</option>
 | 
			
		||||
                    <option value="deeplx">DeepLX</option>
 | 
			
		||||
                    <option value="bing">Bing</option>
 | 
			
		||||
                    <option value="oneringtranslator">OneRingTranslator</option>
 | 
			
		||||
                    <option value="yandex">Yandex</option>
 | 
			
		||||
                <select>
 | 
			
		||||
                <div id="translate_key_button" class="menu_button fa-solid fa-key margin0"></div>
 | 
			
		||||
                <div id="translate_url_button" class="menu_button fa-solid fa-link margin0"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <label data-i18n="ext_translate_target_lang" for="translation_target_language">Target Language</label>
 | 
			
		||||
            <select id="translation_target_language" name="target_language"></select>
 | 
			
		||||
            <div id="translation_clear" class="menu_button">
 | 
			
		||||
                <i class="fa-solid fa-trash-can"></i>
 | 
			
		||||
                <span data-i18n="ext_translate_clear">Clear Translations</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -10,13 +10,12 @@ import {
 | 
			
		||||
    substituteParams,
 | 
			
		||||
    updateMessageBlock,
 | 
			
		||||
} from '../../../script.js';
 | 
			
		||||
import { extension_settings, getContext } from '../../extensions.js';
 | 
			
		||||
import { extension_settings, getContext, renderExtensionTemplateAsync } from '../../extensions.js';
 | 
			
		||||
import { findSecret, secret_state, writeSecret } from '../../secrets.js';
 | 
			
		||||
import { SlashCommand } from '../../slash-commands/SlashCommand.js';
 | 
			
		||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
 | 
			
		||||
import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
 | 
			
		||||
import { splitRecursive } from '../../utils.js';
 | 
			
		||||
import { renderTemplateAsync } from '../../templates.js';
 | 
			
		||||
 | 
			
		||||
export const autoModeOptions = {
 | 
			
		||||
    NONE: 'none',
 | 
			
		||||
@@ -505,7 +504,8 @@ async function onTranslateChatClick() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function onTranslationsClearClick() {
 | 
			
		||||
    const confirm = await callPopup('<h3>Are you sure?</h3>This will remove translated text from all messages in the current chat. This action cannot be undone.', 'confirm');
 | 
			
		||||
    const popupHtml = await renderExtensionTemplateAsync('translate', 'deleteConfirmation');
 | 
			
		||||
    const confirm = await callPopup(popupHtml, 'confirm');
 | 
			
		||||
 | 
			
		||||
    if (!confirm) {
 | 
			
		||||
        return;
 | 
			
		||||
@@ -563,10 +563,10 @@ const handleMessageEdit = createEventHandler(translateMessageEdit, () => true);
 | 
			
		||||
 | 
			
		||||
window['translate'] = translate;
 | 
			
		||||
 | 
			
		||||
jQuery(async() => {
 | 
			
		||||
    const html = await renderTemplateAsync('translateIndex');
 | 
			
		||||
jQuery(async () => {
 | 
			
		||||
    const html = await renderExtensionTemplateAsync('translate', 'index');
 | 
			
		||||
 | 
			
		||||
    const buttonHtml = await renderTemplateAsync('translateButtons');
 | 
			
		||||
    const buttonHtml = await renderExtensionTemplateAsync('translate', 'buttons');
 | 
			
		||||
    $('#extensionsMenu').append(buttonHtml);
 | 
			
		||||
    $('#extensions_settings2').append(html);
 | 
			
		||||
    $('#translate_chat').on('click', onTranslateChatClick);
 | 
			
		||||
@@ -609,7 +609,7 @@ jQuery(async() => {
 | 
			
		||||
            'libre': 'http://127.0.0.1:5000/translate',
 | 
			
		||||
            'lingva': 'https://lingva.ml/api/v1',
 | 
			
		||||
            'oneringtranslator': 'http://127.0.0.1:4990/translate',
 | 
			
		||||
            'deeplx': 'http://127.0.0.1:1188/translate'
 | 
			
		||||
            'deeplx': 'http://127.0.0.1:1188/translate',
 | 
			
		||||
        };
 | 
			
		||||
        const popupText = `<h3>${optionText} API URL</h3><i>Example: <tt>${String(exampleURLs[extension_settings.translate.provider])}</tt></i>`;
 | 
			
		||||
 | 
			
		||||
@@ -653,5 +653,6 @@ jQuery(async() => {
 | 
			
		||||
                : extension_settings.translate.target_language;
 | 
			
		||||
            return await translate(String(value), target);
 | 
			
		||||
        },
 | 
			
		||||
        returns: ARGUMENT_TYPE.STRING,
 | 
			
		||||
    }));
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import {
 | 
			
		||||
    setExtensionPrompt,
 | 
			
		||||
    substituteParams,
 | 
			
		||||
    generateRaw,
 | 
			
		||||
    substituteParamsExtended,
 | 
			
		||||
} from '../../../script.js';
 | 
			
		||||
import {
 | 
			
		||||
    ModuleWorkerWrapper,
 | 
			
		||||
@@ -49,6 +50,7 @@ const settings = {
 | 
			
		||||
    summarize_sent: false,
 | 
			
		||||
    summary_source: 'main',
 | 
			
		||||
    summary_prompt: 'Pause your roleplay. Summarize the most important parts of the message. Limit yourself to 250 words or less. Your response should include nothing but the summary.',
 | 
			
		||||
    force_chunk_delimiter: '',
 | 
			
		||||
 | 
			
		||||
    // For chats
 | 
			
		||||
    enabled_chats: false,
 | 
			
		||||
@@ -152,6 +154,20 @@ async function onVectorizeAllClick() {
 | 
			
		||||
 | 
			
		||||
let syncBlocked = false;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the chunk delimiters for splitting text.
 | 
			
		||||
 * @returns {string[]} Array of chunk delimiters
 | 
			
		||||
 */
 | 
			
		||||
function getChunkDelimiters() {
 | 
			
		||||
    const delimiters = ['\n\n', '\n', ' ', ''];
 | 
			
		||||
 | 
			
		||||
    if (settings.force_chunk_delimiter) {
 | 
			
		||||
        delimiters.unshift(settings.force_chunk_delimiter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return delimiters;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Splits messages into chunks before inserting them into the vector index.
 | 
			
		||||
 * @param {object[]} items Array of vector items
 | 
			
		||||
@@ -165,7 +181,7 @@ function splitByChunks(items) {
 | 
			
		||||
    const chunkedItems = [];
 | 
			
		||||
 | 
			
		||||
    for (const item of items) {
 | 
			
		||||
        const chunks = splitRecursive(item.text, settings.message_chunk_size);
 | 
			
		||||
        const chunks = splitRecursive(item.text, settings.message_chunk_size, getChunkDelimiters());
 | 
			
		||||
        for (const chunk of chunks) {
 | 
			
		||||
            const chunkedItem = { ...item, text: chunk };
 | 
			
		||||
            chunkedItems.push(chunkedItem);
 | 
			
		||||
@@ -441,7 +457,7 @@ async function injectDataBankChunks(queryText, collectionIds) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const insertedText = substituteParams(settings.file_template_db.replace(/{{text}}/i, textResult));
 | 
			
		||||
        const insertedText = substituteParamsExtended(settings.file_template_db, { text: textResult });
 | 
			
		||||
        setExtensionPrompt(EXTENSION_PROMPT_TAG_DB, insertedText, settings.file_position_db, settings.file_depth_db, settings.include_wi, settings.file_depth_role_db);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error('Vectors: Failed to insert Data Bank chunks', error);
 | 
			
		||||
@@ -483,9 +499,10 @@ async function vectorizeFile(fileText, fileName, collectionId, chunkSize, overla
 | 
			
		||||
 | 
			
		||||
        const toast = toastr.info('Vectorization may take some time, please wait...', `Ingesting file ${fileName}`);
 | 
			
		||||
        const overlapSize = Math.round(chunkSize * overlapPercent / 100);
 | 
			
		||||
        const delimiters = getChunkDelimiters();
 | 
			
		||||
        // Overlap should not be included in chunk size. It will be later compensated by overlapChunks
 | 
			
		||||
        chunkSize = overlapSize > 0 ? (chunkSize - overlapSize) : chunkSize;
 | 
			
		||||
        const chunks = splitRecursive(fileText, chunkSize).map((x, y, z) => overlapSize > 0 ? overlapChunks(x, y, z, overlapSize) : x);
 | 
			
		||||
        const chunks = splitRecursive(fileText, chunkSize, delimiters).map((x, y, z) => overlapSize > 0 ? overlapChunks(x, y, z, overlapSize) : x);
 | 
			
		||||
        console.debug(`Vectors: Split file ${fileName} into ${chunks.length} chunks with ${overlapPercent}% overlap`, chunks);
 | 
			
		||||
 | 
			
		||||
        const items = chunks.map((chunk, index) => ({ hash: getStringHash(chunk), text: chunk, index: index }));
 | 
			
		||||
@@ -592,7 +609,7 @@ async function rearrangeChat(chat) {
 | 
			
		||||
function getPromptText(queriedMessages) {
 | 
			
		||||
    const queriedText = queriedMessages.map(x => collapseNewlines(`${x.name}: ${x.mes}`).trim()).join('\n\n');
 | 
			
		||||
    console.log('Vectors: relevant past messages found.\n', queriedText);
 | 
			
		||||
    return substituteParams(settings.template.replace(/{{text}}/i, queriedText));
 | 
			
		||||
    return substituteParamsExtended(settings.template, { text: queriedText });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -1479,6 +1496,12 @@ jQuery(async () => {
 | 
			
		||||
        saveSettingsDebounced();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $('#vectors_force_chunk_delimiter').prop('checked', settings.force_chunk_delimiter).on('input', () => {
 | 
			
		||||
        settings.force_chunk_delimiter = String($('#vectors_force_chunk_delimiter').val());
 | 
			
		||||
        Object.assign(extension_settings.vectors, settings);
 | 
			
		||||
        saveSettingsDebounced();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const validSecret = !!secret_state[SECRET_KEYS.NOMICAI];
 | 
			
		||||
    const placeholder = validSecret ? '✔️ Key saved' : '❌ Missing key';
 | 
			
		||||
    $('#api_key_nomicai').attr('placeholder', placeholder);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
<div class="vectors_settings">
 | 
			
		||||
    <div class="inline-drawer">
 | 
			
		||||
        <div class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
            <b>Vector Storage</b>
 | 
			
		||||
            <b data-i18n="Vector Storage">Vector Storage</b>
 | 
			
		||||
            <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="inline-drawer-content">
 | 
			
		||||
            <div class="flex-container flexFlowColumn">
 | 
			
		||||
                <label for="vectors_source">
 | 
			
		||||
                <label for="vectors_source" data-i18n="Vectorization Source">
 | 
			
		||||
                    Vectorization Source
 | 
			
		||||
                </label>
 | 
			
		||||
                <select id="vectors_source" class="text_pole">
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
                    <option value="extras">Extras</option>
 | 
			
		||||
                    <option value="palm">Google MakerSuite</option>
 | 
			
		||||
                    <option value="llamacpp">llama.cpp</option>
 | 
			
		||||
                    <option value="transformers">Local (Transformers)</option>
 | 
			
		||||
                    <option value="transformers" data-i18n="Local (Transformers)">Local (Transformers)</option>
 | 
			
		||||
                    <option value="mistral">MistralAI</option>
 | 
			
		||||
                    <option value="nomicai">NomicAI</option>
 | 
			
		||||
                    <option value="ollama">Ollama</option>
 | 
			
		||||
@@ -24,28 +24,28 @@
 | 
			
		||||
                </select>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flex-container flexFlowColumn" id="ollama_vectorsModel">
 | 
			
		||||
                <label for="vectors_ollama_model">
 | 
			
		||||
                <label for="vectors_ollama_model" data-i18n="Vectorization Model">
 | 
			
		||||
                    Vectorization Model
 | 
			
		||||
                </label>
 | 
			
		||||
                <input id="vectors_ollama_model" class="text_pole" type="text" placeholder="Model tag, e.g. llama3" />
 | 
			
		||||
                <label for="vectors_ollama_keep" class="checkbox_label" title="When checked, the model will not be unloaded after use.">
 | 
			
		||||
                    <input id="vectors_ollama_keep" type="checkbox" />
 | 
			
		||||
                    <span>Keep model in memory</span>
 | 
			
		||||
                    <span data-i18n="Keep model in memory">Keep model in memory</span>
 | 
			
		||||
                </label>
 | 
			
		||||
                <i>
 | 
			
		||||
                <i data-i18n="Hint: Download models and set the URL in the API connection settings.">
 | 
			
		||||
                    Hint: Download models and set the URL in the API connection settings.
 | 
			
		||||
                </i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flex-container flexFlowColumn" id="llamacpp_vectorsModel">
 | 
			
		||||
                <span>
 | 
			
		||||
                <span data-i18n="The server MUST be started with the --embedding flag to use this feature!">
 | 
			
		||||
                    The server MUST be started with the <code>--embedding</code> flag to use this feature!
 | 
			
		||||
                </span>
 | 
			
		||||
                <i>
 | 
			
		||||
                <i data-i18n="Hint: Set the URL in the API connection settings.">
 | 
			
		||||
                    Hint: Set the URL in the API connection settings.
 | 
			
		||||
                </i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flex-container flexFlowColumn" id="openai_vectorsModel">
 | 
			
		||||
                <label for="vectors_openai_model">
 | 
			
		||||
                <label for="vectors_openai_model" data-i18n="Vectorization Model">
 | 
			
		||||
                    Vectorization Model
 | 
			
		||||
                </label>
 | 
			
		||||
                <select id="vectors_openai_model" class="text_pole">
 | 
			
		||||
@@ -55,7 +55,7 @@
 | 
			
		||||
                </select>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flex-container flexFlowColumn" id="cohere_vectorsModel">
 | 
			
		||||
                <label for="vectors_cohere_model">
 | 
			
		||||
                <label for="vectors_cohere_model" data-i18n="Vectorization Model">
 | 
			
		||||
                    Vectorization Model
 | 
			
		||||
                </label>
 | 
			
		||||
                <select id="vectors_cohere_model" class="text_pole">
 | 
			
		||||
@@ -69,7 +69,7 @@
 | 
			
		||||
                </select>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flex-container flexFlowColumn" id="together_vectorsModel">
 | 
			
		||||
                <label for="vectors_togetherai_model">
 | 
			
		||||
                <label for="vectors_togetherai_model" data-i18n="Vectorization Model">
 | 
			
		||||
                    Vectorization Model
 | 
			
		||||
                </label>
 | 
			
		||||
                <select id="vectors_togetherai_model" class="text_pole">
 | 
			
		||||
@@ -84,11 +84,11 @@
 | 
			
		||||
                </select>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="flex-container flexFlowColumn" id="vllm_vectorsModel">
 | 
			
		||||
                <label for="vectors_vllm_model">
 | 
			
		||||
                <label for="vectors_vllm_model" data-i18n="Vectorization Model">
 | 
			
		||||
                    Vectorization Model
 | 
			
		||||
                </label>
 | 
			
		||||
                <input id="vectors_vllm_model" class="text_pole" type="text" placeholder="Model name, e.g. intfloat/e5-mistral-7b-instruct" />
 | 
			
		||||
                <i>
 | 
			
		||||
                <i data-i18n="Hint: Set the URL in the API connection settings.">
 | 
			
		||||
                    Hint: Set the URL in the API connection settings.
 | 
			
		||||
                </i>
 | 
			
		||||
            </div>
 | 
			
		||||
@@ -102,7 +102,7 @@
 | 
			
		||||
 | 
			
		||||
            <div class="flex-container flexFlowColumn" id="nomicai_apiKey">
 | 
			
		||||
                <label for="api_key_nomicai">
 | 
			
		||||
                    <span>NomicAI API Key</span>
 | 
			
		||||
                    <span data-i18n="NomicAI API Key">NomicAI API Key</span>
 | 
			
		||||
                </label>
 | 
			
		||||
                <div class="flex-container">
 | 
			
		||||
                    <input id="api_key_nomicai" name="api_key_nomicai" class="text_pole flex1 wide100p" maxlength="500" size="35" type="text" autocomplete="off">
 | 
			
		||||
@@ -117,48 +117,54 @@
 | 
			
		||||
            <div class="flex-container marginTopBot5">
 | 
			
		||||
                <div class="flex-container flex1 flexFlowColumn" title="How many last messages will be matched for relevance.">
 | 
			
		||||
                    <label for="vectors_query">
 | 
			
		||||
                        <span>Query messages</span>
 | 
			
		||||
                        <small data-i18n="Query messages">Query messages</small>
 | 
			
		||||
                    </label>
 | 
			
		||||
                    <input type="number" id="vectors_query" class="text_pole widthUnset" min="1" max="99" />
 | 
			
		||||
                    <input type="number" id="vectors_query" class="text_pole" min="1" max="99" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex-container flex1 flexFlowColumn" title="Cut-off score for relevance. Helps to filter out irrelevant data.">
 | 
			
		||||
                    <label for="vectors_query">
 | 
			
		||||
                        <span>Score threshold</span>
 | 
			
		||||
                        <small data-i18n="Score threshold">Score threshold</small>
 | 
			
		||||
                    </label>
 | 
			
		||||
                    <input type="number" id="vectors_score_threshold" class="text_pole widthUnset" min="0" max="1" step="0.05" />
 | 
			
		||||
                    <input type="number" id="vectors_score_threshold" class="text_pole" min="0" max="1" step="0.05" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex-container flex1 flexFlowColumn" title="Prioritize chunking on the preferred delimiter.">
 | 
			
		||||
                    <label for="vectors_force_chunk_delimiter">
 | 
			
		||||
                        <small data-i18n="Chunk boundary">Chunk boundary</small>
 | 
			
		||||
                    </label>
 | 
			
		||||
                    <textarea id="vectors_force_chunk_delimiter" class="text_pole" rows="1" placeholder="(None)"></textarea>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="flex-container">
 | 
			
		||||
                <label class="checkbox_label expander" for="vectors_include_wi" title="Query results can activate World Info entries.">
 | 
			
		||||
                    <input id="vectors_include_wi" type="checkbox" class="checkbox">
 | 
			
		||||
                    Include in World Info Scanning
 | 
			
		||||
                    <span data-i18n="Include in World Info Scanning">Include in World Info Scanning</span>
 | 
			
		||||
                </label>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <hr>
 | 
			
		||||
 | 
			
		||||
            <h4>
 | 
			
		||||
            <h4 data-i18n="World Info settings">
 | 
			
		||||
                World Info settings
 | 
			
		||||
            </h4>
 | 
			
		||||
 | 
			
		||||
            <label class="checkbox_label" for="vectors_enabled_world_info" title="Enable activation of World Info entries based on vector similarity.">
 | 
			
		||||
                <input id="vectors_enabled_world_info" type="checkbox" class="checkbox">
 | 
			
		||||
                Enabled for World Info
 | 
			
		||||
                <span data-i18n="Enable for World Info">Enable for World Info</span>
 | 
			
		||||
            </label>
 | 
			
		||||
 | 
			
		||||
            <div id="vectors_world_info_settings" class="marginTopBot5">
 | 
			
		||||
                <div class="flex-container">
 | 
			
		||||
                    <label for="vectors_enabled_for_all" class="checkbox_label">
 | 
			
		||||
                        <input id="vectors_enabled_for_all" type="checkbox" />
 | 
			
		||||
                        <span>Enabled for all entries</span>
 | 
			
		||||
                        <span data-i18n="Enabled for all entries">Enabled for all entries</span>
 | 
			
		||||
                    </label>
 | 
			
		||||
                    <ul class="margin0">
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <small>Checked: all entries except ❌ status can be activated.</small>
 | 
			
		||||
                            <small data-i18n="Checked: all entries except ❌ status can be activated.">Checked: all entries except ❌ status can be activated.</small>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <small>Unchecked: only entries with 🔗 status can be activated.</small>
 | 
			
		||||
                            <small data-i18n="Unchecked: only entries with ❌ status can be activated.">Unchecked: only entries with 🔗 status can be activated.</small>
 | 
			
		||||
                        </li>
 | 
			
		||||
                    </ul>
 | 
			
		||||
                </div>
 | 
			
		||||
@@ -168,7 +174,7 @@
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex1" title="Maximum number of entries to be activated">
 | 
			
		||||
                        <label for="vectors_max_entries" >
 | 
			
		||||
                            <small>Max Entries</small>
 | 
			
		||||
                            <small data-i18n="Max Entries">Max Entries</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_max_entries" type="number" class="text_pole widthUnset" min="1" max="9999" />
 | 
			
		||||
                    </div>
 | 
			
		||||
@@ -178,13 +184,13 @@
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <h4>
 | 
			
		||||
            <h4 data-i18n="File vectorization settings">
 | 
			
		||||
                File vectorization settings
 | 
			
		||||
            </h4>
 | 
			
		||||
 | 
			
		||||
            <label class="checkbox_label" for="vectors_enabled_files">
 | 
			
		||||
                <input id="vectors_enabled_files" type="checkbox" class="checkbox">
 | 
			
		||||
                Enabled for files
 | 
			
		||||
                <span data-i18n="Enable for files">Enable for files</span>
 | 
			
		||||
            </label>
 | 
			
		||||
 | 
			
		||||
            <div id="vectors_files_settings" class="marginTopBot5">
 | 
			
		||||
@@ -196,152 +202,153 @@
 | 
			
		||||
                    <i class="fa-solid fa-flask" title="Experimental feature"></i>
 | 
			
		||||
                </label>
 | 
			
		||||
                <div class="flex justifyCenter" title="These settings apply to files attached directly to messages.">
 | 
			
		||||
                    <span>Message attachments</span>
 | 
			
		||||
                    <span data-i18n="Message attachments">Message attachments</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex-container">
 | 
			
		||||
                    <div class="flex1" title="Only files past this size will be vectorized.">
 | 
			
		||||
                        <label for="vectors_size_threshold">
 | 
			
		||||
                            <small>Size threshold (KB)</small>
 | 
			
		||||
                            <small data-i18n="Size threshold (KB)">Size threshold (KB)</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_size_threshold" type="number" class="text_pole" min="1" max="99999" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex1" title="Chunk size for file splitting.">
 | 
			
		||||
                        <label for="vectors_chunk_size">
 | 
			
		||||
                            <small>Chunk size (chars)</small>
 | 
			
		||||
                            <small data-i18n="Chunk size (chars)">Chunk size (chars)</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_chunk_size" type="number" class="text_pole" min="1" max="99999" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex1" title="The overlap between adjacent chunks in % from chunk size. The overlap text is trimmed to sentence boundaries. 0 = disabled.">
 | 
			
		||||
                        <label for="vectors_overlap_percent">
 | 
			
		||||
                            <small>Chunk overlap (%)</small>
 | 
			
		||||
                            <small data-i18n="Chunk overlap (%)">Chunk overlap (%)</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_overlap_percent" type="number" class="text_pole" min="0" max="99" step="1" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex1" title="How many chunks to retrieve when querying.">
 | 
			
		||||
                        <label for="vectors_chunk_count">
 | 
			
		||||
                            <small>Retrieve chunks</small>
 | 
			
		||||
                            <small data-i18n="Retrieve chunks">Retrieve chunks</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_chunk_count" type="number" class="text_pole" min="1" max="99999" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex justifyCenter" title="These settings apply to files stored in the Data Bank.">
 | 
			
		||||
                    <span>Data Bank files</span>
 | 
			
		||||
                    <span data-i18n="Data Bank files">Data Bank files</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex-container">
 | 
			
		||||
                    <div class="flex1" title="Only files past this size will be vectorized.">
 | 
			
		||||
                        <label for="vectors_size_threshold_db">
 | 
			
		||||
                            <small>Size threshold (KB)</small>
 | 
			
		||||
                            <small data-i18n="Size threshold (KB)">Size threshold (KB)</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_size_threshold_db" type="number" class="text_pole" min="1" max="99999" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex1" title="Chunk size for file splitting.">
 | 
			
		||||
                        <label for="vectors_chunk_size_db">
 | 
			
		||||
                            <small>Chunk size (chars)</small>
 | 
			
		||||
                            <small data-i18n="Chunk size (chars)">Chunk size (chars)</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_chunk_size_db" type="number" class="text_pole" min="1" max="99999" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex1" title="The overlap between adjacent chunks in % from chunk size. The overlap text is trimmed to sentence boundaries. 0 = disabled.">
 | 
			
		||||
                        <label for="vectors_overlap_percent_db">
 | 
			
		||||
                            <small>Chunk overlap (%)</small>
 | 
			
		||||
                            <small data-i18n="Chunk overlap (%)">Chunk overlap (%)</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_overlap_percent_db" type="number" class="text_pole" min="0" max="99" step="1" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex1" title="How many chunks to retrieve when querying.">
 | 
			
		||||
                        <label for="vectors_chunk_count_db">
 | 
			
		||||
                            <small>Retrieve chunks</small>
 | 
			
		||||
                            <small data-i18n="Retrieve chunks">Retrieve chunks</small>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input id="vectors_chunk_count_db" type="number" class="text_pole" min="1" max="99999" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex-container flexFlowColumn">
 | 
			
		||||
                    <label for="vectors_file_template_db">
 | 
			
		||||
                        <span>Injection Template</span>
 | 
			
		||||
                        <span data-i18n="Injection Template">Injection Template</span>
 | 
			
		||||
                    </label>
 | 
			
		||||
                    <textarea id="vectors_file_template_db" class="margin0 text_pole textarea_compact" rows="3" placeholder="Use {{text}} macro to specify the position of retrieved text."></textarea>
 | 
			
		||||
                    <label for="vectors_file_position_db">Injection Position</label>
 | 
			
		||||
                    <label for="vectors_file_position_db" data-i18n="Injection Position">Injection Position</label>
 | 
			
		||||
                    <div class="radio_group">
 | 
			
		||||
                        <label>
 | 
			
		||||
                            <input type="radio" name="vectors_file_position_db" value="2" />
 | 
			
		||||
                            <span>Before Main Prompt / Story String</span>
 | 
			
		||||
                            <span data-i18n="Before Main Prompt / Story String">Before Main Prompt / Story String</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <!--Keep these as 0 and 1 to interface with the setExtensionPrompt function-->
 | 
			
		||||
                        <label>
 | 
			
		||||
                            <input type="radio" name="vectors_file_position_db" value="0" />
 | 
			
		||||
                            <span>After Main Prompt / Story String</span>
 | 
			
		||||
                            <span data-i18n="After Main Prompt / Story String">After Main Prompt / Story String</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <label for="vectors_file_depth_db" title="How many messages before the current end of the chat." data-i18n="[title]How many messages before the current end of the chat.">
 | 
			
		||||
                            <input type="radio" name="vectors_file_position_db" value="1" />
 | 
			
		||||
                            <span>In-chat @ Depth</span>
 | 
			
		||||
                            <span data-i18n="In-chat @ Depth">In-chat @ Depth</span>
 | 
			
		||||
                            <input id="vectors_file_depth_db" class="text_pole widthUnset" type="number" min="0" max="999" />
 | 
			
		||||
                            <span>as</span>
 | 
			
		||||
                            <select id="vectors_file_depth_role_db"  class="text_pole widthNatural">
 | 
			
		||||
                                <option value="0">System</option>
 | 
			
		||||
                                <option value="1">User</option>
 | 
			
		||||
                                <option value="2">Assistant</option>
 | 
			
		||||
                                <option value="0" data-i18n="System">System</option>
 | 
			
		||||
                                <option value="1" data-i18n="User">User</option>
 | 
			
		||||
                                <option value="2" data-i18n="Assistant">Assistant</option>
 | 
			
		||||
                            </select>
 | 
			
		||||
                        </label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex-container">
 | 
			
		||||
                    <div id="vectors_files_vectorize_all" class="menu_button menu_button_icon" title="Vectorize all files in the Data Bank and current chat.">
 | 
			
		||||
                        Vectorize All
 | 
			
		||||
                        <span data-i18n="Vectorize All">Vectorize All</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div id="vectors_files_purge" class="menu_button menu_button_icon" title="Purge all file vectors in the Data Bank and current chat.">
 | 
			
		||||
                        Purge Vectors
 | 
			
		||||
                        <span data-i18n="Purge Vectors">Purge Vectors</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <hr>
 | 
			
		||||
 | 
			
		||||
            <h4>
 | 
			
		||||
            <h4 data-i18n="Chat vectorization settings">
 | 
			
		||||
                Chat vectorization settings
 | 
			
		||||
            </h4>
 | 
			
		||||
            <label class="checkbox_label" for="vectors_enabled_chats">
 | 
			
		||||
                <input id="vectors_enabled_chats" type="checkbox" class="checkbox">
 | 
			
		||||
                Enabled for chat messages
 | 
			
		||||
                <span data-i18n="Enabled for chat messages">Enabled for chat messages</span>
 | 
			
		||||
            </label>
 | 
			
		||||
 | 
			
		||||
            <hr>
 | 
			
		||||
 | 
			
		||||
            <div id="vectors_chats_settings">
 | 
			
		||||
                <div id="vectors_advanced_settings">
 | 
			
		||||
                    <label for="vectors_template">
 | 
			
		||||
                    <label for="vectors_template" data-i18n="Injection Template">
 | 
			
		||||
                        Injection Template
 | 
			
		||||
                    </label>
 | 
			
		||||
                    <textarea id="vectors_template" class="text_pole textarea_compact" rows="3" placeholder="Use {{text}} macro to specify the position of retrieved text."></textarea>
 | 
			
		||||
                    <label for="vectors_position">Injection Position</label>
 | 
			
		||||
                    <label for="vectors_position" data-i18n="Injection Position">Injection Position</label>
 | 
			
		||||
                    <div class="radio_group">
 | 
			
		||||
                        <label>
 | 
			
		||||
                            <input type="radio" name="vectors_position" value="2" />
 | 
			
		||||
                            Before Main Prompt / Story String
 | 
			
		||||
                            <span data-i18n="Before Main Prompt / Story String">Before Main Prompt / Story String</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <!--Keep these as 0 and 1 to interface with the setExtensionPrompt function-->
 | 
			
		||||
                        <label>
 | 
			
		||||
                            <input type="radio" name="vectors_position" value="0" />
 | 
			
		||||
                            After Main Prompt / Story String
 | 
			
		||||
                            <span data-i18n="After Main Prompt / Story String">After Main Prompt / Story String</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <label for="vectors_depth" title="How many messages before the current end of the chat." data-i18n="[title]How many messages before the current end of the chat.">
 | 
			
		||||
                            <input type="radio" name="vectors_position" value="1" />
 | 
			
		||||
                            In-chat @ Depth <input id="vectors_depth" class="text_pole widthUnset" type="number" min="0" max="999" />
 | 
			
		||||
                            <span data-i18n="In-chat @ Depth">In-chat @ Depth </span>
 | 
			
		||||
                            <input id="vectors_depth" class="text_pole widthUnset" type="number" min="0" max="999" />
 | 
			
		||||
                        </label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="flex-container">
 | 
			
		||||
                        <div class="flex1" title="Can increase the retrieval quality for the cost of processing. 0 = disabled.">
 | 
			
		||||
                            <label for="vectors_message_chunk_size">
 | 
			
		||||
                                <small>Chunk size (chars)</small>
 | 
			
		||||
                                <small data-i18n="Chunk size (chars)">Chunk size (chars)</small>
 | 
			
		||||
                            </label>
 | 
			
		||||
                            <input id="vectors_message_chunk_size" type="number" class="text_pole widthUnset" min="0" max="9999" />
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="flex1" title="Prevents last N messages from being placed out of order.">
 | 
			
		||||
                            <label for="vectors_protect">
 | 
			
		||||
                                <small>Retain#</small>
 | 
			
		||||
                                <small data-i18n="Retain#">Retain#</small>
 | 
			
		||||
                            </label>
 | 
			
		||||
                            <input type="number" id="vectors_protect" class="text_pole widthUnset" min="1" max="9999" />
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="flex1" title="How many past messages to insert as memories.">
 | 
			
		||||
                            <label for="vectors_insert">
 | 
			
		||||
                                <small>Insert#</small>
 | 
			
		||||
                                <small data-i18n="Insert#">Insert#</small>
 | 
			
		||||
                            </label>
 | 
			
		||||
                            <input type="number" id="vectors_insert" class="text_pole widthUnset" min="1" max="9999" />
 | 
			
		||||
                        </div>
 | 
			
		||||
@@ -350,43 +357,43 @@
 | 
			
		||||
                    <div class="flex-container flexFlowColumn">
 | 
			
		||||
                        <div class="flex-container alignitemscenter justifyCenter">
 | 
			
		||||
                            <i class="fa-solid fa-flask" title="Summarization for vectors is an experimental feature that may improve vectors or may worsen them. Use at your own discretion."></i>
 | 
			
		||||
                            <span>Vector Summarization</span>
 | 
			
		||||
                            <span data-i18n="Vector Summarization">Vector Summarization</span>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <label class="checkbox_label expander" for="vectors_summarize" title="Summarize chat messages before generating embeddings.">
 | 
			
		||||
                            <input id="vectors_summarize" type="checkbox" class="checkbox">
 | 
			
		||||
                            Summarize chat messages for vector generation
 | 
			
		||||
                            <span data-i18n="Summarize chat messages for vector generation">Summarize chat messages for vector generation</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <i class="failure">Warning: This will slow down vector generation drastically, as all messages have to be summarized first.</i>
 | 
			
		||||
                        <i class="failure" data-i18n="Warning: This will slow down vector generation drastically, as all messages have to be summarized first.">Warning: This will slow down vector generation drastically, as all messages have to be summarized first.</i>
 | 
			
		||||
 | 
			
		||||
                        <label class="checkbox_label expander" for="vectors_summarize_user" title="Summarize sent chat messages before generating embeddings.">
 | 
			
		||||
                            <input id="vectors_summarize_user" type="checkbox" class="checkbox">
 | 
			
		||||
                            Summarize chat messages when sending
 | 
			
		||||
                            <span data-i18n="Summarize chat messages when sending">Summarize chat messages when sending</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <i class="failure">Warning: This might cause your sent messages to take a bit to process and slow down response time.</i>
 | 
			
		||||
                        <i class="failure" data-i18n="Warning: This might cause your sent messages to take a bit to process and slow down response time.">Warning: This might cause your sent messages to take a bit to process and slow down response time.</i>
 | 
			
		||||
 | 
			
		||||
                        <label for="vectors_summary_source">Summarize with:</label>
 | 
			
		||||
                        <label for="vectors_summary_source" title="Summarize with:">Summarize with:</label>
 | 
			
		||||
                        <select id="vectors_summary_source" class="text_pole">
 | 
			
		||||
                            <option value="main">Main API</option>
 | 
			
		||||
                            <option value="extras">Extras API</option>
 | 
			
		||||
                            <option value="main" data-i18n="Main API">Main API</option>
 | 
			
		||||
                            <option value="extras" data-i18n="Extras API">Extras API</option>
 | 
			
		||||
                        </select>
 | 
			
		||||
 | 
			
		||||
                        <label for="vectors_summary_prompt">Summary Prompt:</label>
 | 
			
		||||
                        <small>Only used when Main API is selected.</small>
 | 
			
		||||
                        <label for="vectors_summary_prompt" title="Summary Prompt:">Summary Prompt:</label>
 | 
			
		||||
                        <small data-i18n="Only used when Main API is selected.">Only used when Main API is selected.</small>
 | 
			
		||||
                        <textarea id="vectors_summary_prompt" class="text_pole textarea_compact" rows="6" placeholder="This prompt will be sent to AI to request the summary generation."></textarea>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <small>
 | 
			
		||||
                <small data-i18n="Old messages are vectorized gradually as you chat. To process all previous messages, click the button below.">
 | 
			
		||||
                    Old messages are vectorized gradually as you chat.
 | 
			
		||||
                    To process all previous messages, click the button below.
 | 
			
		||||
                </small>
 | 
			
		||||
                <div class="flex-container">
 | 
			
		||||
                    <div id="vectors_vectorize_all" class="menu_button menu_button_icon">
 | 
			
		||||
                    <div id="vectors_vectorize_all" class="menu_button menu_button_icon" data-i18n="Vectorize All">
 | 
			
		||||
                        Vectorize All
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div id="vectors_purge" class="menu_button menu_button_icon">
 | 
			
		||||
                    <div id="vectors_purge" class="menu_button menu_button_icon" data-i18n="Purge Vectors">
 | 
			
		||||
                        Purge Vectors
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div id="vectors_view_stats" class="menu_button menu_button_icon">
 | 
			
		||||
                    <div id="vectors_view_stats" class="menu_button menu_button_icon" data-i18n="View Stats">
 | 
			
		||||
                        View Stats
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user