import type { ComfyBoxStdGroupAestheticEmbedding, ComfyBoxStdGroupCheckpoint, ComfyBoxStdGroupDDetailer, ComfyBoxStdGroupDynamicThresholding, ComfyBoxStdGroupHypernetwork, ComfyBoxStdGroupKSampler, ComfyBoxStdGroupLatentImage, ComfyBoxStdGroupLatentUpscale, ComfyBoxStdGroupLoRA, ComfyBoxStdGroupSelfAttentionGuidance, ComfyBoxStdParameters, ComfyBoxStdPrompt } from "./ComfyBoxStdPrompt"; import type { A1111ParsedInfotext } from "./parseA1111"; function getSamplerAndScheduler(a1111Sampler: string): [string, string] { let name = a1111Sampler.toLowerCase().replace("++", "pp").replaceAll(" ", "_"); let scheduler = "normal"; if (name.includes("karras")) { name = name.replace("karras", "").replace(/_+$/, ""); scheduler = "karras"; } else { scheduler = "normal" } return [name, scheduler] } const reAddNetModelName = /^([^(]+)\((.+)\)$/; function parseAddNetModelNameAndHash(name: string | null): [string | undefined, string | undefined] { if (!name) return [undefined, undefined] const match = name.match(reAddNetModelName); if (match != null) { return [match[1], match[2]] } return [undefined, undefined] } const reDDetailerModelName = /(.+)\s\[(.+)\]/; function parseDDetailerModelNameAndHash(name: string | null): [string | undefined, string | undefined] { if (!name || name === "None") return [undefined, undefined] // bbox\mmdet_anime-face_yolov3.pth [51e1af4a] const match = name.match(reDDetailerModelName); if (match != null) { return [match[1], match[2]] } return [undefined, undefined] } export default function convertA1111ToStdPrompt(infotext: A1111ParsedInfotext): ComfyBoxStdPrompt { const popOpt = (name: string): string | undefined => { const v = infotext.extraParams[name]; delete infotext.extraParams[name]; return v; } const parameters: ComfyBoxStdParameters = {} parameters.conditioning = [ { "$meta": { types: ["positive"] }, text: infotext.positive, }, { "$meta": { types: ["negative"] }, text: infotext.negative, } ] const hrUp = popOpt("hires upscale"); const hrSz = popOpt("hires resize"); let hrScaleBy = hrUp ? parseFloat(hrUp) : undefined; let hrMethod = popOpt("hires upscaler"); let hrSteps = popOpt("hires steps"); let hrWidth = undefined let hrHeight = undefined if (hrSz) { [hrWidth, hrHeight] = hrSz.split(hrSz).map(parseInt); } const latent_image: ComfyBoxStdGroupLatentImage = { width: infotext.width, height: infotext.height, // type: "empty", // detect txt2img??? batch_count: infotext.batchSize, batch_pos: infotext.batchPos, } const maskBlur = popOpt("mask blur") if (maskBlur != null) latent_image.mask_blur = parseFloat(maskBlur) parameters.latent_image = [latent_image]; if (hrMethod != null) { let uw, uh; if (hrScaleBy) { uw = infotext.width * hrScaleBy; uh = infotext.height * hrScaleBy; } else { if (hrWidth == null || hrHeight == null) throw new Error("Highres prompt didn't have width/height!") uw = +hrWidth; uh = +hrHeight; } const hr: ComfyBoxStdGroupLatentUpscale = { width: uw, height: uh, upscale_by: hrScaleBy, upscale_method: hrMethod } parameters.latent_upscale = [hr]; } const [sampler_name, scheduler] = getSamplerAndScheduler(infotext.sampler) const k_sampler: ComfyBoxStdGroupKSampler = { steps: infotext.steps, seed: infotext.seed, cfg_scale: infotext.cfgScale, denoise: hrMethod != null ? 1.0 : infotext.denoise || 1.0, // detect img2img??? sampler_name, scheduler, } parameters.k_sampler = [k_sampler]; if (hrMethod != null) { const k_sampler_hr: ComfyBoxStdGroupKSampler = { "$meta": { types: ["upscale"] }, steps: hrSteps != null ? parseInt(hrSteps) : infotext.steps, seed: infotext.seed, cfg_scale: infotext.cfgScale, denoise: infotext.denoise || 1.0, sampler_name, scheduler, } parameters.k_sampler.push(k_sampler_hr) } if (infotext.modelHash || infotext.modelName) { const checkpoint: ComfyBoxStdGroupCheckpoint = { model_name: infotext.modelName, model_hashes: { a1111_shorthash: infotext.modelHash } } parameters.checkpoint = [checkpoint] } if ("clip skip" in infotext.extraParams) { const clipSkip = popOpt("clip skip") parameters.clip = [{ clip_skip: parseInt(clipSkip) }] } if ("sd upscale upscaler" in infotext.extraParams) { const sdUpscaleUpscaler = popOpt("sd upscale upscaler") const sdUpscaleOverlap = popOpt("sd upscale overlap") || "64" parameters.sd_upscale = [{ upscaler: sdUpscaleUpscaler, overlap: parseInt(sdUpscaleOverlap) }] } if ("aesthetic embedding" in infotext.extraParams) { const slerp = popOpt("aesthetic slerp") === "True" const aesthetic_embedding: ComfyBoxStdGroupAestheticEmbedding = { model_name: popOpt("aesthetic embedding"), lr: parseFloat(popOpt("aesthetic lr")), slerp, slerp_angle: parseFloat(popOpt("aesthetic slerp angle")), steps: parseInt(popOpt("aesthetic steps")), text: popOpt("aesthetic text"), text_negative: popOpt("aesthetic text negative") === "True", weight: parseFloat(popOpt("aesthetic weight")), } parameters.aesthetic_embedding = [aesthetic_embedding] } if ("dynamic thresholding enabled" in infotext.extraParams) { const dtEnabled = popOpt("dynamic thresholding enabled") if (dtEnabled === "True") { const dynamic_thresholding: ComfyBoxStdGroupDynamicThresholding = { mimic_scale: parseInt(popOpt("mimic scale")), threshold_percentile: parseFloat(popOpt("threshold percentile")), mimic_mode: popOpt("mimic mode"), mimic_scale_minimum: parseFloat(popOpt("mimic scale minimum")), cfg_mode: popOpt("cfg mode"), cfg_scale_minimum: parseFloat(popOpt("cfg scale minimum")), } parameters.dynamic_thresholding = [dynamic_thresholding] } } if ("sag guidance scale" in infotext.extraParams) { const self_attention_guidance: ComfyBoxStdGroupSelfAttentionGuidance = { guidance_scale: parseFloat(popOpt("sag guidance scale")), mask_threshold: parseFloat(popOpt("sag mask threshold")), } parameters.self_attention_guidance = [self_attention_guidance] } if ("ddetailer prompt" in infotext.extraParams) { const positive_prompt = popOpt("ddetailer prompt") const negative_prompt = popOpt("ddetailer neg prompt") const bitwise = popOpt("ddetailer bitwise") const denoise = parseFloat(popOpt("ddetailer denoising")) const inpaint_full = popOpt("ddetailer inpaint full") === "True" const inpaint_padding = parseInt(popOpt("ddetailer inpaint padding")) const mask_blur = parseFloat(popOpt("ddetailer mask blur")) const cfg = parseFloat(popOpt("ddetailer cfg")) const preprocess_b = popOpt("ddetailer preprocess b") === "True" const [model_a, model_a_shorthash] = parseDDetailerModelNameAndHash(popOpt("ddetailer model a")) const [model_b, model_b_shorthash] = parseDDetailerModelNameAndHash(popOpt("ddetailer model b")) const ddetailer_a: ComfyBoxStdGroupDDetailer = { positive_prompt, negative_prompt, bitwise, denoise, inpaint_full, inpaint_padding, mask_blur, cfg, model: model_a, model_hashes: model_a_shorthash ? { a1111_shorthash: model_a_shorthash } : undefined, preprocess: !preprocess_b, conf: parseFloat(popOpt("ddetailer conf a")), dilation: parseFloat(popOpt("ddetailer dilation a")), offset_x: parseFloat(popOpt("ddetailer offset x a")), offset_y: parseFloat(popOpt("ddetailer offset y a")), } const ddetailer_b: ComfyBoxStdGroupDDetailer = { positive_prompt, negative_prompt, bitwise, denoise, inpaint_full, inpaint_padding, mask_blur, cfg, model: model_b, model_hashes: model_b_shorthash ? { a1111_shorthash: model_b_shorthash } : undefined, preprocess: preprocess_b, conf: parseFloat(popOpt("ddetailer conf b")), dilation: parseFloat(popOpt("ddetailer dilation b")), offset_x: parseFloat(popOpt("ddetailer offset x b")), offset_y: parseFloat(popOpt("ddetailer offset y b")), } parameters.ddetailer = [ddetailer_a, ddetailer_b] } // TODO ControlNet for (const [extraNetworkType, extraNetworks] of Object.entries(infotext.extraNetworks)) { for (const extraNetworkParams of extraNetworks) { let strength; switch (extraNetworkType.toLowerCase()) { case "lora": case "locon": case "lyco": strength = parseFloat(extraNetworkParams.items[1]); const lora: ComfyBoxStdGroupLoRA = { module_name: extraNetworkType.toLowerCase(), model_name: extraNetworkParams.items[0], strength_unet: strength, strength_tenc: strength, } if (parameters.lora) parameters.lora.push(lora) else parameters.lora = [lora] break; case "hypernet": strength = parseFloat(extraNetworkParams.items[1]); const hypernetwork: ComfyBoxStdGroupHypernetwork = { model_name: extraNetworkParams.items[0], strength } if (parameters.hypernetwork) parameters.hypernetwork.push(hypernetwork) else parameters.hypernetwork = [hypernetwork] break; default: break; } } delete infotext.extraNetworks[extraNetworkType] } let index = 1; let found = infotext.extraParams[`addnet module ${index}`] while (`addnet module ${index}` in infotext.extraParams) { popOpt("addnet enabled") const moduleName = popOpt(`addnet module ${index}`) const modelName = popOpt(`addnet model ${index}`); const weight = popOpt(`addnet weight ${index}`); let weightA = popOpt(`addnet weight a ${index}`); let weightB = popOpt(`addnet weight b ${index}`); if (weightA == null || weightB == null) { // linked weights before addnet version update weightA = weight; weightB = weight; } if (moduleName == null || modelName == null || weightA == null || weightB == null) { throw new Error(`Error parsing addnet model params: ${moduleName} ${modelName} ${weightA} ${weightB}`) } if (moduleName !== "LoRA") { throw new Error("Unknown AddNet model type " + moduleName) } const [name, hash] = parseAddNetModelNameAndHash(modelName); if (name == null || hash == null) { throw new Error("Error parsing addnet model name: " + JSON.stringify(modelName)); } let shorthash = undefined let shorthash_legacy = undefined if (hash.length > 8) { // new method using safetensors hash shorthash = hash } else { // old hash using webui's 0x10000 hashing method shorthash_legacy = hash } const lora: ComfyBoxStdGroupLoRA = { model_name: name, module_name: moduleName, model_hashes: { addnet_shorthash: shorthash, addnet_shorthash_legacy: shorthash_legacy }, strength_unet: parseFloat(weightA), strength_tenc: parseFloat(weightB), } if (parameters.lora) parameters.lora.push(lora) else parameters.lora = [lora] index += 1; found = infotext.extraParams[`addnet model ${index}`] } let app_version = popOpt("version") const extra_data: Record = {}; if (Object.keys(infotext.extraParams).length > 0) { extra_data.a1111 = { params: infotext.extraParams } } const prompt: ComfyBoxStdPrompt = { version: 1, metadata: { created_with: "stable-diffusion-webui", app_version, extra_data }, parameters } console.warn("Unhandled A1111 parameters:", infotext.extraParams, infotext.extraNetworks) return prompt }