# Imports import json import os import sys from typing import Sequence, Mapping, Any, Union ALIGN_MODEL_TYPE = "SDXL" ALIGN_SCHEDULER = "AlignYourSteps" GENERATION = 2 ANIMA_CLIP = "2_qwen_3_06b_base.safetensors" GRID_SIZE = 5 LATENT_SCALE = 8 REGIONAL_FEATHER = .5 DETAILER_GUIDE_SIZE = 768 DETAILER_MAX_SIZE = 1024 DETAILER_THRESHOLD = .5 DETAILER_DILATION = 10 DETAILER_CROP = 3 DETAILER_FEATHER = 5 DETAILER_DROP_SIZE = 10 STYLE_IPADAPTER = "2_ip-adapter-plus_sdxl_vit-h.safetensors" STYLE_CLIP_VISION = "1_CLIP-ViT-H-fp16.safetensors" STYLE_WEIGHT_TYPE = "style transfer" STYLE_EMBEDS_SCALING = "V only" STYLE_IMAGE_SIZE = 1024 def upscale_size(width, height, scale): return tuple( round(value / LATENT_SCALE * scale) * LATENT_SCALE for value in (width, height) ) def mask_box(x, y, width, height, image_width, image_height): x1 = round(x * image_width) y1 = round(y * image_height) x2 = round((x + width) * image_width) y2 = round((y + height) * image_height) box_width = x2 - x1 box_height = y2 - y1 x_edges = int(x1 > 0) + int(x2 < image_width) y_edges = int(y1 > 0) + int(y2 < image_height) feather_x = min( round(image_width / GRID_SIZE * REGIONAL_FEATHER), box_width // max(1, x_edges), ) feather_y = min( round(image_height / GRID_SIZE * REGIONAL_FEATHER), box_height // max(1, y_edges), ) return ( x1, y1, box_width, box_height, feather_x if x1 else 0, feather_y if y1 else 0, feather_x if x2 < image_width else 0, feather_y if y2 < image_height else 0, ) def is_anima_model(name): number, separator, model = ( name.rsplit("/", 1)[-1].casefold().partition("_") ) return ( bool(separator) and number.isdigit() and model.startswith("anima") and not model.startswith("animag") ) def get_value_at_index(obj: Union[Sequence, Mapping], index: int) -> Any: """Return a sequence or mapping result item by index.""" try: return obj[index] except KeyError: return obj["result"][index] def get_comfyui_path() -> str: """Return the configured ComfyUI path, preferring COMFYUI_PATH when set.""" comfyui_path = os.environ.get("COMFYUI_PATH") if comfyui_path: return comfyui_path return find_path("ComfyUI") def find_path(name: str, path: str = None) -> str: """Recursively search parent folders until the named entry is found.""" if path is None: path = os.getcwd() if name in os.listdir(path): path_name = os.path.join(path, name) print(f"{name} found: {path_name}") return path_name parent_directory = os.path.dirname(path) if parent_directory == path: return None return find_path(name, parent_directory) def add_comfyui_directory_to_sys_path() -> None: """Add the ComfyUI checkout to sys.path.""" comfyui_path = get_comfyui_path() if comfyui_path is not None and os.path.isdir(comfyui_path): if comfyui_path in sys.path: sys.path.remove(comfyui_path) sys.path.insert(0, comfyui_path) print(f"'{comfyui_path}' added to sys.path") def add_extra_model_paths() -> None: """Load ComfyUI extra model paths configuration when available.""" try: from main import load_extra_path_config except ImportError: print( "Could not import load_extra_path_config from main.py. Looking in utils.extra_config instead." ) from utils.extra_config import load_extra_path_config extra_model_paths = find_path("extra_model_paths.yaml") if extra_model_paths is not None: load_extra_path_config(extra_model_paths) else: print("Could not find the extra_model_paths config file.") def bootstrap_comfyui_runtime() -> None: """Mirror the allocator-related ComfyUI startup steps before torch import.""" add_comfyui_directory_to_sys_path() import comfy.options comfy.options.enable_args_parsing() from comfy.cli_args import args if os.name == "nt": os.environ["MIMALLOC_PURGE_DELAY"] = "0" if args.default_device is not None: default_dev = args.default_device devices = list(range(32)) devices.remove(default_dev) devices.insert(0, default_dev) devices = ",".join(map(str, devices)) os.environ["CUDA_VISIBLE_DEVICES"] = str(devices) os.environ["HIP_VISIBLE_DEVICES"] = str(devices) if args.cuda_device is not None: os.environ["CUDA_VISIBLE_DEVICES"] = str(args.cuda_device) os.environ["HIP_VISIBLE_DEVICES"] = str(args.cuda_device) os.environ["ASCEND_RT_VISIBLE_DEVICES"] = str(args.cuda_device) if args.oneapi_device_selector is not None: os.environ["ONEAPI_DEVICE_SELECTOR"] = args.oneapi_device_selector if args.deterministic and "CUBLAS_WORKSPACE_CONFIG" not in os.environ: os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" import cuda_malloc if "rocm" in cuda_malloc.get_torch_version_noimport(): os.environ["OCL_SET_SVM_SIZE"] = "262144" def cleanup_comfyui_runtime(unload_models: bool | None = None) -> None: """Best-effort cleanup for embedded or repeated generated-script execution.""" import gc def run_cleanup_hook(name: str, should_run: bool = True) -> None: if not should_run or not hasattr(model_management, name): return cleanup_fn = getattr(model_management, name) try: cleanup_fn() except Exception as exc: warnings.warn( f"ComfyUI cleanup hook {name} failed during teardown: {exc}", RuntimeWarning, stacklevel=2, ) should_unload = unload_models if should_unload is None: should_unload = os.environ.get( "COMFYUI_TOPYTHON_UNLOAD_MODELS", "" ).lower() in { "1", "true", "yes", "on", } try: import comfy.model_management as model_management except ModuleNotFoundError: gc.collect() return run_cleanup_hook("cleanup_models_gc") run_cleanup_hook("unload_all_models", should_run=should_unload) run_cleanup_hook("soft_empty_cache") gc.collect() def import_custom_nodes() -> None: """Initialize ComfyUI custom nodes in the exporter runtime.""" comfyui_path = get_comfyui_path() if comfyui_path and comfyui_path not in sys.path: sys.path.insert(0, comfyui_path) import asyncio import execution from nodes import init_extra_nodes if comfyui_path in sys.path: sys.path.remove(comfyui_path) sys.path.insert(0, comfyui_path) import server from app.assets.manager import default_asset_manager loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: server_instance = server.PromptServer(loop, default_asset_manager()) execution.PromptQueue(server_instance) loop.run_until_complete(init_extra_nodes()) finally: asyncio.set_event_loop(None) loop.close() # Workflow data def build_workflow() -> dict[str, Any]: return { "1": { "inputs": {"ckpt_name": "52_novaAnimeXL_ilV190.safetensors"}, "class_type": "CheckpointLoaderSimple", "_meta": {"title": "Loader"}, }, "2": { "inputs": {"text": ["118", 0], "clip": ["117", 1]}, "class_type": "CLIPTextEncode", "_meta": {"title": "CLIP Text Encode (Prompt)"}, }, "3": { "inputs": { "text": "(censored, mosaic censoring, bar censor:1.1), bad " "quality, worst quality, worst detail, bad anatomy, " "extra fingers, extra toes, extra legs, 4 toes, 6 " "toes, 4 fingers, 6 fingers, malformed fingers, " "extra limbs, missing fingers, extra arms, censored, " "deformed, disfigured, text, (multiple views:1.1)", "clip": ["28", 1], }, "class_type": "CLIPTextEncode", "_meta": {"title": "CLIP Text Encode (Prompt)"}, }, "5": { "inputs": { "seed": 809278554234612, "steps": 16, "cfg": 4, "sampler_name": "euler_ancestral", "scheduler": "karras", "denoise": 1, "model": ["117", 0], "positive": ["2", 0], "negative": ["3", 0], "latent_image": ["27", 0], }, "class_type": "KSampler", "_meta": {"title": "KSampler"}, }, "10": { "inputs": { "lora_name": "8_bikabaka.safetensors", "strength_model": 0.3, "strength_clip": 0, "model": ["1", 0], "clip": ["1", 1], }, "class_type": "LoraLoader", "_meta": {"title": "Load LoRA"}, }, "27": { "inputs": {"width": 1152, "height": 896, "batch_size": 1}, "class_type": "EmptyLatentImage", "_meta": {"title": "Empty Landscape"}, }, "28": { "inputs": { "lora_name": "43_5cm-illustriousXL_v01_V1-CAME-000035.safetensors", "strength_model": 0.4, "strength_clip": 0, "model": ["38", 0], "clip": ["38", 1], }, "class_type": "LoraLoader", "_meta": {"title": "Load LoRA"}, }, "38": { "inputs": { "lora_name": "42_アップスケール_remacri_original.pt", "strength_model": 0.4, "strength_clip": 0, "model": ["10", 0], "clip": ["10", 1], }, "class_type": "LoraLoader", "_meta": {"title": "Load LoRA"}, }, "45": { "inputs": {"vae_name": "3_sdxlVAE_sdxlVAE.safetensors"}, "class_type": "VAELoader", "_meta": {"title": "Load VAE"}, }, "56": { "inputs": {"samples": ["5", 0], "vae": ["45", 0]}, "class_type": "VAEDecode", "_meta": {"title": "VAE Decode"}, }, "72": { "inputs": {"images": ["56", 0]}, "class_type": "PreviewImage", "_meta": {"title": "Preview Image"}, }, "117": { "inputs": { "lora_name": "5_add_saturation_XL.safetensors", "strength_model": -1.4, "strength_clip": 0, "model": ["28", 0], "clip": ["28", 1], }, "class_type": "LoraLoader", "_meta": {"title": "Load LoRA (Model and CLIP)"}, }, "118": { "inputs": { "string_a": "%prompt%", "string_b": "", "delimiter": "", }, "class_type": "StringConcatenate", "_meta": {"title": "Concatenate Text"}, }, } def build_extra_pnginfo() -> dict[str, Any] | None: return { "workflow": { "id": "e69619af-5ceb-4a83-821d-68180291905e", "revision": 0, "last_node_id": 121, "last_link_id": 57, "nodes": [ { "id": 27, "type": "EmptyLatentImage", "pos": [100, 358], "size": [270, 106], "flags": {}, "order": 0, "mode": 0, "inputs": [], "outputs": [{"name": "LATENT", "type": "LATENT", "links": [24]}], "title": "Empty Landscape", "properties": {"Node name for S&R": "EmptyLatentImage"}, "widgets_values": [1152, 896, 1], }, { "id": 45, "type": "VAELoader", "pos": [100, 594], "size": [270, 58], "flags": {}, "order": 1, "mode": 0, "inputs": [], "outputs": [{"name": "VAE", "type": "VAE", "links": [32]}], "properties": {"Node name for S&R": "VAELoader"}, "widgets_values": ["3_sdxlVAE_sdxlVAE.safetensors"], }, { "id": 56, "type": "VAEDecode", "pos": [2948.649165895271, 134.76727061509087], "size": [140, 46], "flags": {}, "order": 11, "mode": 0, "inputs": [ {"name": "samples", "type": "LATENT", "link": 31}, {"name": "vae", "type": "VAE", "link": 32}, ], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [33]}], "properties": {"Node name for S&R": "VAEDecode"}, "widgets_values": [], }, { "id": 10, "type": "LoraLoader", "pos": [600, 130], "size": [290.43334045410154, 126], "flags": {}, "order": 4, "mode": 0, "inputs": [ {"name": "model", "type": "MODEL", "link": 25}, {"name": "clip", "type": "CLIP", "link": 26}, ], "outputs": [ {"name": "MODEL", "type": "MODEL", "links": [29]}, {"name": "CLIP", "type": "CLIP", "links": [30]}, ], "title": "Load LoRA", "properties": {"Node name for S&R": "LoraLoader"}, "widgets_values": ["8_bikabaka.safetensors", 0.3, 0], }, { "id": 72, "type": "PreviewImage", "pos": [3188.649165895271, 134.76727061509087], "size": [285.77604360195164, 258], "flags": {}, "order": 12, "mode": 0, "inputs": [{"name": "images", "type": "IMAGE", "link": 33}], "outputs": [{"name": "images", "type": "IMAGE", "links": None}], "properties": {"Node name for S&R": "PreviewImage"}, "widgets_values": [], }, { "id": 1, "type": "CheckpointLoaderSimple", "pos": [100, 130], "size": [270, 98], "flags": {}, "order": 2, "mode": 0, "inputs": [], "outputs": [ {"name": "MODEL", "type": "MODEL", "links": [25]}, {"name": "CLIP", "type": "CLIP", "links": [26]}, {"name": "VAE", "type": "VAE", "links": None}, ], "title": "Loader", "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, "widgets_values": ["52_novaAnimeXL_ilV190.safetensors"], }, { "id": 2, "type": "CLIPTextEncode", "pos": [1880.866680908203, 130], "size": [400, 200], "flags": {}, "order": 9, "mode": 0, "inputs": [ {"name": "clip", "type": "CLIP", "link": 49}, { "name": "text", "type": "STRING", "widget": {"name": "text"}, "link": 54, }, ], "outputs": [ {"name": "CONDITIONING", "type": "CONDITIONING", "links": [39]} ], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": [""], }, { "id": 5, "type": "KSampler", "pos": [2578.649165895271, 134.76727061509087], "size": [270, 262], "flags": {}, "order": 10, "mode": 0, "inputs": [ {"name": "model", "type": "MODEL", "link": 50}, {"name": "positive", "type": "CONDITIONING", "link": 39}, {"name": "negative", "type": "CONDITIONING", "link": 23}, {"name": "latent_image", "type": "LATENT", "link": 24}, ], "outputs": [{"name": "LATENT", "type": "LATENT", "links": [31]}], "properties": {"Node name for S&R": "KSampler"}, "widgets_values": [ 809278554234612, "randomize", 16, 4, "euler_ancestral", "karras", 1, ], }, { "id": 38, "type": "LoraLoader", "pos": [990, 130], "size": [290.43334045410154, 126], "flags": {}, "order": 5, "mode": 0, "inputs": [ {"name": "model", "type": "MODEL", "link": 29}, {"name": "clip", "type": "CLIP", "link": 30}, ], "outputs": [ {"name": "MODEL", "type": "MODEL", "links": [27]}, {"name": "CLIP", "type": "CLIP", "links": [28]}, ], "title": "Load LoRA", "properties": {"Node name for S&R": "LoraLoader"}, "widgets_values": ["42_アップスケール_remacri_original.pt", 0.4, 0], }, { "id": 28, "type": "LoraLoader", "pos": [1380, 130], "size": [290.43334045410154, 126], "flags": {}, "order": 6, "mode": 0, "inputs": [ {"name": "model", "type": "MODEL", "link": 27}, {"name": "clip", "type": "CLIP", "link": 28}, ], "outputs": [ {"name": "MODEL", "type": "MODEL", "links": [47]}, {"name": "CLIP", "type": "CLIP", "links": [20, 48]}, ], "title": "Load LoRA", "properties": {"Node name for S&R": "LoraLoader"}, "widgets_values": [ "43_5cm-illustriousXL_v01_V1-CAME-000035.safetensors", 0.4, 0, ], }, { "id": 117, "type": "LoraLoader", "pos": [1523.7427746854546, 341.08039710943746], "size": [290.43334045410154, 126], "flags": {}, "order": 8, "mode": 0, "inputs": [ {"name": "model", "type": "MODEL", "link": 47}, {"name": "clip", "type": "CLIP", "link": 48}, ], "outputs": [ {"name": "MODEL", "type": "MODEL", "links": [50]}, {"name": "CLIP", "type": "CLIP", "links": [49]}, ], "properties": {"Node name for S&R": "LoraLoader"}, "widgets_values": ["5_add_saturation_XL.safetensors", -1.4, 0], }, { "id": 118, "type": "StringConcatenate", "pos": [1363.7030337022063, -256.7007293998441], "size": [400, 200], "flags": {}, "order": 3, "mode": 0, "inputs": [], "outputs": [{"name": "STRING", "type": "STRING", "links": [54]}], "properties": {"Node name for S&R": "StringConcatenate"}, "widgets_values": [ "%prompt%", "", "", ], }, { "id": 3, "type": "CLIPTextEncode", "pos": [1881.5394309031356, 459.32725000506747], "size": [400, 200], "flags": {}, "order": 7, "mode": 0, "inputs": [{"name": "clip", "type": "CLIP", "link": 20}], "outputs": [ {"name": "CONDITIONING", "type": "CONDITIONING", "links": [23]} ], "properties": {"Node name for S&R": "CLIPTextEncode"}, "widgets_values": [ "(censored, mosaic censoring, bar " "censor:1.1), bad quality, worst " "quality, worst detail, bad " "anatomy, extra fingers, extra " "toes, extra legs, 4 toes, 6 toes, " "4 fingers, 6 fingers, malformed " "fingers, extra limbs, missing " "fingers, extra arms, censored, " "deformed, disfigured, text, " "(multiple views:1.1)" ], }, ], "links": [ [20, 28, 1, 3, 0, "CLIP"], [23, 3, 0, 5, 2, "CONDITIONING"], [24, 27, 0, 5, 3, "LATENT"], [25, 1, 0, 10, 0, "MODEL"], [26, 1, 1, 10, 1, "CLIP"], [27, 38, 0, 28, 0, "MODEL"], [28, 38, 1, 28, 1, "CLIP"], [29, 10, 0, 38, 0, "MODEL"], [30, 10, 1, 38, 1, "CLIP"], [31, 5, 0, 56, 0, "LATENT"], [32, 45, 0, 56, 1, "VAE"], [33, 56, 0, 72, 0, "IMAGE"], [39, 2, 0, 5, 1, "CONDITIONING"], [47, 28, 0, 117, 0, "MODEL"], [48, 28, 1, 117, 1, "CLIP"], [49, 117, 1, 2, 0, "CLIP"], [50, 117, 0, 5, 0, "MODEL"], [54, 118, 0, 2, 1, "STRING"], ], "groups": [], "config": {}, "extra": { "ds": { "scale": 0.6303940863128564, "offset": [-602.8886169463092, 486.2591310892753], }, "frontendVersion": "1.45.20", }, "version": 0.4, } } def image_metadata( config, seeds, detailer_seeds, detailer_vaes, vaes, regions, environment_start, global_strength, ): api = {} def add(class_type, inputs): node_id = str(len(api) + 1) api[node_id] = {"inputs": inputs, "class_type": class_type} return node_id style_images = {} style_pipeline = None style_clip_vision = None def style_config(stage): if stage == "second" and config.get("second_style_images"): return ( "second", config["second_style_images"], config["second_style_weight"], config["second_style_end"], ) images = config.get("style_images") scope = config.get("style_scope", "generation") enabled = ( stage == "first" or stage == "second" and scope in ("generation", "all") or stage == "detailer" and scope == "all" ) if images and enabled: return "first", images, config["style_weight"], config["style_end"] return None def apply_style(model, stage): nonlocal style_pipeline, style_clip_vision values = style_config(stage) if values is None: return model key, names, weight, end = values if key not in style_images: style_image = None for name in names: image = [add("LoadImage", {"image": name}), 0] image = [add("ImageScale", { "image": image, "upscale_method": "lanczos", "width": STYLE_IMAGE_SIZE, "height": STYLE_IMAGE_SIZE, "crop": "center", }), 0] if style_image is None: style_image = image else: style_image = [add("ImageBatch", { "image1": style_image, "image2": image, }), 0] style_images[key] = style_image if style_pipeline is None: style_pipeline = [add("IPAdapterModelLoader", { "ipadapter_file": STYLE_IPADAPTER, }), 0] style_clip_vision = [add("CLIPVisionLoader", { "clip_name": STYLE_CLIP_VISION, }), 0] return [add("IPAdapterAdvanced", { "model": model, "ipadapter": style_pipeline, "clip_vision": style_clip_vision, "image": style_images[key], "weight": weight, "weight_type": STYLE_WEIGHT_TYPE, "combine_embeds": "average", "start_at": 0, "end_at": end, "embeds_scaling": STYLE_EMBEDS_SCALING, }), 0] def load_chain(model_name, loras): if is_anima_model(model_name): model = [add("UNETLoader", { "unet_name": model_name, "weight_dtype": "default", }), 0] clip = [add("CLIPLoader", { "clip_name": ANIMA_CLIP, "type": "stable_diffusion", "device": "default", }), 0] else: node_id = add( "CheckpointLoaderSimple", {"ckpt_name": model_name}, ) model = [node_id, 0] clip = [node_id, 1] for lora in loras: node_id = add("LoraLoader", { "lora_name": lora["name"], "strength_model": lora["strength"], "strength_clip": lora["clip"], "model": model, "clip": clip, }) model = [node_id, 0] clip = [node_id, 1] return model, clip def custom_sampler(sampler_name, model): prefix, separator, name = sampler_name.partition(":") if not separator: return None if prefix == "ppm-dyn": return [add("DynSamplerSelect", { "sampler_name": name, "eta": 1, "s_dy_pow": -1, "s_extra_steps": False, }), 0] if prefix == "ppm-cfgpp": return [add("CFGPPSamplerSelect", { "sampler_name": name, "eta": 1, "s_gamma_start": 0, "s_gamma_end": 1, "s_extra_steps": False, }), 0] if prefix == "ppm": return [add("PPMSamplerSelect", { "sampler_name": name, "model": model, "cfg_pp": False, "s_sigma_diff": 2, }), 0] return None def sample( model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent, denoise, ): sampler = custom_sampler(sampler_name, model) if scheduler != ALIGN_SCHEDULER and sampler is None: return [add("KSampler", { "seed": seed, "steps": steps, "cfg": cfg, "sampler_name": sampler_name, "scheduler": scheduler, "denoise": denoise, "model": model, "positive": positive, "negative": negative, "latent_image": latent, }), 0] if scheduler == ALIGN_SCHEDULER: sigmas = [add("AlignYourStepsScheduler", { "model_type": ALIGN_MODEL_TYPE, "steps": steps, "denoise": denoise, }), 0] else: sigmas = [add("BasicScheduler", { "model": model, "scheduler": scheduler, "steps": steps, "denoise": denoise, }), 0] if sampler is None: sampler = [add("KSamplerSelect", { "sampler_name": sampler_name, }), 0] return [add("SamplerCustom", { "model": model, "add_noise": True, "noise_seed": seed, "cfg": cfg, "positive": positive, "negative": negative, "sampler": sampler, "sigmas": sigmas, "latent_image": latent, }), 0] def encode_positive(model, clip, prompt, image_width, image_height): mask_width = image_width // LATENT_SCALE mask_height = image_height // LATENT_SCALE positive = [add("CLIPTextEncode", { "text": prompt, "clip": clip, }), 0] regional_mode = config.get("regional_mode", "conditioning") if regions: positive = [add("ConditioningSetAreaStrength", { "conditioning": positive, "strength": global_strength, }), 0] if regions and regional_mode == "conditioning": positive = [add("ConditioningSetTimestepRange", { "conditioning": positive, "start": environment_start, "end": 1, }), 0] regional_inputs = {} for index, (prompt, x, y, width, height, strength) in enumerate( regions, 1, ): conditioning = [add("CLIPTextEncode", { "text": prompt, "clip": clip, }), 0] x, y, width, height, left, top, right, bottom = mask_box( x, y, width, height, mask_width, mask_height, ) mask = [add("SolidMask", { "value": 1, "width": width, "height": height, }), 0] if any((left, top, right, bottom)): mask = [add("FeatherMask", { "mask": mask, "left": left, "top": top, "right": right, "bottom": bottom, }), 0] background = [add("SolidMask", { "value": 0, "width": mask_width, "height": mask_height, }), 0] mask = [add("MaskComposite", { "destination": background, "source": mask, "x": x, "y": y, "operation": "add", }), 0] if regional_mode == "attention": conditioning = [add("ConditioningSetAreaStrength", { "conditioning": conditioning, "strength": strength, }), 0] regional_inputs[f"cond_{index}"] = conditioning regional_inputs[f"mask_{index}"] = mask else: conditioning = [add("ConditioningSetMask", { "conditioning": conditioning, "mask": mask, "strength": strength, "set_cond_area": "mask bounds", }), 0] positive = [add("ConditioningCombine", { "conditioning_1": positive, "conditioning_2": conditioning, }), 0] if regional_inputs: base_mask = [add("SolidMask", { "value": 1, "width": mask_width, "height": mask_height, }), 0] model = [add("AttentionCouplePPM", { "model": model, "base_cond": positive, "base_mask": base_mask, **regional_inputs, }), 0] return model, positive first_model = config["model"] second_model = config["second_model"] or first_model first_vae, second_vae = vaes base_model, clip = load_chain(first_model, config["loras"]) model = apply_style(base_model, "first") model, positive = encode_positive( model, clip, config["prompt"], config["width"], config["height"], ) negative = [add("CLIPTextEncode", { "text": config["negative"], "clip": clip, }), 0] latent = [add("EmptyLatentImage", { "width": config["width"], "height": config["height"], "batch_size": config["batch_size"], }), 0] samples = sample( model, seeds[0], config["steps"], config["cfg"], config["sampler"], config["scheduler"], positive, negative, latent, 1, ) if config["upscale"]: width, height = upscale_size( config["width"], config["height"], config["upscale_scale"], ) samples = [add("LatentUpscale", { "upscale_method": config["upscale_method"], "width": width, "height": height, "crop": "disabled", "samples": samples, }), 0] if is_anima_model(first_model) != is_anima_model(second_model): source_vae = [add("VAELoader", {"vae_name": first_vae}), 0] image = [add("VAEDecode", { "samples": samples, "vae": source_vae, }), 0] target_vae = [add("VAELoader", {"vae_name": second_vae}), 0] samples = [add("VAEEncode", { "pixels": image, "vae": target_vae, }), 0] if config["second_model"]: base_model, clip = load_chain( config["second_model"], config["second_loras"], ) model = apply_style(base_model, "second") model, positive = encode_positive( model, clip, config.get("second_prompt") or config["prompt"], width, height, ) negative = [add("CLIPTextEncode", { "text": config.get("second_negative") or config["negative"], "clip": clip, }), 0] samples = sample( model, seeds[1], config["second_steps"], config["second_cfg"], config["second_sampler"], config["second_scheduler"], positive, negative, samples, config["denoise"], ) vae = second_vae if config["upscale"] else first_vae vae_node = add("VAELoader", {"vae_name": vae}) image = add("VAEDecode", {"samples": samples, "vae": [vae_node, 0]}) base_clip, base_vae = clip, [vae_node, 0] final_prompt = config.get("second_prompt") or config["prompt"] \ if config["upscale"] else config["prompt"] final_negative = config.get("second_negative") or config["negative"] \ if config["upscale"] else config["negative"] for detailer, seed, detailer_vae in zip( config["detailers"], detailer_seeds, detailer_vaes, ): if detailer["model"]: model, clip = load_chain(detailer["model"], []) vae = [add("VAELoader", { "vae_name": detailer_vae, }), 0] prompt, negative_prompt = config["prompt"], config["negative"] else: model, clip, vae = base_model, base_clip, base_vae prompt, negative_prompt = final_prompt, final_negative model = apply_style(model, "detailer") positive = [add("CLIPTextEncode", { "text": detailer["prompt"] or prompt, "clip": clip, }), 0] negative = [add("CLIPTextEncode", { "text": detailer["negative"] or negative_prompt, "clip": clip, }), 0] detector = add("UltralyticsDetectorProvider", { "model_name": f"bbox/{detailer['detector']}", }) image = add("FaceDetailer", { "image": [image, 0], "model": model, "clip": clip, "vae": vae, "guide_size": DETAILER_GUIDE_SIZE, "guide_size_for": True, "max_size": DETAILER_MAX_SIZE, "seed": seed, "steps": detailer["steps"], "cfg": detailer["cfg"], "sampler_name": detailer["sampler"], "scheduler": detailer["scheduler"], "positive": positive, "negative": negative, "denoise": detailer["denoise"], "feather": DETAILER_FEATHER, "noise_mask": True, "force_inpaint": True, "bbox_threshold": DETAILER_THRESHOLD, "bbox_dilation": DETAILER_DILATION, "bbox_crop_factor": DETAILER_CROP, "sam_detection_hint": "none", "sam_dilation": 0, "sam_threshold": .93, "sam_bbox_expansion": 0, "sam_mask_hint_threshold": .7, "sam_mask_hint_use_negative": "False", "drop_size": DETAILER_DROP_SIZE, "bbox_detector": [detector, 0], "wildcard": "", "cycle": 1, }) if config["upscale"] and config["upscale_model"]: upscale_model = [add("UpscaleModelLoader", { "model_name": config["upscale_model"], }), 0] image = add("ImageUpscaleWithModel", { "upscale_model": upscale_model, "image": [image, 0], }) add("PreviewImage", {"images": [image, 0]}) return { "prompt": json.dumps(api, separators=(",", ":")), "parameters": json.dumps(config, separators=(",", ":")), } workflow = build_workflow() prompt = json.loads(json.dumps(workflow)) extra_pnginfo = build_extra_pnginfo() # Workflow execution def main(unload_models: bool | None = None): bootstrap_comfyui_runtime() add_extra_model_paths() import_custom_nodes() # Node imports from nodes import ( CLIPTextEncode, CheckpointLoaderSimple, EmptyLatentImage, KSampler, LoraLoader, NODE_CLASS_MAPPINGS, VAEDecode, VAELoader, ) import torch try: with torch.inference_mode(): checkpointloadersimple = CheckpointLoaderSimple() checkpointloadersimple_1 = checkpointloadersimple.load_checkpoint( ckpt_name="52_novaAnimeXL_ilV190.safetensors" ) stringconcatenate = NODE_CLASS_MAPPINGS["StringConcatenate"]() stringconcatenate_118 = stringconcatenate.EXECUTE_NORMALIZED( string_a="%prompt%", string_b="", delimiter="", ) loraloader = LoraLoader() loraloader_10 = loraloader.load_lora( lora_name="8_bikabaka.safetensors", strength_model=0.3, strength_clip=0, model=get_value_at_index(checkpointloadersimple_1, 0), clip=get_value_at_index(checkpointloadersimple_1, 1), ) loraloader_38 = loraloader.load_lora( lora_name="42_\u30a2\u30c3\u30d7\u30b9\u30b1\u30fc\u30eb_remacri_original.pt", strength_model=0.4, strength_clip=0, model=get_value_at_index(loraloader_10, 0), clip=get_value_at_index(loraloader_10, 1), ) loraloader_28 = loraloader.load_lora( lora_name="43_5cm-illustriousXL_v01_V1-CAME-000035.safetensors", strength_model=0.4, strength_clip=0, model=get_value_at_index(loraloader_38, 0), clip=get_value_at_index(loraloader_38, 1), ) loraloader_117 = loraloader.load_lora( lora_name="5_add_saturation_XL.safetensors", strength_model=-1.4, strength_clip=0, model=get_value_at_index(loraloader_28, 0), clip=get_value_at_index(loraloader_28, 1), ) cliptextencode = CLIPTextEncode() cliptextencode_2 = cliptextencode.encode( text=get_value_at_index(stringconcatenate_118, 0), clip=get_value_at_index(loraloader_117, 1), ) cliptextencode_3 = cliptextencode.encode( text="(censored, mosaic censoring, bar censor:1.1), bad quality, worst quality, worst detail, bad anatomy, extra fingers, extra toes, extra legs, 4 toes, 6 toes, 4 fingers, 6 fingers, malformed fingers, extra limbs, missing fingers, extra arms, censored, deformed, disfigured, text, (multiple views:1.1)", clip=get_value_at_index(loraloader_28, 1), ) emptylatentimage = EmptyLatentImage() emptylatentimage_27 = emptylatentimage.generate( width=1152, height=896, batch_size=1 ) vaeloader = VAELoader() vaeloader_45 = vaeloader.load_vae(vae_name="3_sdxlVAE_sdxlVAE.safetensors") ksampler = KSampler() vaedecode = VAEDecode() for q in range(1): node_5_seed = prompt["5"]["inputs"]["seed"] = GENERATION ksampler_5 = ksampler.sample( seed=node_5_seed, steps=16, cfg=4, sampler_name="euler_ancestral", scheduler="karras", denoise=1, model=get_value_at_index(loraloader_117, 0), positive=get_value_at_index(cliptextencode_2, 0), negative=get_value_at_index(cliptextencode_3, 0), latent_image=get_value_at_index(emptylatentimage_27, 0), ) vaedecode_56 = vaedecode.decode( samples=get_value_at_index(ksampler_5, 0), vae=get_value_at_index(vaeloader_45, 0), ) finally: cleanup_comfyui_runtime(unload_models=unload_models) # Entrypoint if __name__ == "__main__": main()