CFG: Add ability to combine negative prompts
This allows for flexibility with global, character, and chat negative prompts. Combining prompts is very useful for users who want to maintain a set of global negatives and then add extra layers on top of that. The ordering is chat -> character -> global tags due to the specificity of each. The guidance scale follows the cascade of chat -> character -> global due to being one number that is set when CFG is fired. If the guidance scale is 1, nothing happens. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
5bb6c89868
commit
4a21ee0337
|
@ -7,22 +7,23 @@ import {
|
||||||
} from "../../../script.js";
|
} from "../../../script.js";
|
||||||
import { selected_group } from "../../group-chats.js";
|
import { selected_group } from "../../group-chats.js";
|
||||||
import { extension_settings, saveMetadataDebounced } from "../../extensions.js";
|
import { extension_settings, saveMetadataDebounced } from "../../extensions.js";
|
||||||
import { getCharaFilename, delay } from "../../utils.js";
|
import { getCharaFilename, delay, debounce } from "../../utils.js";
|
||||||
import { power_user } from "../../power-user.js";
|
import { power_user } from "../../power-user.js";
|
||||||
|
import { metadataKeys } from "./util.js";
|
||||||
|
|
||||||
// Keep track of where your extension is located, name should match repo name
|
// Keep track of where your extension is located, name should match repo name
|
||||||
const extensionName = "cfg";
|
const extensionName = "cfg";
|
||||||
const extensionFolderPath = `scripts/extensions/${extensionName}`;
|
const extensionFolderPath = `scripts/extensions/${extensionName}`;
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
"global": {
|
global: {
|
||||||
"guidance_scale": 1,
|
"guidance_scale": 1,
|
||||||
"negative_prompt": ''
|
"negative_prompt": ''
|
||||||
},
|
},
|
||||||
"chara": []
|
chara: []
|
||||||
};
|
};
|
||||||
const settingType = {
|
const settingType = {
|
||||||
"guidance_scale": 0,
|
guidance_scale: 0,
|
||||||
"negative_prompt": 1
|
negative_prompt: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for character and chat CFG values
|
// Used for character and chat CFG values
|
||||||
|
@ -64,7 +65,7 @@ function setCharCfg(tempValue, setting) {
|
||||||
if (extension_settings.cfg.chara && existingCharaCfg) {
|
if (extension_settings.cfg.chara && existingCharaCfg) {
|
||||||
const tempAssign = Object.assign(existingCharaCfg, tempCharaCfg);
|
const tempAssign = Object.assign(existingCharaCfg, tempCharaCfg);
|
||||||
|
|
||||||
// if both values are default, remove the entry
|
// If both values are default, remove the entry
|
||||||
if (!existingCharaCfg.useChara && (tempAssign.guidance_scale ?? 1.00) === 1.00 && (tempAssign.negative_prompt?.length ?? 0) === 0) {
|
if (!existingCharaCfg.useChara && (tempAssign.guidance_scale ?? 1.00) === 1.00 && (tempAssign.negative_prompt?.length ?? 0) === 0) {
|
||||||
extension_settings.cfg.chara.splice(existingCharaCfgIndex, 1);
|
extension_settings.cfg.chara.splice(existingCharaCfgIndex, 1);
|
||||||
}
|
}
|
||||||
|
@ -73,8 +74,6 @@ function setCharCfg(tempValue, setting) {
|
||||||
extension_settings.cfg.chara = []
|
extension_settings.cfg.chara = []
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(tempCharaCfg, { useChara: false })
|
|
||||||
|
|
||||||
extension_settings.cfg.chara.push(tempCharaCfg);
|
extension_settings.cfg.chara.push(tempCharaCfg);
|
||||||
} else {
|
} else {
|
||||||
console.log("Character CFG error: No avatar name key could be found.");
|
console.log("Character CFG error: No avatar name key could be found.");
|
||||||
|
@ -88,28 +87,13 @@ function setCharCfg(tempValue, setting) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCharCfgCheckbox() {
|
|
||||||
const value = !!$(this).prop('checked');
|
|
||||||
const charaCfgIndex = extension_settings.cfg.chara.findIndex((e) => e.name === getCharaFilename());
|
|
||||||
const charaCfg = extension_settings.cfg.chara[charaCfgIndex];
|
|
||||||
if (charaCfg) {
|
|
||||||
if (!value && (charaCfg.guidance_scale ?? 1.00) === 1.00 && (charaCfg.negative_prompt?.length ?? 0) === 0) {
|
|
||||||
extension_settings.cfg.chara.splice(charaCfgIndex, 1);
|
|
||||||
} else {
|
|
||||||
charaCfg.useChara = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSettings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setChatCfg(tempValue, setting) {
|
function setChatCfg(tempValue, setting) {
|
||||||
switch(setting) {
|
switch(setting) {
|
||||||
case settingType.guidance_scale:
|
case settingType.guidance_scale:
|
||||||
chat_metadata['guidance_scale'] = tempValue;
|
chat_metadata[metadataKeys.guidance_scale] = tempValue;
|
||||||
break;
|
break;
|
||||||
case settingType.negative_prompt:
|
case settingType.negative_prompt:
|
||||||
chat_metadata['negative_prompt'] = tempValue;
|
chat_metadata[metadataKeys.negative_prompt] = tempValue;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -173,18 +157,24 @@ function onChatChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reloads chat-specific settings
|
// Reloads chat-specific settings
|
||||||
|
// TODO: Fix race condition bug where deleted chara CFG still loads previous prompts
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
// Set chat CFG if it exists
|
// Set chat CFG if it exists
|
||||||
$('#chat_cfg_guidance_scale').val(chat_metadata['guidance_scale'] ?? 1.00);
|
$('#chat_cfg_guidance_scale').val(chat_metadata[metadataKeys.guidance_scale] ?? 1.0.toFixed(2));
|
||||||
$('#chat_cfg_guidance_scale_counter').text(chat_metadata['guidance_scale']?.toFixed(2) ?? 1.00);
|
$('#chat_cfg_guidance_scale_counter').text(chat_metadata[metadataKeys.guidance_scale]?.toFixed(2) ?? 1.0.toFixed(2));
|
||||||
$('#chat_cfg_negative_prompt').val(chat_metadata['negative_prompt'] ?? '');
|
$('#chat_cfg_negative_prompt').val(chat_metadata[metadataKeys.negative_prompt] ?? '');
|
||||||
|
if (chat_metadata[metadataKeys.negative_combine]?.length > 0) {
|
||||||
|
chat_metadata[metadataKeys.negative_combine].forEach((element) => {
|
||||||
|
$(`input[name="cfg_negative_combine"][value="${element}"]`)
|
||||||
|
.prop("checked", true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Set character CFG if it exists
|
// Set character CFG if it exists
|
||||||
const charaCfg = extension_settings.cfg.chara.find((e) => e.name === getCharaFilename());
|
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').val(charaCfg?.guidance_scale ?? 1.00);
|
||||||
$('#chara_cfg_guidance_scale_counter').text(charaCfg?.guidance_scale?.toFixed(2) ?? 1.00);
|
$('#chara_cfg_guidance_scale_counter').text(charaCfg?.guidance_scale?.toFixed(2) ?? 1.0.toFixed(2));
|
||||||
$('#chara_cfg_negative_prompt').val(charaCfg?.negative_prompt ?? '');
|
$('#chara_cfg_negative_prompt').val(charaCfg?.negative_prompt ?? '');
|
||||||
$('#use_chara_cfg').prop('checked', charaCfg?.useChara ?? false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load initial extension settings
|
// Load initial extension settings
|
||||||
|
@ -251,6 +241,17 @@ jQuery(async () => {
|
||||||
setChatCfg($(this).val(), settingType.negative_prompt);
|
setChatCfg($(this).val(), settingType.negative_prompt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
windowHtml.find(`input[name="cfg_negative_combine"]`).on('input', function() {
|
||||||
|
const values = windowHtml.find(`input[name="cfg_negative_combine"]`)
|
||||||
|
.filter(":checked")
|
||||||
|
.map(function() { return parseInt($(this).val()) })
|
||||||
|
.get()
|
||||||
|
.filter((e) => e !== NaN) || [];
|
||||||
|
|
||||||
|
chat_metadata[metadataKeys.negative_combine] = values;
|
||||||
|
saveMetadataDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
windowHtml.find('#chara_cfg_guidance_scale').on('input', function() {
|
windowHtml.find('#chara_cfg_guidance_scale').on('input', function() {
|
||||||
const value = $(this).val();
|
const value = $(this).val();
|
||||||
const success = setCharCfg(value, settingType.guidance_scale);
|
const success = setCharCfg(value, settingType.guidance_scale);
|
||||||
|
@ -263,12 +264,9 @@ jQuery(async () => {
|
||||||
setCharCfg($(this).val(), settingType.negative_prompt);
|
setCharCfg($(this).val(), settingType.negative_prompt);
|
||||||
});
|
});
|
||||||
|
|
||||||
windowHtml.find('#use_chara_cfg').on('input', setCharCfgCheckbox);
|
|
||||||
|
|
||||||
windowHtml.find('#global_cfg_guidance_scale').on('input', function() {
|
windowHtml.find('#global_cfg_guidance_scale').on('input', function() {
|
||||||
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
||||||
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||||
console.log(extension_settings.cfg.global.guidance_scale)
|
|
||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,32 +2,67 @@ import { chat_metadata } from "../../../script.js";
|
||||||
import { extension_settings } from "../../extensions.js"
|
import { extension_settings } from "../../extensions.js"
|
||||||
import { getCharaFilename } from "../../utils.js";
|
import { getCharaFilename } from "../../utils.js";
|
||||||
|
|
||||||
|
export const cfgType = {
|
||||||
|
chat: 0,
|
||||||
|
chara: 1,
|
||||||
|
global: 2
|
||||||
|
}
|
||||||
|
export const metadataKeys = {
|
||||||
|
guidance_scale: "cfg_guidance_scale",
|
||||||
|
negative_prompt: "cfg_negative_prompt",
|
||||||
|
negative_combine: "cfg_negative_combine"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add groupchat support and fetch the CFG values for the current character
|
||||||
|
|
||||||
// Gets the CFG value from hierarchy of chat -> character -> global
|
// Gets the CFG value from hierarchy of chat -> character -> global
|
||||||
// Returns undefined values which should be handled in the respective backend APIs
|
// Returns undefined values which should be handled in the respective backend APIs
|
||||||
// If the guidance scale is 1, ignore the CFG negative prompt since it won't be used anyways
|
|
||||||
|
|
||||||
// TODO: Add the ability to combine negative prompts if specified. Proposed, chat + global and chat + chara
|
|
||||||
// TODO: Add groupchat support and fetch the CFG values for the current character
|
|
||||||
export function getCfg() {
|
export function getCfg() {
|
||||||
if (chat_metadata['guidance_scale'] !== 1) {
|
let splitNegativePrompt = [];
|
||||||
return {
|
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename());
|
||||||
guidanceScale: chat_metadata['guidance_scale'],
|
const guidanceScale = getGuidanceScale(charaCfg);
|
||||||
negativePrompt: chat_metadata['negative_prompt']
|
const chatNegativeCombine = chat_metadata[metadataKeys.negative_combine];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const charaCfg = extension_settings.cfg.chara.find((e) => e.name === getCharaFilename());
|
// If there's a guidance scale, continue. Otherwise assume undefined
|
||||||
if (charaCfg && charaCfg?.useChara) {
|
if (guidanceScale?.value && guidanceScale?.value !== 1) {
|
||||||
if (charaCfg.guidance_scale !== 1) {
|
if (guidanceScale.type === cfgType.chat || chatNegativeCombine.includes(cfgType.chat)) {
|
||||||
return {
|
splitNegativePrompt.push(chat_metadata[metadataKeys.negative_prompt]?.trim());
|
||||||
guidanceScale: charaCfg.guidance_scale,
|
|
||||||
negativePrompt: charaCfg.negative_prompt
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (extension_settings.cfg.global?.guidance_scale !== 1) {
|
|
||||||
|
if (guidanceScale.type === cfgType.chara || chatNegativeCombine.includes(cfgType.chara)) {
|
||||||
|
splitNegativePrompt.push(charaCfg.negative_prompt?.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guidanceScale.type === cfgType.global || chatNegativeCombine.includes(cfgType.global)) {
|
||||||
|
splitNegativePrompt.push(extension_settings.cfg.global.negative_prompt?.trim());
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
guidanceScale: extension_settings.cfg.global.guidance_scale,
|
guidanceScale: guidanceScale.value,
|
||||||
negativePrompt: extension_settings.cfg.global.negative_prompt
|
negativePrompt: splitNegativePrompt.filter((e) => e.length > 0).join(", ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the guidance scale is 1, ignore the CFG negative prompt since it won't be used anyways
|
||||||
|
function getGuidanceScale(charaCfg) {
|
||||||
|
const chatGuidanceScale = chat_metadata[metadataKeys.guidance_scale];
|
||||||
|
if (chatGuidanceScale && chatGuidanceScale !== 1) {
|
||||||
|
return {
|
||||||
|
type: cfgType.chat,
|
||||||
|
value: chatGuidanceScale
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charaCfg && charaCfg.guidance_scale !== 1) {
|
||||||
|
return {
|
||||||
|
type: cfgType.chara,
|
||||||
|
value: charaCfg.guidance_scale
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: cfgType.global,
|
||||||
|
value: extension_settings.cfg.global.guidance_scale
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -63,10 +63,6 @@
|
||||||
</label>
|
</label>
|
||||||
<textarea id="chara_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
<textarea id="chara_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<label class="checkbox_label" for="use_chara_cfg">
|
|
||||||
<input id="use_chara_cfg" type="checkbox" />
|
|
||||||
<span data-i18n="Use character CFG">Use character CFG</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="sysHR">
|
<hr class="sysHR">
|
||||||
|
@ -99,5 +95,33 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<hr class="sysHR">
|
||||||
|
<div class="inline-drawer">
|
||||||
|
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||||
|
<b>Negative Cascading</b>
|
||||||
|
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||||
|
</div>
|
||||||
|
<div class="inline-drawer-content">
|
||||||
|
<small>
|
||||||
|
Combine negative prompts from other boxes. For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.
|
||||||
|
</small>
|
||||||
|
<br />
|
||||||
|
<label for="cfg_negative_combine">
|
||||||
|
<span data-i18n="Scale">Always Include</span>
|
||||||
|
</label>
|
||||||
|
<label class="checkbox_label">
|
||||||
|
<input type="checkbox" name="cfg_negative_combine" value="0" />
|
||||||
|
<span data-i18n="Only Format Display">Chat Negatives</span>
|
||||||
|
</label>
|
||||||
|
<label class="checkbox_label">
|
||||||
|
<input type="checkbox" name="cfg_negative_combine" value="1" />
|
||||||
|
<span data-i18n="Only Format Display">Character Negatives</span>
|
||||||
|
</label>
|
||||||
|
<label class="checkbox_label">
|
||||||
|
<input type="checkbox" name="cfg_negative_combine" value="2" />
|
||||||
|
<span data-i18n="Use character CFG">Global Negatives</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue