SillyTavern/public/scripts/cfg-scale.js

493 lines
16 KiB
JavaScript

import {
chat_metadata,
substituteParams,
this_chid,
eventSource,
event_types,
saveSettingsDebounced,
} from '../script.js';
import { extension_settings, saveMetadataDebounced } from './extensions.js';
import { selected_group } from './group-chats.js';
import { getCharaFilename, delay } from './utils.js';
import { power_user } from './power-user.js';
const extensionName = 'cfg';
const defaultSettings = {
global: {
'guidance_scale': 1,
'negative_prompt': ''
},
chara: []
};
const settingType = {
guidance_scale: 0,
negative_prompt: 1,
positive_prompt: 2
};
// Used for character and chat CFG values
function updateSettings() {
saveSettingsDebounced();
loadSettings();
}
function setCharCfg(tempValue, setting) {
const avatarName = getCharaFilename();
// Assign temp object
let tempCharaCfg = {
name: avatarName
};
switch(setting) {
case settingType.guidance_scale:
tempCharaCfg['guidance_scale'] = Number(tempValue);
break;
case settingType.negative_prompt:
tempCharaCfg['negative_prompt'] = tempValue;
break;
case settingType.positive_prompt:
tempCharaCfg['positive_prompt'] = tempValue;
break;
default:
return false;
}
let existingCharaCfgIndex;
let existingCharaCfg;
if (extension_settings.cfg.chara) {
existingCharaCfgIndex = extension_settings.cfg.chara.findIndex((e) => e.name === avatarName);
existingCharaCfg = extension_settings.cfg.chara[existingCharaCfgIndex];
}
if (extension_settings.cfg.chara && existingCharaCfg) {
const tempAssign = Object.assign(existingCharaCfg, tempCharaCfg);
// If both values are default, remove the entry
if (!existingCharaCfg.useChara &&
(tempAssign.guidance_scale ?? 1.00) === 1.00 &&
(tempAssign.negative_prompt?.length ?? 0) === 0 &&
(tempAssign.positive_prompt?.length ?? 0) === 0)
{
extension_settings.cfg.chara.splice(existingCharaCfgIndex, 1);
}
} else if (avatarName && tempValue.length > 0) {
if (!extension_settings.cfg.chara) {
extension_settings.cfg.chara = [];
}
extension_settings.cfg.chara.push(tempCharaCfg);
} else {
console.debug('Character CFG error: No avatar name key could be found.');
// Don't save settings if something went wrong
return false;
}
updateSettings();
return true;
}
function setChatCfg(tempValue, setting) {
switch(setting) {
case settingType.guidance_scale:
chat_metadata[metadataKeys.guidance_scale] = tempValue;
break;
case settingType.negative_prompt:
chat_metadata[metadataKeys.negative_prompt] = tempValue;
break;
case settingType.positive_prompt:
chat_metadata[metadataKeys.positive_prompt] = tempValue;
break;
default:
return false;
}
saveMetadataDebounced();
return true;
}
// TODO: Only change CFG when character is selected
function onCfgMenuItemClick() {
if (selected_group || this_chid) {
//show CFG config if it's hidden
if ($('#cfgConfig').css('display') !== 'flex') {
$('#cfgConfig').addClass('resizing');
$('#cfgConfig').css('display', 'flex');
$('#cfgConfig').css('opacity', 0.0);
$('#cfgConfig').transition({
opacity: 1.0,
duration: 250,
}, async function () {
await delay(50);
$('#cfgConfig').removeClass('resizing');
});
//auto-open the main AN inline drawer
if ($('#CFGBlockToggle')
.siblings('.inline-drawer-content')
.css('display') !== 'block') {
$('#floatingPrompt').addClass('resizing');
$('#CFGBlockToggle').click();
}
} else {
//hide AN if it's already displayed
$('#cfgConfig').addClass('resizing');
$('#cfgConfig').transition({
opacity: 0.0,
duration: 250,
},
async function () {
await delay(50);
$('#cfgConfig').removeClass('resizing');
});
setTimeout(function () {
$('#cfgConfig').hide();
}, 250);
}
//duplicate options menu close handler from script.js
//because this listener takes priority
$('#options').stop().fadeOut(250);
} else {
toastr.warning('Select a character before trying to configure CFG', '', { timeOut: 2000 });
}
}
async function onChatChanged() {
loadSettings();
await modifyCharaHtml();
}
// Rearrange the panel if a group chat is present
async function modifyCharaHtml() {
if (selected_group) {
$('#chara_cfg_container').hide();
$('#groupchat_cfg_use_chara_container').show();
} else {
$('#chara_cfg_container').show();
$('#groupchat_cfg_use_chara_container').hide();
// TODO: Remove chat checkbox here
}
}
// Reloads chat-specific settings
function loadSettings() {
// Set chat CFG if it exists
$('#chat_cfg_guidance_scale').val(chat_metadata[metadataKeys.guidance_scale] ?? 1.0.toFixed(2));
$('#chat_cfg_guidance_scale_counter').val(chat_metadata[metadataKeys.guidance_scale]?.toFixed(2) ?? 1.0.toFixed(2));
$('#chat_cfg_negative_prompt').val(chat_metadata[metadataKeys.negative_prompt] ?? '');
$('#chat_cfg_positive_prompt').val(chat_metadata[metadataKeys.positive_prompt] ?? '');
$('#groupchat_cfg_use_chara').prop('checked', chat_metadata[metadataKeys.groupchat_individual_chars] ?? false);
if (chat_metadata[metadataKeys.prompt_combine]?.length > 0) {
chat_metadata[metadataKeys.prompt_combine].forEach((element) => {
$(`input[name="cfg_prompt_combine"][value="${element}"]`)
.prop('checked', true);
});
}
// Display the negative separator in quotes if not quoted already
let promptSeparatorDisplay = [];
const promptSeparator = chat_metadata[metadataKeys.prompt_separator];
if (promptSeparator) {
promptSeparatorDisplay.push(promptSeparator);
if (!promptSeparator.startsWith('"')) {
promptSeparatorDisplay.unshift('"');
}
if (!promptSeparator.endsWith('"')) {
promptSeparatorDisplay.push('"');
}
}
$('#cfg_prompt_separator').val(promptSeparatorDisplay.length === 0 ? '' : promptSeparatorDisplay.join(''));
$('#cfg_prompt_insertion_depth').val(chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1);
// Set character CFG if it exists
if (!selected_group) {
const charaCfg = extension_settings.cfg.chara.find((e) => e.name === getCharaFilename());
$('#chara_cfg_guidance_scale').val(charaCfg?.guidance_scale ?? 1.00);
$('#chara_cfg_guidance_scale_counter').val(charaCfg?.guidance_scale?.toFixed(2) ?? 1.0.toFixed(2));
$('#chara_cfg_negative_prompt').val(charaCfg?.negative_prompt ?? '');
$('#chara_cfg_positive_prompt').val(charaCfg?.positive_prompt ?? '');
}
}
// Load initial extension settings
async function initialLoadSettings() {
// Create the settings if they don't exist
extension_settings[extensionName] = extension_settings[extensionName] || {};
if (Object.keys(extension_settings[extensionName]).length === 0) {
Object.assign(extension_settings[extensionName], defaultSettings);
saveSettingsDebounced();
}
// Set global CFG values on load
$('#global_cfg_guidance_scale').val(extension_settings.cfg.global.guidance_scale);
$('#global_cfg_guidance_scale_counter').val(extension_settings.cfg.global.guidance_scale.toFixed(2));
$('#global_cfg_negative_prompt').val(extension_settings.cfg.global.negative_prompt);
$('#global_cfg_positive_prompt').val(extension_settings.cfg.global.positive_prompt);
}
function migrateSettings() {
let performSettingsSave = false;
let performMetaSave = false;
if (power_user.guidance_scale) {
extension_settings.cfg.global.guidance_scale = power_user.guidance_scale;
delete power_user['guidance_scale'];
performSettingsSave = true;
}
if (power_user.negative_prompt) {
extension_settings.cfg.global.negative_prompt = power_user.negative_prompt;
delete power_user['negative_prompt'];
performSettingsSave = true;
}
if (chat_metadata['cfg_negative_combine']) {
chat_metadata[metadataKeys.prompt_combine] = chat_metadata['cfg_negative_combine'];
chat_metadata['cfg_negative_combine'] = undefined;
performMetaSave = true;
}
if (chat_metadata['cfg_negative_insertion_depth']) {
chat_metadata[metadataKeys.prompt_insertion_depth] = chat_metadata['cfg_negative_insertion_depth'];
chat_metadata['cfg_negative_insertion_depth'] = undefined;
performMetaSave = true;
}
if (chat_metadata['cfg_negative_separator']) {
chat_metadata[metadataKeys.prompt_separator] = chat_metadata['cfg_negative_separator'];
chat_metadata['cfg_negative_separator'] = undefined;
performMetaSave = true;
}
if (performSettingsSave) {
saveSettingsDebounced();
}
if (performMetaSave) {
saveMetadataDebounced();
}
}
// This function is called when the extension is loaded
export function initCfg() {
$('#CFGClose').on('click', function () {
$('#cfgConfig').transition({
opacity: 0,
duration: 200,
easing: 'ease-in-out',
});
setTimeout(function () { $('#cfgConfig').hide(); }, 200);
});
$('#chat_cfg_guidance_scale').on('input', function() {
const numberValue = Number($(this).val());
const success = setChatCfg(numberValue, settingType.guidance_scale);
if (success) {
$('#chat_cfg_guidance_scale_counter').val(numberValue.toFixed(2));
}
});
$('#chat_cfg_negative_prompt').on('input', function() {
setChatCfg($(this).val(), settingType.negative_prompt);
});
$('#chat_cfg_positive_prompt').on('input', function() {
setChatCfg($(this).val(), settingType.positive_prompt);
});
$('#chara_cfg_guidance_scale').on('input', function() {
const value = $(this).val();
const success = setCharCfg(value, settingType.guidance_scale);
if (success) {
$('#chara_cfg_guidance_scale_counter').val(Number(value).toFixed(2));
}
});
$('#chara_cfg_negative_prompt').on('input', function() {
setCharCfg($(this).val(), settingType.negative_prompt);
});
$('#chara_cfg_positive_prompt').on('input', function() {
setCharCfg($(this).val(), settingType.positive_prompt);
});
$('#global_cfg_guidance_scale').on('input', function() {
extension_settings.cfg.global.guidance_scale = Number($(this).val());
$('#global_cfg_guidance_scale_counter').val(extension_settings.cfg.global.guidance_scale.toFixed(2));
saveSettingsDebounced();
});
$('#global_cfg_negative_prompt').on('input', function() {
extension_settings.cfg.global.negative_prompt = $(this).val();
saveSettingsDebounced();
});
$('#global_cfg_positive_prompt').on('input', function() {
extension_settings.cfg.global.positive_prompt = $(this).val();
saveSettingsDebounced();
});
$('input[name="cfg_prompt_combine"]').on('input', function() {
const values = $('#cfgConfig').find('input[name="cfg_prompt_combine"]')
.filter(':checked')
.map(function() { return Number($(this).val()); })
.get()
.filter((e) => !Number.isNaN(e)) || [];
chat_metadata[metadataKeys.prompt_combine] = values;
saveMetadataDebounced();
});
$('#cfg_prompt_insertion_depth').on('input', function() {
chat_metadata[metadataKeys.prompt_insertion_depth] = Number($(this).val());
saveMetadataDebounced();
});
$('#cfg_prompt_separator').on('input', function() {
chat_metadata[metadataKeys.prompt_separator] = $(this).val();
saveMetadataDebounced();
});
$('#groupchat_cfg_use_chara').on('input', function() {
const checked = !!$(this).prop('checked');
chat_metadata[metadataKeys.groupchat_individual_chars] = checked;
if (checked) {
toastr.info('You can edit character CFG values in their respective character chats.');
}
saveMetadataDebounced();
});
initialLoadSettings();
if (extension_settings.cfg) {
migrateSettings();
}
$('#option_toggle_CFG').on('click', onCfgMenuItemClick);
// Hook events
eventSource.on(event_types.CHAT_CHANGED, async () => {
await onChatChanged();
});
}
export const cfgType = {
chat: 0,
chara: 1,
global: 2
};
export const metadataKeys = {
guidance_scale: 'cfg_guidance_scale',
negative_prompt: 'cfg_negative_prompt',
positive_prompt: 'cfg_positive_prompt',
prompt_combine: 'cfg_prompt_combine',
groupchat_individual_chars: 'cfg_groupchat_individual_chars',
prompt_insertion_depth: 'cfg_prompt_insertion_depth',
prompt_separator: 'cfg_prompt_separator'
};
// Gets the CFG guidance scale
// If the guidance scale is 1, ignore the CFG prompt(s) since it won't be used anyways
export function getGuidanceScale() {
if (!extension_settings.cfg) {
console.warn('CFG extension is not enabled. Skipping CFG guidance.');
return;
}
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
const chatGuidanceScale = chat_metadata[metadataKeys.guidance_scale];
const groupchatCharOverride = chat_metadata[metadataKeys.groupchat_individual_chars] ?? false;
if (chatGuidanceScale && chatGuidanceScale !== 1 && !groupchatCharOverride) {
return {
type: cfgType.chat,
value: chatGuidanceScale
};
}
if ((!selected_group && charaCfg || groupchatCharOverride) && charaCfg?.guidance_scale !== 1) {
return {
type: cfgType.chara,
value: charaCfg.guidance_scale
};
}
if (extension_settings.cfg.global && extension_settings.cfg.global?.guidance_scale !== 1) {
return {
type: cfgType.global,
value: extension_settings.cfg.global.guidance_scale
};
}
}
/**
* Gets the CFG prompt separator.
* @returns {string} The CFG prompt separator
*/
function getCustomSeparator() {
const defaultSeparator = '\n';
try {
if (chat_metadata[metadataKeys.prompt_separator]) {
return JSON.parse(chat_metadata[metadataKeys.prompt_separator]);
}
return defaultSeparator;
} catch {
console.warn('Invalid JSON detected for prompt separator. Using default separator.');
return defaultSeparator;
}
}
// Gets the CFG prompt
export function getCfgPrompt(guidanceScale, isNegative) {
let splitCfgPrompt = [];
const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
if (guidanceScale.type === cfgType.chat || cfgPromptCombine.includes(cfgType.chat)) {
splitCfgPrompt.unshift(
substituteParams(
chat_metadata[isNegative ? metadataKeys.negative_prompt : metadataKeys.positive_prompt]
)
);
}
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
if (guidanceScale.type === cfgType.chara || cfgPromptCombine.includes(cfgType.chara)) {
splitCfgPrompt.unshift(
substituteParams(
isNegative ? charaCfg.negative_prompt : charaCfg.positive_prompt
)
);
}
if (guidanceScale.type === cfgType.global || cfgPromptCombine.includes(cfgType.global)) {
splitCfgPrompt.unshift(
substituteParams(
isNegative ? extension_settings.cfg.global.negative_prompt : extension_settings.cfg.global.positive_prompt
)
);
}
const customSeparator = getCustomSeparator();
const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
return {
value: combinedCfgPrompt,
depth: insertionDepth
};
}