Extend /sd command

9af4d62cdfbb7842bb4b665fc90652039b71100c

Cohee <18619528+Cohee1207@users.noreply.github.com>

3 files changed, +261 -140Ignore whitespace
public/scripts/extensions/stable-diffusion/index.js+260 -57
@@ -20,7 +20,7 @@ import {
2020} from '../../../script.js';
2121import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js';
2222import { selected_group } from '../../group-chats.js';
2323import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce, isFalseBoolean } from '../../utils.js';
2424import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js';
2525import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';
2626import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js';
@@ -31,6 +31,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
3131import { debounce_timeout } from '../../constants.js';
3232import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js';
3333import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
34+import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
3435export { MODULE_NAME };
3536
3637const MODULE_NAME = 'sd';
@@ -221,7 +222,6 @@ const defaultSettings = {
221222
222223 // Refine mode
223224 refine_mode: false,
224- expand: false,
225225 interactive_mode: false,
226226 multimodal_captioning: false,
227227 snap: false,
@@ -240,7 +240,7 @@ const defaultSettings = {
240240 drawthings_auth: '',
241241
242242 hr_upscaler: 'Latent',
243243 hr_scale: 21.0,
244244 hr_scale_min: 1.0,
245245 hr_scale_max: 4.0,
246246 hr_scale_step: 0.1,
@@ -260,10 +260,6 @@ const defaultSettings = {
260260 clip_skip: 1,
261261
262262 // NovelAI settings
263- novel_upscale_ratio_min: 1.0,
264- novel_upscale_ratio_max: 4.0,
265- novel_upscale_ratio_step: 0.1,
266- novel_upscale_ratio: 1.0,
267263 novel_anlas_guard: false,
268264 novel_sm: false,
269265 novel_sm_dyn: false,
@@ -416,7 +412,6 @@ async function loadSettings() {
416412 $('#sd_hr_scale').val(extension_settings.sd.hr_scale).trigger('input');
417413 $('#sd_denoising_strength').val(extension_settings.sd.denoising_strength).trigger('input');
418414 $('#sd_hr_second_pass_steps').val(extension_settings.sd.hr_second_pass_steps).trigger('input');
419- $('#sd_novel_upscale_ratio').val(extension_settings.sd.novel_upscale_ratio).trigger('input');
420415 $('#sd_novel_anlas_guard').prop('checked', extension_settings.sd.novel_anlas_guard);
421416 $('#sd_novel_sm').prop('checked', extension_settings.sd.novel_sm);
422417 $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn);
@@ -430,7 +425,6 @@ async function loadSettings() {
430425 $('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces);
431426 $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr);
432427 $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode);
433- $('#sd_expand').prop('checked', extension_settings.sd.expand);
434428 $('#sd_multimodal_captioning').prop('checked', extension_settings.sd.multimodal_captioning);
435429 $('#sd_auto_url').val(extension_settings.sd.auto_url);
436430 $('#sd_auto_auth').val(extension_settings.sd.auto_auth);
@@ -644,37 +638,13 @@ async function onSaveStyleClick() {
644638 saveSettingsDebounced();
645639}
646640
647-async function expandPrompt(prompt) {
648- try {
649- const response = await fetch('/api/sd/expand', {
650- method: 'POST',
651- headers: getRequestHeaders(),
652- body: JSON.stringify({ prompt: prompt }),
653- });
654-
655- if (!response.ok) {
656- throw new Error('API returned an error.');
657- }
658-
659- const data = await response.json();
660- return data.prompt;
661- } catch {
662- return prompt;
663- }
664-}
665-
666641/**
667642 * Modifies prompt based on auto-expansion and user inputs.
668643 * @param {string} prompt Prompt to refine
669- * @param {boolean} allowExpand Whether to allow auto-expansion
670644 * @param {boolean} isNegative Whether the prompt is a negative one
671645 * @returns {Promise<string>} Refined prompt
672646 */
673647async function refinePrompt(prompt, allowExpand, isNegative = false) {
674- if (allowExpand && extension_settings.sd.expand) {
675- prompt = await expandPrompt(prompt);
676- }
677-
678648 if (extension_settings.sd.refine_mode) {
679649 const text = isNegative ? '<h3>Review and edit the <i>negative</i> prompt:</h3>' : '<h3>Review and edit the prompt:</h3>';
680650 const refinedPrompt = await callGenericPopup(text + 'Press "Cancel" to abort the image generation.', POPUP_TYPE.INPUT, prompt.trim(), { rows: 5, okButton: 'Continue' });
@@ -800,11 +770,6 @@ function combinePrefixes(str1, str2, macro = '') {
800770 return process(result);
801771}
802772
803-function onExpandInput() {
804- extension_settings.sd.expand = !!$(this).prop('checked');
805- saveSettingsDebounced();
806-}
807-
808773function onRefineModeInput() {
809774 extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked');
810775 saveSettingsDebounced();
@@ -969,12 +934,6 @@ async function onViewAnlasClick() {
969934 toastr.info(`Free image generation: ${unlimitedGeneration ? 'Yes' : 'No'}`, `Anlas: ${anlas}`);
970935}
971936
972-function onNovelUpscaleRatioInput() {
973- extension_settings.sd.novel_upscale_ratio = Number($('#sd_novel_upscale_ratio').val());
974- $('#sd_novel_upscale_ratio_value').val(extension_settings.sd.novel_upscale_ratio.toFixed(1));
975- saveSettingsDebounced();
976-}
977-
978937function onNovelAnlasGuardInput() {
979938 extension_settings.sd.novel_anlas_guard = !!$('#sd_novel_anlas_guard').prop('checked');
980939 saveSettingsDebounced();
@@ -2273,6 +2232,23 @@ function getRawLastMessage() {
22732232}
22742233
22752234/**
2235+ * Ensure that the selected option exists in the dropdown.
2236+ * @param {string} setting Setting key
2237+ * @param {string} selector Dropdown selector
2238+ * @returns {void}
2239+ */
2240+function ensureSelectionExists(setting, selector) {
2241+ /** @type {HTMLSelectElement} */
2242+ const selectElement = document.querySelector(selector);
2243+ if (!selectElement) {
2244+ return;
2245+ }
2246+ if (selectElement.selectedOptions.length && !Array.from(selectElement.options).some(option => option.value === extension_settings.sd[setting])) {
2247+ extension_settings.sd[setting] = selectElement.selectedOptions[0].value;
2248+ }
2249+}
2250+
2251+/**
22762252 * Generates an image based on the given trigger word.
22772253 * @param {string} initiator The initiator of the image generation
22782254 * @param {Record<string, object>} args Command arguments
@@ -2292,8 +2268,8 @@ async function generatePicture(initiator, args, trigger, message, callback) {
22922268 return;
22932269 }
22942270
2295- extension_settings.sd.sampler = $('#sd_sampler').find(':selected').val();
2271+ ensureSelectionExists('sampler', '#sd_sampler');
2296- extension_settings.sd.model = $('#sd_model').find(':selected').val();
2272+ ensureSelectionExists('model', '#sd_model');
22972273
22982274 trigger = trigger.trim();
22992275 const generationType = getGenerationType(trigger);
@@ -2441,7 +2417,7 @@ async function getPrompt(generationType, message, trigger, quietPrompt, combineN
24412417 }
24422418
24432419 if (generationType !== generationMode.FREE) {
24442420 prompt = await refinePrompt(prompt, truefalse);
24452421 }
24462422
24472423 return prompt;
@@ -3031,7 +3007,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) {
30313007 width: width,
30323008 height: height,
30333009 negative_prompt: negativePrompt,
30343010 upscale_ratio: extension_settings.sd.novel_upscale_ratiohr_scale,
30353011 decrisper: extension_settings.sd.novel_decrisper,
30363012 sm: sm,
30373013 sm_dyn: sm_dyn,
@@ -3613,8 +3589,8 @@ async function sdMessageButton(e) {
36133589 try {
36143590 setBusyIcon(true);
36153591 if (hasSavedImage) {
36163592 const prompt = await refinePrompt(message.extra.title, false, false);
36173593 const negative = hasSavedNegative ? await refinePrompt(message.extra.negative, false, true) : '';
36183594 message.extra.title = prompt;
36193595
36203596 const generationType = message?.extra?.generationType ?? generationMode.FREE;
@@ -3756,8 +3732,8 @@ async function onImageSwiped({ message, element, direction }) {
37563732 eventSource.once(CUSTOM_STOP_EVENT, stopListener);
37573733 const callback = () => { };
37583734 const hasNegative = message.extra.negative;
37593735 const prompt = await refinePrompt(message.extra.title, false, false);
37603736 const negativePromptPrefix = hasNegative ? await refinePrompt(message.extra.negative, false, true) : '';
37613737 const characterName = context.groupId
37623738 ? context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]]?.id?.toString()
37633739 : context.characters[context.characterId]?.name;
@@ -3788,12 +3764,83 @@ async function onImageSwiped({ message, element, direction }) {
37883764 await context.saveChat();
37893765}
37903766
3767+/**
3768+ * Applies the command arguments to the extension settings.
3769+ * @typedef {import('../../slash-commands/SlashCommand.js').NamedArguments} NamedArguments
3770+ * @typedef {import('../../slash-commands/SlashCommand.js').NamedArgumentsCapture} NamedArgumentsCapture
3771+ * @param {NamedArguments | NamedArgumentsCapture} args - Command arguments
3772+ * @returns {Record<string, any>} - Current settings before applying the command arguments
3773+ */
3774+function applyCommandArguments(args) {
3775+ const overrideSettings = {};
3776+ const currentSettings = {};
3777+ const settingMap = {
3778+ 'edit': 'refine_mode',
3779+ 'extend': 'free_extend',
3780+ 'multimodal': 'multimodal_captioning',
3781+ 'seed': 'seed',
3782+ 'width': 'width',
3783+ 'height': 'height',
3784+ 'steps': 'steps',
3785+ 'cfg': 'scale',
3786+ 'skip': 'clip_skip',
3787+ 'model': 'model',
3788+ 'sampler': 'sampler',
3789+ 'scheduler': 'scheduler',
3790+ 'vae': 'vae',
3791+ 'upscaler': 'hr_upscaler',
3792+ 'scale': 'hr_scale',
3793+ 'hires': 'enable_hr',
3794+ 'denoise': 'denoising_strength',
3795+ '2ndpass': 'hr_second_pass_steps',
3796+ 'faces': 'restore_faces',
3797+ };
3798+
3799+ for (const [param, setting] of Object.entries(settingMap)) {
3800+ if (args[param] === undefined || defaultSettings[setting] === undefined) {
3801+ continue;
3802+ }
3803+ currentSettings[setting] = extension_settings.sd[setting];
3804+ const value = String(args[param]);
3805+ const type = typeof defaultSettings[setting];
3806+ switch (type) {
3807+ case 'boolean':
3808+ overrideSettings[setting] = isTrueBoolean(value) || !isFalseBoolean(value);
3809+ break;
3810+ case 'number':
3811+ overrideSettings[setting] = Number(value);
3812+ break;
3813+ default:
3814+ overrideSettings[setting] = value;
3815+ break;
3816+ }
3817+ }
3818+
3819+ Object.assign(extension_settings.sd, overrideSettings);
3820+ return currentSettings;
3821+}
3822+
37913823jQuery(async () => {
37923824 await addSDGenButtons();
37933825
37943826 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
37953827 name: 'imagine',
3796- callback: (args, trigger) => generatePicture(initiators.command, args, String(trigger)),
3828+ returns: 'URL of the generated image, or an empty string if the generation failed',
3829+ callback: async (args, trigger) => {
3830+ const currentSettings = applyCommandArguments(args);
3831+
3832+ try {
3833+ return await generatePicture(initiators.command, args, String(trigger));
3834+ } catch (error) {
3835+ console.error('Failed to generate image:', error);
3836+ return '';
3837+ } finally {
3838+ if (Object.keys(currentSettings).length) {
3839+ Object.assign(extension_settings.sd, currentSettings);
3840+ saveSettingsDebounced();
3841+ }
3842+ }
3843+ },
37973844 aliases: ['sd', 'img', 'image'],
37983845 namedArgumentList: [
37993846 new SlashCommandNamedArgument(
@@ -3803,6 +3850,164 @@ jQuery(async () => {
38033850 name: 'negative',
38043851 description: 'negative prompt prefix',
38053852 typeList: [ARGUMENT_TYPE.STRING],
3853+ isRequired: false,
3854+ acceptsMultiple: false,
3855+ }),
3856+ SlashCommandNamedArgument.fromProps({
3857+ name: 'extend',
3858+ description: 'auto-extend free mode prompts with the LLM',
3859+ typeList: [ARGUMENT_TYPE.BOOLEAN],
3860+ enumProvider: commonEnumProviders.boolean('trueFalse'),
3861+ isRequired: false,
3862+ acceptsMultiple: false,
3863+ }),
3864+ SlashCommandNamedArgument.fromProps({
3865+ name: 'edit',
3866+ description: 'edit the prompt before generation',
3867+ typeList: [ARGUMENT_TYPE.BOOLEAN],
3868+ enumProvider: commonEnumProviders.boolean('trueFalse'),
3869+ isRequired: false,
3870+ acceptsMultiple: false,
3871+ }),
3872+ SlashCommandNamedArgument.fromProps({
3873+ name: 'multimodal',
3874+ description: 'use multimodal captioning (for portraits only)',
3875+ typeList: [ARGUMENT_TYPE.BOOLEAN],
3876+ enumProvider: commonEnumProviders.boolean('trueFalse'),
3877+ isRequired: false,
3878+ acceptsMultiple: false,
3879+ }),
3880+ SlashCommandNamedArgument.fromProps({
3881+ name: 'snap',
3882+ description: 'snap auto-adjusted dimensions to the nearest known resolution (portraits and backgrounds only)',
3883+ typeList: [ARGUMENT_TYPE.BOOLEAN],
3884+ enumProvider: commonEnumProviders.boolean('trueFalse'),
3885+ isRequired: false,
3886+ acceptsMultiple: false,
3887+ }),
3888+ SlashCommandNamedArgument.fromProps({
3889+ name: 'seed',
3890+ description: 'random seed',
3891+ isRequired: false,
3892+ typeList: [ARGUMENT_TYPE.NUMBER],
3893+ acceptsMultiple: false,
3894+ }),
3895+ SlashCommandNamedArgument.fromProps({
3896+ name: 'width',
3897+ description: 'image width',
3898+ isRequired: false,
3899+ typeList: [ARGUMENT_TYPE.NUMBER],
3900+ acceptsMultiple: false,
3901+ }),
3902+ SlashCommandNamedArgument.fromProps({
3903+ name: 'height',
3904+ description: 'image height',
3905+ isRequired: false,
3906+ typeList: [ARGUMENT_TYPE.NUMBER],
3907+ acceptsMultiple: false,
3908+ }),
3909+ SlashCommandNamedArgument.fromProps({
3910+ name: 'steps',
3911+ description: 'number of steps',
3912+ isRequired: false,
3913+ typeList: [ARGUMENT_TYPE.NUMBER],
3914+ acceptsMultiple: false,
3915+ }),
3916+ SlashCommandNamedArgument.fromProps({
3917+ name: 'cfg',
3918+ description: 'CFG scale',
3919+ isRequired: false,
3920+ typeList: [ARGUMENT_TYPE.NUMBER],
3921+ acceptsMultiple: false,
3922+ }),
3923+ SlashCommandNamedArgument.fromProps({
3924+ name: 'skip',
3925+ description: 'CLIP skip layers',
3926+ isRequired: false,
3927+ typeList: [ARGUMENT_TYPE.NUMBER],
3928+ acceptsMultiple: false,
3929+ }),
3930+ SlashCommandNamedArgument.fromProps({
3931+ name: 'model',
3932+ description: 'model override',
3933+ isRequired: false,
3934+ typeList: [ARGUMENT_TYPE.STRING],
3935+ acceptsMultiple: false,
3936+ forceEnum: true,
3937+ enumProvider: () => Array.from(document.querySelectorAll('#sd_model > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)),
3938+ }),
3939+ SlashCommandNamedArgument.fromProps({
3940+ name: 'sampler',
3941+ description: 'sampler override',
3942+ isRequired: false,
3943+ typeList: [ARGUMENT_TYPE.STRING],
3944+ acceptsMultiple: false,
3945+ forceEnum: true,
3946+ enumProvider: () => Array.from(document.querySelectorAll('#sd_sampler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)),
3947+ }),
3948+ SlashCommandNamedArgument.fromProps({
3949+ name: 'scheduler',
3950+ description: 'scheduler override',
3951+ isRequired: false,
3952+ typeList: [ARGUMENT_TYPE.STRING],
3953+ acceptsMultiple: false,
3954+ forceEnum: true,
3955+ enumProvider: () => Array.from(document.querySelectorAll('#sd_scheduler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)),
3956+ }),
3957+ SlashCommandNamedArgument.fromProps({
3958+ name: 'vae',
3959+ description: 'VAE name override',
3960+ isRequired: false,
3961+ typeList: [ARGUMENT_TYPE.STRING],
3962+ acceptsMultiple: false,
3963+ forceEnum: true,
3964+ enumProvider: () => Array.from(document.querySelectorAll('#sd_vae > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)),
3965+ }),
3966+ SlashCommandNamedArgument.fromProps({
3967+ name: 'upscaler',
3968+ description: 'upscaler override',
3969+ isRequired: false,
3970+ typeList: [ARGUMENT_TYPE.STRING],
3971+ acceptsMultiple: false,
3972+ forceEnum: true,
3973+ enumProvider: () => Array.from(document.querySelectorAll('#sd_hr_upscaler > [value]')).map(o => new SlashCommandEnumValue(o.getAttribute('value'), o.textContent)),
3974+ }),
3975+ SlashCommandNamedArgument.fromProps({
3976+ name: 'hires',
3977+ description: 'enable high-res fix',
3978+ isRequired: false,
3979+ typeList: [ARGUMENT_TYPE.BOOLEAN],
3980+ acceptsMultiple: false,
3981+ enumProvider: commonEnumProviders.boolean('trueFalse'),
3982+ }),
3983+ SlashCommandNamedArgument.fromProps({
3984+ name: 'scale',
3985+ description: 'upscale amount',
3986+ isRequired: false,
3987+ typeList: [ARGUMENT_TYPE.NUMBER],
3988+ acceptsMultiple: false,
3989+ }),
3990+ SlashCommandNamedArgument.fromProps({
3991+ name: 'denoise',
3992+ description: 'denoising strength',
3993+ isRequired: false,
3994+ typeList: [ARGUMENT_TYPE.NUMBER],
3995+ acceptsMultiple: false,
3996+ }),
3997+ SlashCommandNamedArgument.fromProps({
3998+ name: '2ndpass',
3999+ description: 'second pass steps',
4000+ isRequired: false,
4001+ typeList: [ARGUMENT_TYPE.NUMBER],
4002+ acceptsMultiple: false,
4003+ }),
4004+ SlashCommandNamedArgument.fromProps({
4005+ name: 'faces',
4006+ description: 'restore faces',
4007+ isRequired: false,
4008+ typeList: [ARGUMENT_TYPE.BOOLEAN],
4009+ acceptsMultiple: false,
4010+ enumProvider: commonEnumProviders.boolean('trueFalse'),
38064011 }),
38074012 ],
38084013 unnamedArgumentList: [
@@ -3874,7 +4079,6 @@ jQuery(async () => {
38744079 $('#sd_hr_scale').on('input', onHrScaleInput);
38754080 $('#sd_denoising_strength').on('input', onDenoisingStrengthInput);
38764081 $('#sd_hr_second_pass_steps').on('input', onHrSecondPassStepsInput);
3877- $('#sd_novel_upscale_ratio').on('input', onNovelUpscaleRatioInput);
38784082 $('#sd_novel_anlas_guard').on('input', onNovelAnlasGuardInput);
38794083 $('#sd_novel_view_anlas').on('click', onViewAnlasClick);
38804084 $('#sd_novel_sm').on('input', onNovelSmInput);
@@ -3887,7 +4091,6 @@ jQuery(async () => {
38874091 $('#sd_comfy_open_workflow_editor').on('click', onComfyOpenWorkflowEditorClick);
38884092 $('#sd_comfy_new_workflow').on('click', onComfyNewWorkflowClick);
38894093 $('#sd_comfy_delete_workflow').on('click', onComfyDeleteWorkflowClick);
3890- $('#sd_expand').on('input', onExpandInput);
38914094 $('#sd_style').on('change', onStyleSelect);
38924095 $('#sd_save_style').on('click', onSaveStyleClick);
38934096 $('#sd_delete_style').on('click', onDeleteStyleClick);
public/scripts/extensions/stable-diffusion/settings.html+1 -14
@@ -27,11 +27,6 @@
2727 <span data-i18n="sd_free_extend_txt">Extend free mode prompts</span>
2828 <small data-i18n="sd_free_extend_small">(interactive/commands)</small>
2929 </label>
30- <label for="sd_expand" class="checkbox_label" data-i18n="[title]sd_expand" title="Automatically extend prompts using text generation model">
31- <input id="sd_expand" type="checkbox" />
32- <span data-i18n="sd_expand_txt">Auto-extend prompts</span>
33- <span class="right_menu_button fa-solid fa-triangle-exclamation" data-i18n="[title]sd_expand_warning" title="May produce unexpected results. Manual prompt editing is recommended."></span>
34- </label>
3530 <label for="sd_snap" class="checkbox_label" data-i18n="[title]sd_snap" title="Snap generation requests with a forced aspect ratio (portraits, backgrounds) to the nearest known resolution, while trying to preserve the absolute pixel counts (recommended for SDXL).">
3631 <input id="sd_snap" type="checkbox" />
3732 <span data-i18n="sd_snap_txt">Snap auto-adjusted resolutions</span>
@@ -308,7 +303,7 @@
308303 </div>
309304
310305 <div class="flex-container">
311306 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,drawthings,novel">
312307 <small>
313308 <span data-i18n="Upscale by">Upscale by</span>
314309 </small>
@@ -332,14 +327,6 @@
332327 <input class="neo-range-input" type="number" id="sd_hr_second_pass_steps_value" data-for="sd_hr_second_pass_steps" max="{{hr_second_pass_steps_max}}" step="{{hr_second_pass_steps_step}}" value="{{hr_second_pass_steps}}" >
333328 </div>
334329
335- <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="novel">
336- <small>
337- <span data-i18n="Upscale by">Upscale by</span>
338- </small>
339- <input class="neo-range-slider" type="range" id="sd_novel_upscale_ratio" name="sd_novel_upscale_ratio" min="{{novel_upscale_ratio_min}}" max="{{novel_upscale_ratio_max}}" step="{{novel_upscale_ratio_step}}" value="{{novel_upscale_ratio}}" >
340- <input class="neo-range-input" type="number" id="sd_novel_upscale_ratio_value" data-for="sd_novel_upscale_ratio" min="{{novel_upscale_ratio_min}}" max="{{novel_upscale_ratio_max}}" step="{{novel_upscale_ratio_step}}" value="{{novel_upscale_ratio}}" >
341- </div>
342-
343330 <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,comfy,horde,drawthings,extras">
344331 <small>
345332 <span data-i18n="CLIP Skip">CLIP Skip</span>
src/endpoints/stable-diffusion.js+0 -69
@@ -10,41 +10,6 @@ const { readSecret, SECRET_KEYS } = require('./secrets.js');
1010const FormData = require('form-data');
1111
1212/**
13- * Sanitizes a string.
14- * @param {string} x String to sanitize
15- * @returns {string} Sanitized string
16- */
17-function safeStr(x) {
18- x = String(x);
19- x = x.replace(/ +/g, ' ');
20- x = x.trim();
21- x = x.replace(/^[\s,.]+|[\s,.]+$/g, '');
22- return x;
23-}
24-
25-const splitStrings = [
26- ', extremely',
27- ', intricate,',
28-];
29-
30-const dangerousPatterns = '[]【】()()|::';
31-
32-/**
33- * Removes patterns from a string.
34- * @param {string} x String to sanitize
35- * @param {string} pattern Pattern to remove
36- * @returns {string} Sanitized string
37- */
38-function removePattern(x, pattern) {
39- for (let i = 0; i < pattern.length; i++) {
40- let p = pattern[i];
41- let regex = new RegExp('\\' + p, 'g');
42- x = x.replace(regex, '');
43- }
44- return x;
45-}
46-
47-/**
4813 * Gets the comfy workflows.
4914 * @param {import('../users.js').UserDirectoryList} directories
5015 * @returns {string[]} List of comfy workflows
@@ -391,40 +356,6 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => {
391356 }
392357});
393358
394-/**
395- * SD prompt expansion using GPT-2 text generation model.
396- * Adapted from: https://github.com/lllyasviel/Fooocus/blob/main/modules/expansion.py
397- */
398-router.post('/expand', jsonParser, async (request, response) => {
399- const originalPrompt = request.body.prompt;
400-
401- if (!originalPrompt) {
402- console.warn('No prompt provided for SD expansion.');
403- return response.send({ prompt: '' });
404- }
405-
406- console.log('Refine prompt input:', originalPrompt);
407- const splitString = splitStrings[Math.floor(Math.random() * splitStrings.length)];
408- let prompt = safeStr(originalPrompt) + splitString;
409-
410- try {
411- const task = 'text-generation';
412- const module = await import('../transformers.mjs');
413- const pipe = await module.default.getPipeline(task);
414-
415- const result = await pipe(prompt, { num_beams: 1, max_new_tokens: 256, do_sample: true });
416-
417- const newText = result[0].generated_text;
418- const newPrompt = safeStr(removePattern(newText, dangerousPatterns));
419- console.log('Refine prompt output:', newPrompt);
420-
421- return response.send({ prompt: newPrompt });
422- } catch {
423- console.warn('Failed to load transformers.js pipeline.');
424- return response.send({ prompt: originalPrompt });
425- }
426-});
427-
428359const comfy = express.Router();
429360
430361comfy.post('/ping', jsonParser, async (request, response) => {