CoolFace
Apppublic

fred-dev/comfy_ui_ali

sourceHugging Facemitupdated 1y agoView on Hugging Face
0likes
nodes.py2297 linesDownload Raw Back to root
1from __future__ import annotations2import torch3 4import os5import sys6import json7import hashlib8import traceback9import math10import time11import random12import logging13 14from PIL import Image, ImageOps, ImageSequence15from PIL.PngImagePlugin import PngInfo16 17import numpy as np18import safetensors.torch19 20sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))21 22import comfy.diffusers_load23import comfy.samplers24import comfy.sample25import comfy.sd26import comfy.utils27import comfy.controlnet28from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict, FileLocator29 30import comfy.clip_vision31 32import comfy.model_management33from comfy.cli_args import args34 35import importlib36 37import folder_paths38import latent_preview39import node_helpers40 41def before_node_execution():42    comfy.model_management.throw_exception_if_processing_interrupted()43 44def interrupt_processing(value=True):45    comfy.model_management.interrupt_current_processing(value)46 47MAX_RESOLUTION=1638448 49class CLIPTextEncode(ComfyNodeABC):50    @classmethod51    def INPUT_TYPES(s) -> InputTypeDict:52        return {53            "required": {54                "text": (IO.STRING, {"multiline": True, "dynamicPrompts": True, "tooltip": "The text to be encoded."}),55                "clip": (IO.CLIP, {"tooltip": "The CLIP model used for encoding the text."})56            }57        }58    RETURN_TYPES = (IO.CONDITIONING,)59    OUTPUT_TOOLTIPS = ("A conditioning containing the embedded text used to guide the diffusion model.",)60    FUNCTION = "encode"61 62    CATEGORY = "conditioning"63    DESCRIPTION = "Encodes a text prompt using a CLIP model into an embedding that can be used to guide the diffusion model towards generating specific images."64 65    def encode(self, clip, text):66        if clip is None:67            raise RuntimeError("ERROR: clip input is invalid: None\n\nIf the clip is from a checkpoint loader node your checkpoint does not contain a valid clip or text encoder model.")68        tokens = clip.tokenize(text)69        return (clip.encode_from_tokens_scheduled(tokens), )70 71 72class ConditioningCombine:73    @classmethod74    def INPUT_TYPES(s):75        return {"required": {"conditioning_1": ("CONDITIONING", ), "conditioning_2": ("CONDITIONING", )}}76    RETURN_TYPES = ("CONDITIONING",)77    FUNCTION = "combine"78 79    CATEGORY = "conditioning"80 81    def combine(self, conditioning_1, conditioning_2):82        return (conditioning_1 + conditioning_2, )83 84class ConditioningAverage :85    @classmethod86    def INPUT_TYPES(s):87        return {"required": {"conditioning_to": ("CONDITIONING", ), "conditioning_from": ("CONDITIONING", ),88                              "conditioning_to_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})89                             }}90    RETURN_TYPES = ("CONDITIONING",)91    FUNCTION = "addWeighted"92 93    CATEGORY = "conditioning"94 95    def addWeighted(self, conditioning_to, conditioning_from, conditioning_to_strength):96        out = []97 98        if len(conditioning_from) > 1:99            logging.warning("Warning: ConditioningAverage conditioning_from contains more than 1 cond, only the first one will actually be applied to conditioning_to.")100 101        cond_from = conditioning_from[0][0]102        pooled_output_from = conditioning_from[0][1].get("pooled_output", None)103 104        for i in range(len(conditioning_to)):105            t1 = conditioning_to[i][0]106            pooled_output_to = conditioning_to[i][1].get("pooled_output", pooled_output_from)107            t0 = cond_from[:,:t1.shape[1]]108            if t0.shape[1] < t1.shape[1]:109                t0 = torch.cat([t0] + [torch.zeros((1, (t1.shape[1] - t0.shape[1]), t1.shape[2]))], dim=1)110 111            tw = torch.mul(t1, conditioning_to_strength) + torch.mul(t0, (1.0 - conditioning_to_strength))112            t_to = conditioning_to[i][1].copy()113            if pooled_output_from is not None and pooled_output_to is not None:114                t_to["pooled_output"] = torch.mul(pooled_output_to, conditioning_to_strength) + torch.mul(pooled_output_from, (1.0 - conditioning_to_strength))115            elif pooled_output_from is not None:116                t_to["pooled_output"] = pooled_output_from117 118            n = [tw, t_to]119            out.append(n)120        return (out, )121 122class ConditioningConcat:123    @classmethod124    def INPUT_TYPES(s):125        return {"required": {126            "conditioning_to": ("CONDITIONING",),127            "conditioning_from": ("CONDITIONING",),128            }}129    RETURN_TYPES = ("CONDITIONING",)130    FUNCTION = "concat"131 132    CATEGORY = "conditioning"133 134    def concat(self, conditioning_to, conditioning_from):135        out = []136 137        if len(conditioning_from) > 1:138            logging.warning("Warning: ConditioningConcat conditioning_from contains more than 1 cond, only the first one will actually be applied to conditioning_to.")139 140        cond_from = conditioning_from[0][0]141 142        for i in range(len(conditioning_to)):143            t1 = conditioning_to[i][0]144            tw = torch.cat((t1, cond_from),1)145            n = [tw, conditioning_to[i][1].copy()]146            out.append(n)147 148        return (out, )149 150class ConditioningSetArea:151    @classmethod152    def INPUT_TYPES(s):153        return {"required": {"conditioning": ("CONDITIONING", ),154                              "width": ("INT", {"default": 64, "min": 64, "max": MAX_RESOLUTION, "step": 8}),155                              "height": ("INT", {"default": 64, "min": 64, "max": MAX_RESOLUTION, "step": 8}),156                              "x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}),157                              "y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}),158                              "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),159                             }}160    RETURN_TYPES = ("CONDITIONING",)161    FUNCTION = "append"162 163    CATEGORY = "conditioning"164 165    def append(self, conditioning, width, height, x, y, strength):166        c = node_helpers.conditioning_set_values(conditioning, {"area": (height // 8, width // 8, y // 8, x // 8),167                                                                "strength": strength,168                                                                "set_area_to_bounds": False})169        return (c, )170 171class ConditioningSetAreaPercentage:172    @classmethod173    def INPUT_TYPES(s):174        return {"required": {"conditioning": ("CONDITIONING", ),175                              "width": ("FLOAT", {"default": 1.0, "min": 0, "max": 1.0, "step": 0.01}),176                              "height": ("FLOAT", {"default": 1.0, "min": 0, "max": 1.0, "step": 0.01}),177                              "x": ("FLOAT", {"default": 0, "min": 0, "max": 1.0, "step": 0.01}),178                              "y": ("FLOAT", {"default": 0, "min": 0, "max": 1.0, "step": 0.01}),179                              "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),180                             }}181    RETURN_TYPES = ("CONDITIONING",)182    FUNCTION = "append"183 184    CATEGORY = "conditioning"185 186    def append(self, conditioning, width, height, x, y, strength):187        c = node_helpers.conditioning_set_values(conditioning, {"area": ("percentage", height, width, y, x),188                                                                "strength": strength,189                                                                "set_area_to_bounds": False})190        return (c, )191 192class ConditioningSetAreaStrength:193    @classmethod194    def INPUT_TYPES(s):195        return {"required": {"conditioning": ("CONDITIONING", ),196                              "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),197                             }}198    RETURN_TYPES = ("CONDITIONING",)199    FUNCTION = "append"200 201    CATEGORY = "conditioning"202 203    def append(self, conditioning, strength):204        c = node_helpers.conditioning_set_values(conditioning, {"strength": strength})205        return (c, )206 207 208class ConditioningSetMask:209    @classmethod210    def INPUT_TYPES(s):211        return {"required": {"conditioning": ("CONDITIONING", ),212                              "mask": ("MASK", ),213                              "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),214                              "set_cond_area": (["default", "mask bounds"],),215                             }}216    RETURN_TYPES = ("CONDITIONING",)217    FUNCTION = "append"218 219    CATEGORY = "conditioning"220 221    def append(self, conditioning, mask, set_cond_area, strength):222        set_area_to_bounds = False223        if set_cond_area != "default":224            set_area_to_bounds = True225        if len(mask.shape) < 3:226            mask = mask.unsqueeze(0)227 228        c = node_helpers.conditioning_set_values(conditioning, {"mask": mask,229                                                                "set_area_to_bounds": set_area_to_bounds,230                                                                "mask_strength": strength})231        return (c, )232 233class ConditioningZeroOut:234    @classmethod235    def INPUT_TYPES(s):236        return {"required": {"conditioning": ("CONDITIONING", )}}237    RETURN_TYPES = ("CONDITIONING",)238    FUNCTION = "zero_out"239 240    CATEGORY = "advanced/conditioning"241 242    def zero_out(self, conditioning):243        c = []244        for t in conditioning:245            d = t[1].copy()246            pooled_output = d.get("pooled_output", None)247            if pooled_output is not None:248                d["pooled_output"] = torch.zeros_like(pooled_output)249            n = [torch.zeros_like(t[0]), d]250            c.append(n)251        return (c, )252 253class ConditioningSetTimestepRange:254    @classmethod255    def INPUT_TYPES(s):256        return {"required": {"conditioning": ("CONDITIONING", ),257                             "start": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),258                             "end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001})259                             }}260    RETURN_TYPES = ("CONDITIONING",)261    FUNCTION = "set_range"262 263    CATEGORY = "advanced/conditioning"264 265    def set_range(self, conditioning, start, end):266        c = node_helpers.conditioning_set_values(conditioning, {"start_percent": start,267                                                                "end_percent": end})268        return (c, )269 270class VAEDecode:271    @classmethod272    def INPUT_TYPES(s):273        return {274            "required": {275                "samples": ("LATENT", {"tooltip": "The latent to be decoded."}),276                "vae": ("VAE", {"tooltip": "The VAE model used for decoding the latent."})277            }278        }279    RETURN_TYPES = ("IMAGE",)280    OUTPUT_TOOLTIPS = ("The decoded image.",)281    FUNCTION = "decode"282 283    CATEGORY = "latent"284    DESCRIPTION = "Decodes latent images back into pixel space images."285 286    def decode(self, vae, samples):287        images = vae.decode(samples["samples"])288        if len(images.shape) == 5: #Combine batches289            images = images.reshape(-1, images.shape[-3], images.shape[-2], images.shape[-1])290        return (images, )291 292class VAEDecodeTiled:293    @classmethod294    def INPUT_TYPES(s):295        return {"required": {"samples": ("LATENT", ), "vae": ("VAE", ),296                             "tile_size": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 32}),297                             "overlap": ("INT", {"default": 64, "min": 0, "max": 4096, "step": 32}),298                             "temporal_size": ("INT", {"default": 64, "min": 8, "max": 4096, "step": 4, "tooltip": "Only used for video VAEs: Amount of frames to decode at a time."}),299                             "temporal_overlap": ("INT", {"default": 8, "min": 4, "max": 4096, "step": 4, "tooltip": "Only used for video VAEs: Amount of frames to overlap."}),300                            }}301    RETURN_TYPES = ("IMAGE",)302    FUNCTION = "decode"303 304    CATEGORY = "_for_testing"305 306    def decode(self, vae, samples, tile_size, overlap=64, temporal_size=64, temporal_overlap=8):307        if tile_size < overlap * 4:308            overlap = tile_size // 4309        if temporal_size < temporal_overlap * 2:310            temporal_overlap = temporal_overlap // 2311        temporal_compression = vae.temporal_compression_decode()312        if temporal_compression is not None:313            temporal_size = max(2, temporal_size // temporal_compression)314            temporal_overlap = max(1, min(temporal_size // 2, temporal_overlap // temporal_compression))315        else:316            temporal_size = None317            temporal_overlap = None318 319        compression = vae.spacial_compression_decode()320        images = vae.decode_tiled(samples["samples"], tile_x=tile_size // compression, tile_y=tile_size // compression, overlap=overlap // compression, tile_t=temporal_size, overlap_t=temporal_overlap)321        if len(images.shape) == 5: #Combine batches322            images = images.reshape(-1, images.shape[-3], images.shape[-2], images.shape[-1])323        return (images, )324 325class VAEEncode:326    @classmethod327    def INPUT_TYPES(s):328        return {"required": { "pixels": ("IMAGE", ), "vae": ("VAE", )}}329    RETURN_TYPES = ("LATENT",)330    FUNCTION = "encode"331 332    CATEGORY = "latent"333 334    def encode(self, vae, pixels):335        t = vae.encode(pixels[:,:,:,:3])336        return ({"samples":t}, )337 338class VAEEncodeTiled:339    @classmethod340    def INPUT_TYPES(s):341        return {"required": {"pixels": ("IMAGE", ), "vae": ("VAE", ),342                             "tile_size": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64}),343                             "overlap": ("INT", {"default": 64, "min": 0, "max": 4096, "step": 32}),344                             "temporal_size": ("INT", {"default": 64, "min": 8, "max": 4096, "step": 4, "tooltip": "Only used for video VAEs: Amount of frames to encode at a time."}),345                             "temporal_overlap": ("INT", {"default": 8, "min": 4, "max": 4096, "step": 4, "tooltip": "Only used for video VAEs: Amount of frames to overlap."}),346                            }}347    RETURN_TYPES = ("LATENT",)348    FUNCTION = "encode"349 350    CATEGORY = "_for_testing"351 352    def encode(self, vae, pixels, tile_size, overlap, temporal_size=64, temporal_overlap=8):353        t = vae.encode_tiled(pixels[:,:,:,:3], tile_x=tile_size, tile_y=tile_size, overlap=overlap, tile_t=temporal_size, overlap_t=temporal_overlap)354        return ({"samples": t}, )355 356class VAEEncodeForInpaint:357    @classmethod358    def INPUT_TYPES(s):359        return {"required": { "pixels": ("IMAGE", ), "vae": ("VAE", ), "mask": ("MASK", ), "grow_mask_by": ("INT", {"default": 6, "min": 0, "max": 64, "step": 1}),}}360    RETURN_TYPES = ("LATENT",)361    FUNCTION = "encode"362 363    CATEGORY = "latent/inpaint"364 365    def encode(self, vae, pixels, mask, grow_mask_by=6):366        x = (pixels.shape[1] // vae.downscale_ratio) * vae.downscale_ratio367        y = (pixels.shape[2] // vae.downscale_ratio) * vae.downscale_ratio368        mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(pixels.shape[1], pixels.shape[2]), mode="bilinear")369 370        pixels = pixels.clone()371        if pixels.shape[1] != x or pixels.shape[2] != y:372            x_offset = (pixels.shape[1] % vae.downscale_ratio) // 2373            y_offset = (pixels.shape[2] % vae.downscale_ratio) // 2374            pixels = pixels[:,x_offset:x + x_offset, y_offset:y + y_offset,:]375            mask = mask[:,:,x_offset:x + x_offset, y_offset:y + y_offset]376 377        #grow mask by a few pixels to keep things seamless in latent space378        if grow_mask_by == 0:379            mask_erosion = mask380        else:381            kernel_tensor = torch.ones((1, 1, grow_mask_by, grow_mask_by))382            padding = math.ceil((grow_mask_by - 1) / 2)383 384            mask_erosion = torch.clamp(torch.nn.functional.conv2d(mask.round(), kernel_tensor, padding=padding), 0, 1)385 386        m = (1.0 - mask.round()).squeeze(1)387        for i in range(3):388            pixels[:,:,:,i] -= 0.5389            pixels[:,:,:,i] *= m390            pixels[:,:,:,i] += 0.5391        t = vae.encode(pixels)392 393        return ({"samples":t, "noise_mask": (mask_erosion[:,:,:x,:y].round())}, )394 395 396class InpaintModelConditioning:397    @classmethod398    def INPUT_TYPES(s):399        return {"required": {"positive": ("CONDITIONING", ),400                             "negative": ("CONDITIONING", ),401                             "vae": ("VAE", ),402                             "pixels": ("IMAGE", ),403                             "mask": ("MASK", ),404                             "noise_mask": ("BOOLEAN", {"default": True, "tooltip": "Add a noise mask to the latent so sampling will only happen within the mask. Might improve results or completely break things depending on the model."}),405                             }}406 407    RETURN_TYPES = ("CONDITIONING","CONDITIONING","LATENT")408    RETURN_NAMES = ("positive", "negative", "latent")409    FUNCTION = "encode"410 411    CATEGORY = "conditioning/inpaint"412 413    def encode(self, positive, negative, pixels, vae, mask, noise_mask=True):414        x = (pixels.shape[1] // 8) * 8415        y = (pixels.shape[2] // 8) * 8416        mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(pixels.shape[1], pixels.shape[2]), mode="bilinear")417 418        orig_pixels = pixels419        pixels = orig_pixels.clone()420        if pixels.shape[1] != x or pixels.shape[2] != y:421            x_offset = (pixels.shape[1] % 8) // 2422            y_offset = (pixels.shape[2] % 8) // 2423            pixels = pixels[:,x_offset:x + x_offset, y_offset:y + y_offset,:]424            mask = mask[:,:,x_offset:x + x_offset, y_offset:y + y_offset]425 426        m = (1.0 - mask.round()).squeeze(1)427        for i in range(3):428            pixels[:,:,:,i] -= 0.5429            pixels[:,:,:,i] *= m430            pixels[:,:,:,i] += 0.5431        concat_latent = vae.encode(pixels)432        orig_latent = vae.encode(orig_pixels)433 434        out_latent = {}435 436        out_latent["samples"] = orig_latent437        if noise_mask:438            out_latent["noise_mask"] = mask439 440        out = []441        for conditioning in [positive, negative]:442            c = node_helpers.conditioning_set_values(conditioning, {"concat_latent_image": concat_latent,443                                                                    "concat_mask": mask})444            out.append(c)445        return (out[0], out[1], out_latent)446 447 448class SaveLatent:449    def __init__(self):450        self.output_dir = folder_paths.get_output_directory()451 452    @classmethod453    def INPUT_TYPES(s):454        return {"required": { "samples": ("LATENT", ),455                              "filename_prefix": ("STRING", {"default": "latents/ComfyUI"})},456                "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},457                }458    RETURN_TYPES = ()459    FUNCTION = "save"460 461    OUTPUT_NODE = True462 463    CATEGORY = "_for_testing"464 465    def save(self, samples, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):466        full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)467 468        # support save metadata for latent sharing469        prompt_info = ""470        if prompt is not None:471            prompt_info = json.dumps(prompt)472 473        metadata = None474        if not args.disable_metadata:475            metadata = {"prompt": prompt_info}476            if extra_pnginfo is not None:477                for x in extra_pnginfo:478                    metadata[x] = json.dumps(extra_pnginfo[x])479 480        file = f"{filename}_{counter:05}_.latent"481 482        results: list[FileLocator] = []483        results.append({484            "filename": file,485            "subfolder": subfolder,486            "type": "output"487        })488 489        file = os.path.join(full_output_folder, file)490 491        output = {}492        output["latent_tensor"] = samples["samples"].contiguous()493        output["latent_format_version_0"] = torch.tensor([])494 495        comfy.utils.save_torch_file(output, file, metadata=metadata)496        return { "ui": { "latents": results } }497 498 499class LoadLatent:500    @classmethod501    def INPUT_TYPES(s):502        input_dir = folder_paths.get_input_directory()503        files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f)) and f.endswith(".latent")]504        return {"required": {"latent": [sorted(files), ]}, }505 506    CATEGORY = "_for_testing"507 508    RETURN_TYPES = ("LATENT", )509    FUNCTION = "load"510 511    def load(self, latent):512        latent_path = folder_paths.get_annotated_filepath(latent)513        latent = safetensors.torch.load_file(latent_path, device="cpu")514        multiplier = 1.0515        if "latent_format_version_0" not in latent:516            multiplier = 1.0 / 0.18215517        samples = {"samples": latent["latent_tensor"].float() * multiplier}518        return (samples, )519 520    @classmethod521    def IS_CHANGED(s, latent):522        image_path = folder_paths.get_annotated_filepath(latent)523        m = hashlib.sha256()524        with open(image_path, 'rb') as f:525            m.update(f.read())526        return m.digest().hex()527 528    @classmethod529    def VALIDATE_INPUTS(s, latent):530        if not folder_paths.exists_annotated_filepath(latent):531            return "Invalid latent file: {}".format(latent)532        return True533 534 535class CheckpointLoader:536    @classmethod537    def INPUT_TYPES(s):538        return {"required": { "config_name": (folder_paths.get_filename_list("configs"), ),539                              "ckpt_name": (folder_paths.get_filename_list("checkpoints"), )}}540    RETURN_TYPES = ("MODEL", "CLIP", "VAE")541    FUNCTION = "load_checkpoint"542 543    CATEGORY = "advanced/loaders"544    DEPRECATED = True545 546    def load_checkpoint(self, config_name, ckpt_name):547        config_path = folder_paths.get_full_path("configs", config_name)548        ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name)549        return comfy.sd.load_checkpoint(config_path, ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings"))550 551class CheckpointLoaderSimple:552    @classmethod553    def INPUT_TYPES(s):554        return {555            "required": {556                "ckpt_name": (folder_paths.get_filename_list("checkpoints"), {"tooltip": "The name of the checkpoint (model) to load."}),557            }558        }559    RETURN_TYPES = ("MODEL", "CLIP", "VAE")560    OUTPUT_TOOLTIPS = ("The model used for denoising latents.",561                       "The CLIP model used for encoding text prompts.",562                       "The VAE model used for encoding and decoding images to and from latent space.")563    FUNCTION = "load_checkpoint"564 565    CATEGORY = "loaders"566    DESCRIPTION = "Loads a diffusion model checkpoint, diffusion models are used to denoise latents."567 568    def load_checkpoint(self, ckpt_name):569        ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name)570        out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings"))571        return out[:3]572 573class DiffusersLoader:574    @classmethod575    def INPUT_TYPES(cls):576        paths = []577        for search_path in folder_paths.get_folder_paths("diffusers"):578            if os.path.exists(search_path):579                for root, subdir, files in os.walk(search_path, followlinks=True):580                    if "model_index.json" in files:581                        paths.append(os.path.relpath(root, start=search_path))582 583        return {"required": {"model_path": (paths,), }}584    RETURN_TYPES = ("MODEL", "CLIP", "VAE")585    FUNCTION = "load_checkpoint"586 587    CATEGORY = "advanced/loaders/deprecated"588 589    def load_checkpoint(self, model_path, output_vae=True, output_clip=True):590        for search_path in folder_paths.get_folder_paths("diffusers"):591            if os.path.exists(search_path):592                path = os.path.join(search_path, model_path)593                if os.path.exists(path):594                    model_path = path595                    break596 597        return comfy.diffusers_load.load_diffusers(model_path, output_vae=output_vae, output_clip=output_clip, embedding_directory=folder_paths.get_folder_paths("embeddings"))598 599 600class unCLIPCheckpointLoader:601    @classmethod602    def INPUT_TYPES(s):603        return {"required": { "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ),604                             }}605    RETURN_TYPES = ("MODEL", "CLIP", "VAE", "CLIP_VISION")606    FUNCTION = "load_checkpoint"607 608    CATEGORY = "loaders"609 610    def load_checkpoint(self, ckpt_name, output_vae=True, output_clip=True):611        ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name)612        out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, output_clipvision=True, embedding_directory=folder_paths.get_folder_paths("embeddings"))613        return out614 615class CLIPSetLastLayer:616    @classmethod617    def INPUT_TYPES(s):618        return {"required": { "clip": ("CLIP", ),619                              "stop_at_clip_layer": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}),620                              }}621    RETURN_TYPES = ("CLIP",)622    FUNCTION = "set_last_layer"623 624    CATEGORY = "conditioning"625 626    def set_last_layer(self, clip, stop_at_clip_layer):627        clip = clip.clone()628        clip.clip_layer(stop_at_clip_layer)629        return (clip,)630 631class LoraLoader:632    def __init__(self):633        self.loaded_lora = None634 635    @classmethod636    def INPUT_TYPES(s):637        return {638            "required": {639                "model": ("MODEL", {"tooltip": "The diffusion model the LoRA will be applied to."}),640                "clip": ("CLIP", {"tooltip": "The CLIP model the LoRA will be applied to."}),641                "lora_name": (folder_paths.get_filename_list("loras"), {"tooltip": "The name of the LoRA."}),642                "strength_model": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01, "tooltip": "How strongly to modify the diffusion model. This value can be negative."}),643                "strength_clip": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01, "tooltip": "How strongly to modify the CLIP model. This value can be negative."}),644            }645        }646 647    RETURN_TYPES = ("MODEL", "CLIP")648    OUTPUT_TOOLTIPS = ("The modified diffusion model.", "The modified CLIP model.")649    FUNCTION = "load_lora"650 651    CATEGORY = "loaders"652    DESCRIPTION = "LoRAs are used to modify diffusion and CLIP models, altering the way in which latents are denoised such as applying styles. Multiple LoRA nodes can be linked together."653 654    def load_lora(self, model, clip, lora_name, strength_model, strength_clip):655        if strength_model == 0 and strength_clip == 0:656            return (model, clip)657 658        lora_path = folder_paths.get_full_path_or_raise("loras", lora_name)659        lora = None660        if self.loaded_lora is not None:661            if self.loaded_lora[0] == lora_path:662                lora = self.loaded_lora[1]663            else:664                self.loaded_lora = None665 666        if lora is None:667            lora = comfy.utils.load_torch_file(lora_path, safe_load=True)668            self.loaded_lora = (lora_path, lora)669 670        model_lora, clip_lora = comfy.sd.load_lora_for_models(model, clip, lora, strength_model, strength_clip)671        return (model_lora, clip_lora)672 673class LoraLoaderModelOnly(LoraLoader):674    @classmethod675    def INPUT_TYPES(s):676        return {"required": { "model": ("MODEL",),677                              "lora_name": (folder_paths.get_filename_list("loras"), ),678                              "strength_model": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}),679                              }}680    RETURN_TYPES = ("MODEL",)681    FUNCTION = "load_lora_model_only"682 683    def load_lora_model_only(self, model, lora_name, strength_model):684        return (self.load_lora(model, None, lora_name, strength_model, 0)[0],)685 686class VAELoader:687    @staticmethod688    def vae_list():689        vaes = folder_paths.get_filename_list("vae")690        approx_vaes = folder_paths.get_filename_list("vae_approx")691        sdxl_taesd_enc = False692        sdxl_taesd_dec = False693        sd1_taesd_enc = False694        sd1_taesd_dec = False695        sd3_taesd_enc = False696        sd3_taesd_dec = False697        f1_taesd_enc = False698        f1_taesd_dec = False699 700        for v in approx_vaes:701            if v.startswith("taesd_decoder."):702                sd1_taesd_dec = True703            elif v.startswith("taesd_encoder."):704                sd1_taesd_enc = True705            elif v.startswith("taesdxl_decoder."):706                sdxl_taesd_dec = True707            elif v.startswith("taesdxl_encoder."):708                sdxl_taesd_enc = True709            elif v.startswith("taesd3_decoder."):710                sd3_taesd_dec = True711            elif v.startswith("taesd3_encoder."):712                sd3_taesd_enc = True713            elif v.startswith("taef1_encoder."):714                f1_taesd_dec = True715            elif v.startswith("taef1_decoder."):716                f1_taesd_enc = True717        if sd1_taesd_dec and sd1_taesd_enc:718            vaes.append("taesd")719        if sdxl_taesd_dec and sdxl_taesd_enc:720            vaes.append("taesdxl")721        if sd3_taesd_dec and sd3_taesd_enc:722            vaes.append("taesd3")723        if f1_taesd_dec and f1_taesd_enc:724            vaes.append("taef1")725        return vaes726 727    @staticmethod728    def load_taesd(name):729        sd = {}730        approx_vaes = folder_paths.get_filename_list("vae_approx")731 732        encoder = next(filter(lambda a: a.startswith("{}_encoder.".format(name)), approx_vaes))733        decoder = next(filter(lambda a: a.startswith("{}_decoder.".format(name)), approx_vaes))734 735        enc = comfy.utils.load_torch_file(folder_paths.get_full_path_or_raise("vae_approx", encoder))736        for k in enc:737            sd["taesd_encoder.{}".format(k)] = enc[k]738 739        dec = comfy.utils.load_torch_file(folder_paths.get_full_path_or_raise("vae_approx", decoder))740        for k in dec:741            sd["taesd_decoder.{}".format(k)] = dec[k]742 743        if name == "taesd":744            sd["vae_scale"] = torch.tensor(0.18215)745            sd["vae_shift"] = torch.tensor(0.0)746        elif name == "taesdxl":747            sd["vae_scale"] = torch.tensor(0.13025)748            sd["vae_shift"] = torch.tensor(0.0)749        elif name == "taesd3":750            sd["vae_scale"] = torch.tensor(1.5305)751            sd["vae_shift"] = torch.tensor(0.0609)752        elif name == "taef1":753            sd["vae_scale"] = torch.tensor(0.3611)754            sd["vae_shift"] = torch.tensor(0.1159)755        return sd756 757    @classmethod758    def INPUT_TYPES(s):759        return {"required": { "vae_name": (s.vae_list(), )}}760    RETURN_TYPES = ("VAE",)761    FUNCTION = "load_vae"762 763    CATEGORY = "loaders"764 765    #TODO: scale factor?766    def load_vae(self, vae_name):767        if vae_name in ["taesd", "taesdxl", "taesd3", "taef1"]:768            sd = self.load_taesd(vae_name)769        else:770            vae_path = folder_paths.get_full_path_or_raise("vae", vae_name)771            sd = comfy.utils.load_torch_file(vae_path)772        vae = comfy.sd.VAE(sd=sd)773        vae.throw_exception_if_invalid()774        return (vae,)775 776class ControlNetLoader:777    @classmethod778    def INPUT_TYPES(s):779        return {"required": { "control_net_name": (folder_paths.get_filename_list("controlnet"), )}}780 781    RETURN_TYPES = ("CONTROL_NET",)782    FUNCTION = "load_controlnet"783 784    CATEGORY = "loaders"785 786    def load_controlnet(self, control_net_name):787        controlnet_path = folder_paths.get_full_path_or_raise("controlnet", control_net_name)788        controlnet = comfy.controlnet.load_controlnet(controlnet_path)789        return (controlnet,)790 791class DiffControlNetLoader:792    @classmethod793    def INPUT_TYPES(s):794        return {"required": { "model": ("MODEL",),795                              "control_net_name": (folder_paths.get_filename_list("controlnet"), )}}796 797    RETURN_TYPES = ("CONTROL_NET",)798    FUNCTION = "load_controlnet"799 800    CATEGORY = "loaders"801 802    def load_controlnet(self, model, control_net_name):803        controlnet_path = folder_paths.get_full_path_or_raise("controlnet", control_net_name)804        controlnet = comfy.controlnet.load_controlnet(controlnet_path, model)805        return (controlnet,)806 807 808class ControlNetApply:809    @classmethod810    def INPUT_TYPES(s):811        return {"required": {"conditioning": ("CONDITIONING", ),812                             "control_net": ("CONTROL_NET", ),813                             "image": ("IMAGE", ),814                             "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01})815                             }}816    RETURN_TYPES = ("CONDITIONING",)817    FUNCTION = "apply_controlnet"818 819    DEPRECATED = True820    CATEGORY = "conditioning/controlnet"821 822    def apply_controlnet(self, conditioning, control_net, image, strength):823        if strength == 0:824            return (conditioning, )825 826        c = []827        control_hint = image.movedim(-1,1)828        for t in conditioning:829            n = [t[0], t[1].copy()]830            c_net = control_net.copy().set_cond_hint(control_hint, strength)831            if 'control' in t[1]:832                c_net.set_previous_controlnet(t[1]['control'])833            n[1]['control'] = c_net834            n[1]['control_apply_to_uncond'] = True835            c.append(n)836        return (c, )837 838 839class ControlNetApplyAdvanced:840    @classmethod841    def INPUT_TYPES(s):842        return {"required": {"positive": ("CONDITIONING", ),843                             "negative": ("CONDITIONING", ),844                             "control_net": ("CONTROL_NET", ),845                             "image": ("IMAGE", ),846                             "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),847                             "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),848                             "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001})849                             },850                "optional": {"vae": ("VAE", ),851                             }852    }853 854    RETURN_TYPES = ("CONDITIONING","CONDITIONING")855    RETURN_NAMES = ("positive", "negative")856    FUNCTION = "apply_controlnet"857 858    CATEGORY = "conditioning/controlnet"859 860    def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent, vae=None, extra_concat=[]):861        if strength == 0:862            return (positive, negative)863 864        control_hint = image.movedim(-1,1)865        cnets = {}866 867        out = []868        for conditioning in [positive, negative]:869            c = []870            for t in conditioning:871                d = t[1].copy()872 873                prev_cnet = d.get('control', None)874                if prev_cnet in cnets:875                    c_net = cnets[prev_cnet]876                else:877                    c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent), vae=vae, extra_concat=extra_concat)878                    c_net.set_previous_controlnet(prev_cnet)879                    cnets[prev_cnet] = c_net880 881                d['control'] = c_net882                d['control_apply_to_uncond'] = False883                n = [t[0], d]884                c.append(n)885            out.append(c)886        return (out[0], out[1])887 888 889class UNETLoader:890    @classmethod891    def INPUT_TYPES(s):892        return {"required": { "unet_name": (folder_paths.get_filename_list("diffusion_models"), ),893                              "weight_dtype": (["default", "fp8_e4m3fn", "fp8_e4m3fn_fast", "fp8_e5m2"],)894                             }}895    RETURN_TYPES = ("MODEL",)896    FUNCTION = "load_unet"897 898    CATEGORY = "advanced/loaders"899 900    def load_unet(self, unet_name, weight_dtype):901        model_options = {}902        if weight_dtype == "fp8_e4m3fn":903            model_options["dtype"] = torch.float8_e4m3fn904        elif weight_dtype == "fp8_e4m3fn_fast":905            model_options["dtype"] = torch.float8_e4m3fn906            model_options["fp8_optimizations"] = True907        elif weight_dtype == "fp8_e5m2":908            model_options["dtype"] = torch.float8_e5m2909 910        unet_path = folder_paths.get_full_path_or_raise("diffusion_models", unet_name)911        model = comfy.sd.load_diffusion_model(unet_path, model_options=model_options)912        return (model,)913 914class CLIPLoader:915    @classmethod916    def INPUT_TYPES(s):917        return {"required": { "clip_name": (folder_paths.get_filename_list("text_encoders"), ),918                              "type": (["stable_diffusion", "stable_cascade", "sd3", "stable_audio", "mochi", "ltxv", "pixart", "cosmos", "lumina2", "wan"], ),919                              },920                "optional": {921                              "device": (["default", "cpu"], {"advanced": True}),922                             }}923    RETURN_TYPES = ("CLIP",)924    FUNCTION = "load_clip"925 926    CATEGORY = "advanced/loaders"927 928    DESCRIPTION = "[Recipes]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 xxl/ clip-g / clip-l\nstable_audio: t5 base\nmochi: t5 xxl\ncosmos: old t5 xxl\nlumina2: gemma 2 2B\nwan: umt5 xxl"929 930    def load_clip(self, clip_name, type="stable_diffusion", device="default"):931        if type == "stable_cascade":932            clip_type = comfy.sd.CLIPType.STABLE_CASCADE933        elif type == "sd3":934            clip_type = comfy.sd.CLIPType.SD3935        elif type == "stable_audio":936            clip_type = comfy.sd.CLIPType.STABLE_AUDIO937        elif type == "mochi":938            clip_type = comfy.sd.CLIPType.MOCHI939        elif type == "ltxv":940            clip_type = comfy.sd.CLIPType.LTXV941        elif type == "pixart":942            clip_type = comfy.sd.CLIPType.PIXART943        elif type == "cosmos":944            clip_type = comfy.sd.CLIPType.COSMOS945        elif type == "lumina2":946            clip_type = comfy.sd.CLIPType.LUMINA2947        elif type == "wan":948            clip_type = comfy.sd.CLIPType.WAN949        else:950            clip_type = comfy.sd.CLIPType.STABLE_DIFFUSION951 952        model_options = {}953        if device == "cpu":954            model_options["load_device"] = model_options["offload_device"] = torch.device("cpu")955 956        clip_path = folder_paths.get_full_path_or_raise("text_encoders", clip_name)957        clip = comfy.sd.load_clip(ckpt_paths=[clip_path], embedding_directory=folder_paths.get_folder_paths("embeddings"), clip_type=clip_type, model_options=model_options)958        return (clip,)959 960class DualCLIPLoader:961    @classmethod962    def INPUT_TYPES(s):963        return {"required": { "clip_name1": (folder_paths.get_filename_list("text_encoders"), ),964                              "clip_name2": (folder_paths.get_filename_list("text_encoders"), ),965                              "type": (["sdxl", "sd3", "flux", "hunyuan_video"], ),966                              },967                "optional": {968                              "device": (["default", "cpu"], {"advanced": True}),969                             }}970    RETURN_TYPES = ("CLIP",)971    FUNCTION = "load_clip"972 973    CATEGORY = "advanced/loaders"974 975    DESCRIPTION = "[Recipes]\n\nsdxl: clip-l, clip-g\nsd3: clip-l, clip-g / clip-l, t5 / clip-g, t5\nflux: clip-l, t5"976 977    def load_clip(self, clip_name1, clip_name2, type, device="default"):978        clip_path1 = folder_paths.get_full_path_or_raise("text_encoders", clip_name1)979        clip_path2 = folder_paths.get_full_path_or_raise("text_encoders", clip_name2)980        if type == "sdxl":981            clip_type = comfy.sd.CLIPType.STABLE_DIFFUSION982        elif type == "sd3":983            clip_type = comfy.sd.CLIPType.SD3984        elif type == "flux":985            clip_type = comfy.sd.CLIPType.FLUX986        elif type == "hunyuan_video":987            clip_type = comfy.sd.CLIPType.HUNYUAN_VIDEO988 989        model_options = {}990        if device == "cpu":991            model_options["load_device"] = model_options["offload_device"] = torch.device("cpu")992 993        clip = comfy.sd.load_clip(ckpt_paths=[clip_path1, clip_path2], embedding_directory=folder_paths.get_folder_paths("embeddings"), clip_type=clip_type, model_options=model_options)994        return (clip,)995 996class CLIPVisionLoader:997    @classmethod998    def INPUT_TYPES(s):999        return {"required": { "clip_name": (folder_paths.get_filename_list("clip_vision"), ),1000                             }}1001    RETURN_TYPES = ("CLIP_VISION",)1002    FUNCTION = "load_clip"1003 1004    CATEGORY = "loaders"1005 1006    def load_clip(self, clip_name):1007        clip_path = folder_paths.get_full_path_or_raise("clip_vision", clip_name)1008        clip_vision = comfy.clip_vision.load(clip_path)1009        return (clip_vision,)1010 1011class CLIPVisionEncode:1012    @classmethod1013    def INPUT_TYPES(s):1014        return {"required": { "clip_vision": ("CLIP_VISION",),1015                              "image": ("IMAGE",),1016                              "crop": (["center", "none"],)1017                             }}1018    RETURN_TYPES = ("CLIP_VISION_OUTPUT",)1019    FUNCTION = "encode"1020 1021    CATEGORY = "conditioning"1022 1023    def encode(self, clip_vision, image, crop):1024        crop_image = True1025        if crop != "center":1026            crop_image = False1027        output = clip_vision.encode_image(image, crop=crop_image)1028        return (output,)1029 1030class StyleModelLoader:1031    @classmethod1032    def INPUT_TYPES(s):1033        return {"required": { "style_model_name": (folder_paths.get_filename_list("style_models"), )}}1034 1035    RETURN_TYPES = ("STYLE_MODEL",)1036    FUNCTION = "load_style_model"1037 1038    CATEGORY = "loaders"1039 1040    def load_style_model(self, style_model_name):1041        style_model_path = folder_paths.get_full_path_or_raise("style_models", style_model_name)1042        style_model = comfy.sd.load_style_model(style_model_path)1043        return (style_model,)1044 1045 1046class StyleModelApply:1047    @classmethod1048    def INPUT_TYPES(s):1049        return {"required": {"conditioning": ("CONDITIONING", ),1050                             "style_model": ("STYLE_MODEL", ),1051                             "clip_vision_output": ("CLIP_VISION_OUTPUT", ),1052                             "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}),1053                             "strength_type": (["multiply", "attn_bias"], ),1054                             }}1055    RETURN_TYPES = ("CONDITIONING",)1056    FUNCTION = "apply_stylemodel"1057 1058    CATEGORY = "conditioning/style_model"1059 1060    def apply_stylemodel(self, conditioning, style_model, clip_vision_output, strength, strength_type):1061        cond = style_model.get_cond(clip_vision_output).flatten(start_dim=0, end_dim=1).unsqueeze(dim=0)1062        if strength_type == "multiply":1063            cond *= strength1064 1065        n = cond.shape[1]1066        c_out = []1067        for t in conditioning:1068            (txt, keys) = t1069            keys = keys.copy()1070            # even if the strength is 1.0 (i.e, no change), if there's already a mask, we have to add to it1071            if "attention_mask" in keys or (strength_type == "attn_bias" and strength != 1.0):1072                # math.log raises an error if the argument is zero1073                # torch.log returns -inf, which is what we want1074                attn_bias = torch.log(torch.Tensor([strength if strength_type == "attn_bias" else 1.0]))1075                # get the size of the mask image1076                mask_ref_size = keys.get("attention_mask_img_shape", (1, 1))1077                n_ref = mask_ref_size[0] * mask_ref_size[1]1078                n_txt = txt.shape[1]1079                # grab the existing mask1080                mask = keys.get("attention_mask", None)1081                # create a default mask if it doesn't exist1082                if mask is None:1083                    mask = torch.zeros((txt.shape[0], n_txt + n_ref, n_txt + n_ref), dtype=torch.float16)1084                # convert the mask dtype, because it might be boolean1085                # we want it to be interpreted as a bias1086                if mask.dtype == torch.bool:1087                    # log(True) = log(1) = 01088                    # log(False) = log(0) = -inf1089                    mask = torch.log(mask.to(dtype=torch.float16))1090                # now we make the mask bigger to add space for our new tokens1091                new_mask = torch.zeros((txt.shape[0], n_txt + n + n_ref, n_txt + n + n_ref), dtype=torch.float16)1092                # copy over the old mask, in quandrants1093                new_mask[:, :n_txt, :n_txt] = mask[:, :n_txt, :n_txt]1094                new_mask[:, :n_txt, n_txt+n:] = mask[:, :n_txt, n_txt:]1095                new_mask[:, n_txt+n:, :n_txt] = mask[:, n_txt:, :n_txt]1096                new_mask[:, n_txt+n:, n_txt+n:] = mask[:, n_txt:, n_txt:]1097                # now fill in the attention bias to our redux tokens1098                new_mask[:, :n_txt, n_txt:n_txt+n] = attn_bias1099                new_mask[:, n_txt+n:, n_txt:n_txt+n] = attn_bias1100                keys["attention_mask"] = new_mask.to(txt.device)1101                keys["attention_mask_img_shape"] = mask_ref_size1102 1103            c_out.append([torch.cat((txt, cond), dim=1), keys])1104 1105        return (c_out,)1106 1107class unCLIPConditioning:1108    @classmethod1109    def INPUT_TYPES(s):1110        return {"required": {"conditioning": ("CONDITIONING", ),1111                             "clip_vision_output": ("CLIP_VISION_OUTPUT", ),1112                             "strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),1113                             "noise_augmentation": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),1114                             }}1115    RETURN_TYPES = ("CONDITIONING",)1116    FUNCTION = "apply_adm"1117 1118    CATEGORY = "conditioning"1119 1120    def apply_adm(self, conditioning, clip_vision_output, strength, noise_augmentation):1121        if strength == 0:1122            return (conditioning, )1123 1124        c = []1125        for t in conditioning:1126            o = t[1].copy()1127            x = {"clip_vision_output": clip_vision_output, "strength": strength, "noise_augmentation": noise_augmentation}1128            if "unclip_conditioning" in o:1129                o["unclip_conditioning"] = o["unclip_conditioning"][:] + [x]1130            else:1131                o["unclip_conditioning"] = [x]1132            n = [t[0], o]1133            c.append(n)1134        return (c, )1135 1136class GLIGENLoader:1137    @classmethod1138    def INPUT_TYPES(s):1139        return {"required": { "gligen_name": (folder_paths.get_filename_list("gligen"), )}}1140 1141    RETURN_TYPES = ("GLIGEN",)1142    FUNCTION = "load_gligen"1143 1144    CATEGORY = "loaders"1145 1146    def load_gligen(self, gligen_name):1147        gligen_path = folder_paths.get_full_path_or_raise("gligen", gligen_name)1148        gligen = comfy.sd.load_gligen(gligen_path)1149        return (gligen,)1150 1151class GLIGENTextBoxApply:1152    @classmethod1153    def INPUT_TYPES(s):1154        return {"required": {"conditioning_to": ("CONDITIONING", ),1155                              "clip": ("CLIP", ),1156                              "gligen_textbox_model": ("GLIGEN", ),1157                              "text": ("STRING", {"multiline": True, "dynamicPrompts": True}),1158                              "width": ("INT", {"default": 64, "min": 8, "max": MAX_RESOLUTION, "step": 8}),1159                              "height": ("INT", {"default": 64, "min": 8, "max": MAX_RESOLUTION, "step": 8}),1160                              "x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}),1161                              "y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}),1162                             }}1163    RETURN_TYPES = ("CONDITIONING",)1164    FUNCTION = "append"1165 1166    CATEGORY = "conditioning/gligen"1167 1168    def append(self, conditioning_to, clip, gligen_textbox_model, text, width, height, x, y):1169        c = []1170        cond, cond_pooled = clip.encode_from_tokens(clip.tokenize(text), return_pooled="unprojected")1171        for t in conditioning_to:1172            n = [t[0], t[1].copy()]1173            position_params = [(cond_pooled, height // 8, width // 8, y // 8, x // 8)]1174            prev = []1175            if "gligen" in n[1]:1176                prev = n[1]['gligen'][2]1177 1178            n[1]['gligen'] = ("position", gligen_textbox_model, prev + position_params)1179            c.append(n)1180        return (c, )1181 1182class EmptyLatentImage:1183    def __init__(self):1184        self.device = comfy.model_management.intermediate_device()1185 1186    @classmethod1187    def INPUT_TYPES(s):1188        return {1189            "required": {1190                "width": ("INT", {"default": 512, "min": 16, "max": MAX_RESOLUTION, "step": 8, "tooltip": "The width of the latent images in pixels."}),1191                "height": ("INT", {"default": 512, "min": 16, "max": MAX_RESOLUTION, "step": 8, "tooltip": "The height of the latent images in pixels."}),1192                "batch_size": ("INT", {"default": 1, "min": 1, "max": 4096, "tooltip": "The number of latent images in the batch."})1193            }1194        }1195    RETURN_TYPES = ("LATENT",)1196    OUTPUT_TOOLTIPS = ("The empty latent image batch.",)1197    FUNCTION = "generate"1198 1199    CATEGORY = "latent"1200    DESCRIPTION = "Create a new batch of empty latent images to be denoised via sampling."

Showing the first 1,200 of 2297 lines. Download the file for the rest.