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";
|
||||
import { selected_group } from "../../group-chats.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 { metadataKeys } from "./util.js";
|
||||
|
||||
// Keep track of where your extension is located, name should match repo name
|
||||
const extensionName = "cfg";
|
||||
const extensionFolderPath = `scripts/extensions/${extensionName}`;
|
||||
const defaultSettings = {
|
||||
"global": {
|
||||
global: {
|
||||
"guidance_scale": 1,
|
||||
"negative_prompt": ''
|
||||
},
|
||||
"chara": []
|
||||
chara: []
|
||||
};
|
||||
const settingType = {
|
||||
"guidance_scale": 0,
|
||||
"negative_prompt": 1
|
||||
guidance_scale: 0,
|
||||
negative_prompt: 1
|
||||
}
|
||||
|
||||
// Used for character and chat CFG values
|
||||
|
@ -64,7 +65,7 @@ function setCharCfg(tempValue, setting) {
|
|||
if (extension_settings.cfg.chara && existingCharaCfg) {
|
||||
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) {
|
||||
extension_settings.cfg.chara.splice(existingCharaCfgIndex, 1);
|
||||
}
|
||||
|
@ -73,8 +74,6 @@ function setCharCfg(tempValue, setting) {
|
|||
extension_settings.cfg.chara = []
|
||||
}
|
||||
|
||||
Object.assign(tempCharaCfg, { useChara: false })
|
||||
|
||||
extension_settings.cfg.chara.push(tempCharaCfg);
|
||||
} else {
|
||||
console.log("Character CFG error: No avatar name key could be found.");
|
||||
|
@ -88,28 +87,13 @@ function setCharCfg(tempValue, setting) {
|
|||
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) {
|
||||
switch(setting) {
|
||||
case settingType.guidance_scale:
|
||||
chat_metadata['guidance_scale'] = tempValue;
|
||||
chat_metadata[metadataKeys.guidance_scale] = tempValue;
|
||||
break;
|
||||
case settingType.negative_prompt:
|
||||
chat_metadata['negative_prompt'] = tempValue;
|
||||
chat_metadata[metadataKeys.negative_prompt] = tempValue;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
@ -173,18 +157,24 @@ function onChatChanged() {
|
|||
}
|
||||
|
||||
// Reloads chat-specific settings
|
||||
// TODO: Fix race condition bug where deleted chara CFG still loads previous prompts
|
||||
function loadSettings() {
|
||||
// Set chat CFG if it exists
|
||||
$('#chat_cfg_guidance_scale').val(chat_metadata['guidance_scale'] ?? 1.00);
|
||||
$('#chat_cfg_guidance_scale_counter').text(chat_metadata['guidance_scale']?.toFixed(2) ?? 1.00);
|
||||
$('#chat_cfg_negative_prompt').val(chat_metadata['negative_prompt'] ?? '');
|
||||
$('#chat_cfg_guidance_scale').val(chat_metadata[metadataKeys.guidance_scale] ?? 1.0.toFixed(2));
|
||||
$('#chat_cfg_guidance_scale_counter').text(chat_metadata[metadataKeys.guidance_scale]?.toFixed(2) ?? 1.0.toFixed(2));
|
||||
$('#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
|
||||
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').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 ?? '');
|
||||
$('#use_chara_cfg').prop('checked', charaCfg?.useChara ?? false);
|
||||
}
|
||||
|
||||
// Load initial extension settings
|
||||
|
@ -251,6 +241,17 @@ jQuery(async () => {
|
|||
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() {
|
||||
const value = $(this).val();
|
||||
const success = setCharCfg(value, settingType.guidance_scale);
|
||||
|
@ -263,12 +264,9 @@ jQuery(async () => {
|
|||
setCharCfg($(this).val(), settingType.negative_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#use_chara_cfg').on('input', setCharCfgCheckbox);
|
||||
|
||||
windowHtml.find('#global_cfg_guidance_scale').on('input', function() {
|
||||
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
||||
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||
console.log(extension_settings.cfg.global.guidance_scale)
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
|
|
|
@ -2,32 +2,67 @@ import { chat_metadata } from "../../../script.js";
|
|||
import { extension_settings } from "../../extensions.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
|
||||
// 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() {
|
||||
if (chat_metadata['guidance_scale'] !== 1) {
|
||||
return {
|
||||
guidanceScale: chat_metadata['guidance_scale'],
|
||||
negativePrompt: chat_metadata['negative_prompt']
|
||||
}
|
||||
}
|
||||
let splitNegativePrompt = [];
|
||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename());
|
||||
const guidanceScale = getGuidanceScale(charaCfg);
|
||||
const chatNegativeCombine = chat_metadata[metadataKeys.negative_combine];
|
||||
|
||||
const charaCfg = extension_settings.cfg.chara.find((e) => e.name === getCharaFilename());
|
||||
if (charaCfg && charaCfg?.useChara) {
|
||||
if (charaCfg.guidance_scale !== 1) {
|
||||
return {
|
||||
guidanceScale: charaCfg.guidance_scale,
|
||||
negativePrompt: charaCfg.negative_prompt
|
||||
}
|
||||
// If there's a guidance scale, continue. Otherwise assume undefined
|
||||
if (guidanceScale?.value && guidanceScale?.value !== 1) {
|
||||
if (guidanceScale.type === cfgType.chat || chatNegativeCombine.includes(cfgType.chat)) {
|
||||
splitNegativePrompt.push(chat_metadata[metadataKeys.negative_prompt]?.trim());
|
||||
}
|
||||
} 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 {
|
||||
guidanceScale: extension_settings.cfg.global.guidance_scale,
|
||||
negativePrompt: extension_settings.cfg.global.negative_prompt
|
||||
guidanceScale: guidanceScale.value,
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<hr class="sysHR">
|
||||
|
@ -99,5 +95,33 @@
|
|||
</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>
|
||||
|
|
Loading…
Reference in New Issue