Merge branch 'staging' into smol-tag-improvements

This commit is contained in:
Cohee
2024-06-16 16:04:37 +03:00
65 changed files with 2907 additions and 1969 deletions

View File

@@ -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">

View File

@@ -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(

View File

@@ -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="&lt; Use default &gt;">${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>&lcub;&lcub;caption&rcub;&rcub;</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>&lcub;&lcub;caption&rcub;&rcub;</code> <span data-i18n="macro)">macro)</span></small></label>
<textarea id="caption_template" class="text_pole" rows="2" placeholder="&lt; Use default &gt;">${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>

View File

@@ -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(

View File

@@ -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 &lcub;&lcub;labels&rcub;&rcub; 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>&nbsp;<span id="image_list_header_name"></span>
<strong data-i18n="Sprite set:">Sprite set:</strong>&nbsp;<span id="image_list_header_name"></span>
</h3>
<div id="image_list"></div>

View File

@@ -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.');

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -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'));

View File

@@ -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.&#10;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.&#10;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.&#10;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.&#10;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">

View 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>

View File

@@ -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>

View 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>

View File

@@ -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,
}));
});

View File

@@ -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);

View File

@@ -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 &lcub;&lcub;text&rcub;&rcub; 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 &lcub;&lcub;text&rcub;&rcub; 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>