Add GGUF models and denoise parameter for ComfyUI
| @@ -17,6 +17,7 @@ | ||
| 17 | 17 | <li data-placeholder="scheduler" class="sd_comfy_workflow_editor_not_found">"%scheduler%"</li> |
| 18 | 18 | <li data-placeholder="steps" class="sd_comfy_workflow_editor_not_found">"%steps%"</li> |
| 19 | 19 | <li data-placeholder="scale" class="sd_comfy_workflow_editor_not_found">"%scale%"</li> |
| 20 | + <li data-placeholder="denoise" class="sd_comfy_workflow_editor_not_found">"%denoise%"</li> | |
| 20 | 21 | <li data-placeholder="clip_skip" class="sd_comfy_workflow_editor_not_found">"%clip_skip%"</li> |
| 21 | 22 | <li data-placeholder="width" class="sd_comfy_workflow_editor_not_found">"%width%"</li> |
| 22 | 23 | <li data-placeholder="height" class="sd_comfy_workflow_editor_not_found">"%height%"</li> |
| @@ -3269,6 +3269,10 @@ async function generateComfyImage(prompt, negativePrompt, signal) { | ||
| 3269 | 3269 | |
| 3270 | 3270 | const seed = extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : Math.round(Math.random() * Number.MAX_SAFE_INTEGER); |
| 3271 | 3271 | workflow = workflow.replaceAll('"%seed%"', JSON.stringify(seed)); |
| 3272 | + | |
| 3273 | + const denoising_strength = extension_settings.sd.denoising_strength === undefined ? 1.0 : extension_settings.sd.denoising_strength; | |
| 3274 | + workflow = workflow.replaceAll('"%denoise%"', JSON.stringify(denoising_strength)); | |
| 3275 | + | |
| 3272 | 3276 | placeholders.forEach(ph => { |
| 3273 | 3277 | workflow = workflow.replaceAll(`"%${ph}%"`, JSON.stringify(extension_settings.sd[ph])); |
| 3274 | 3278 | }); |
| @@ -319,7 +319,7 @@ | ||
| 319 | 319 | <input class="neo-range-input" type="number" id="sd_hr_scale_value" data-for="sd_hr_scale" min="{{hr_scale_min}}" max="{{hr_scale_max}}" step="{{hr_scale_step}}" value="{{hr_scale}}" > |
| 320 | 320 | </div> |
| 321 | 321 | |
| 322 | 322 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,comfy"> |
| 323 | 323 | <small> |
| 324 | 324 | <span data-i18n="Denoising strength">Denoising strength</span> |
| 325 | 325 | </small> |
| @@ -7,7 +7,7 @@ import sanitize from 'sanitize-filename'; | ||
| 7 | 7 | import { sync as writeFileAtomicSync } from 'write-file-atomic'; |
| 8 | 8 | import FormData from 'form-data'; |
| 9 | 9 | |
| 10 | 10 | import { getBasicAuthHeaderdelay, delaygetBasicAuthHeader } from '../util.js'; |
| 11 | 11 | import { jsonParser } from '../express-common.js'; |
| 12 | 12 | import { readSecret, SECRET_KEYS } from './secrets.js'; |
| 13 | 13 | |
| @@ -19,7 +19,7 @@ import { readSecret, SECRET_KEYS } from './secrets.js'; | ||
| 19 | 19 | function getComfyWorkflows(directories) { |
| 20 | 20 | return fs |
| 21 | 21 | .readdirSync(directories.comfyWorkflows) |
| 22 | 22 | .filter(file => file[0] !== '.' && file.toLowerCase().endsWith('.json')) |
| 23 | 23 | .sort(Intl.Collator().compare); |
| 24 | 24 | } |
| 25 | 25 | |
| @@ -67,8 +67,7 @@ router.post('/upscalers', jsonParser, async (request, response) => { | ||
| 67 | 67 | |
| 68 | 68 | /** @type {any} */ |
| 69 | 69 | const data = await result.json(); |
| 70 | 70 | const names =return data.map(x => x.name); |
| 71 | - return names; | |
| 72 | 71 | } |
| 73 | 72 | |
| 74 | 73 | async function getLatentUpscalers() { |
| @@ -88,8 +87,7 @@ router.post('/upscalers', jsonParser, async (request, response) => { | ||
| 88 | 87 | |
| 89 | 88 | /** @type {any} */ |
| 90 | 89 | const data = await result.json(); |
| 91 | 90 | const names =return data.map(x => x.name); |
| 92 | - return names; | |
| 93 | 91 | } |
| 94 | 92 | |
| 95 | 93 | const [upscalers, latentUpscalers] = await Promise.all([getUpscalerModels(), getLatentUpscalers()]); |
| @@ -241,8 +239,7 @@ router.post('/set-model', jsonParser, async (request, response) => { | ||
| 241 | 239 | 'Authorization': getBasicAuthHeader(request.body.auth), |
| 242 | 240 | }, |
| 243 | 241 | }); |
| 244 | 242 | const data =return await result.json(); |
| 245 | - return data; | |
| 246 | 243 | } |
| 247 | 244 | |
| 248 | 245 | const url = new URL(request.body.url); |
| @@ -274,7 +271,7 @@ router.post('/set-model', jsonParser, async (request, response) => { | ||
| 274 | 271 | |
| 275 | 272 | const progress = progressState['progress']; |
| 276 | 273 | const jobCount = progressState['state']['job_count']; |
| 277 | 274 | if (progress === 0.0 && jobCount === 0) { |
| 278 | 275 | break; |
| 279 | 276 | } |
| 280 | 277 | |
| @@ -412,7 +409,17 @@ comfy.post('/models', jsonParser, async (request, response) => { | ||
| 412 | 409 | } |
| 413 | 410 | /** @type {any} */ |
| 414 | 411 | const data = await result.json(); |
| 415 | - return response.send(data.CheckpointLoaderSimple.input.required.ckpt_name[0].map(it => ({ value: it, text: it }))); | |
| 412 | + | |
| 413 | + const ckpts = data.CheckpointLoaderSimple.input.required.ckpt_name[0].map(it => ({ value: it, text: it })) || []; | |
| 414 | + | |
| 415 | + // load list of GGUF unets from diffusion_models if the loader node is available | |
| 416 | + const ggufs = data.UnetLoaderGGUF?.input.required.unet_name[0].map(it => ({ value: it, text: `GGUF: ${it}` })) || []; | |
| 417 | + const models = ckpts.concat(ggufs); | |
| 418 | + | |
| 419 | + // make the display names of the models somewhat presentable | |
| 420 | + models.forEach(it => it.text = it.text.replace(/\.[^.]*$/, '').replace(/_/g, ' ')); | |
| 421 | + | |
| 422 | + return response.send(models); | |
| 416 | 423 | } catch (error) { |
| 417 | 424 | console.log(error); |
| 418 | 425 | return response.sendStatus(500); |
| @@ -550,7 +557,13 @@ comfy.post('/generate', jsonParser, async (request, response) => { | ||
| 550 | 557 | await delay(100); |
| 551 | 558 | } |
| 552 | 559 | if (item.status.status_str === 'error') { |
| 553 | - throw new Error('ComfyUI generation did not succeed.'); | |
| 560 | + // Report node tracebacks if available | |
| 561 | + const errorMessages = item.status?.messages | |
| 562 | + ?.filter(it => it[0] === 'execution_error') | |
| 563 | + .map(it => it[1]) | |
| 564 | + .map(it => `${it.node_type} [${it.node_id}] ${it.exception_type}: ${it.exception_message}`) | |
| 565 | + .join('\n') || ''; | |
| 566 | + throw new Error(`ComfyUI generation did not succeed.\n\n${errorMessages}`.trim()); | |
| 554 | 567 | } |
| 555 | 568 | const imgInfo = Object.keys(item.outputs).map(it => item.outputs[it].images).flat()[0]; |
| 556 | 569 | const imgUrl = new URL(request.body.url); |
| @@ -563,8 +576,9 @@ comfy.post('/generate', jsonParser, async (request, response) => { | ||
| 563 | 576 | const imgBuffer = await imgResponse.buffer(); |
| 564 | 577 | return response.send(imgBuffer.toString('base64')); |
| 565 | 578 | } catch (error) { |
| 566 | 579 | console.log('ComfyUI error:', error); |
| 567 | - return response.sendStatus(500); | |
| 580 | + response.status(500).send(`${error.message}`); | |
| 581 | + return response; | |
| 568 | 582 | } |
| 569 | 583 | }); |
| 570 | 584 | |