From 9a12a9328b5b007b762e04599b1eba4cd84e45e4 Mon Sep 17 00:00:00 2001 From: Jedrzej Kosinski Date: Fri, 22 May 2026 21:38:58 -0700 Subject: [PATCH] Revert per-loader device inputs from #13483 / #13748 Remove the device-selection widgets that were added directly to existing loader nodes (and the new CheckpointLoaderDevice / ImageOnlyCheckpointLoaderDevice variants): - nodes.py: - delete CheckpointLoaderDevice class and its NODE_CLASS_MAPPINGS / NODE_DISPLAY_NAME_MAPPINGS entries - remove the optional `device` input + VALIDATE_INPUTS + resolve logic from UNETLoader, VAELoader, CLIPLoader, DualCLIPLoader - restore CLIPLoader/DualCLIPLoader `device` options to ["default", "cpu"] - comfy_extras/nodes_video_model.py: - delete ImageOnlyCheckpointLoaderDevice class + its mapping entries - comfy_extras/nodes_lt_audio.py: - restore LTXAVTextEncoderLoader `device` options to ["default", "cpu"] and revert the resolve logic back to the simple `if device == "cpu"` branch The replacement approach is a small set of passthrough Select*Device nodes (added in the next commit) that retarget MODEL/CLIP/VAE devices without bloating every loader's UI or duplicating loaders. The cuda_device_context helper and the model_management helpers (get_gpu_device_options / resolve_gpu_device_option) from #13483 are kept; they are still used by the new selector nodes. Amp-Thread-ID: https://ampcode.com/threads/T-019e52b4-31ee-72cd-996b-64ecd9420e13 Co-authored-by: Amp --- comfy_extras/nodes_lt_audio.py | 10 +-- comfy_extras/nodes_video_model.py | 65 --------------- nodes.py | 127 +++--------------------------- 3 files changed, 13 insertions(+), 189 deletions(-) diff --git a/comfy_extras/nodes_lt_audio.py b/comfy_extras/nodes_lt_audio.py index afe5b0d13..51ddf584a 100644 --- a/comfy_extras/nodes_lt_audio.py +++ b/comfy_extras/nodes_lt_audio.py @@ -182,7 +182,7 @@ class LTXAVTextEncoderLoader(io.ComfyNode): ), io.Combo.Input( "device", - options=comfy.model_management.get_gpu_device_options(), + options=["default", "cpu"], advanced=True, ) ], @@ -197,12 +197,8 @@ class LTXAVTextEncoderLoader(io.ComfyNode): clip_path2 = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name) model_options = {} - resolved = comfy.model_management.resolve_gpu_device_option(device) - if resolved is not None: - if resolved.type == "cpu": - model_options["load_device"] = model_options["offload_device"] = resolved - else: - model_options["load_device"] = resolved + if device == "cpu": + model_options["load_device"] = model_options["offload_device"] = torch.device("cpu") 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) return io.NodeOutput(clip) diff --git a/comfy_extras/nodes_video_model.py b/comfy_extras/nodes_video_model.py index b0d0390ca..8f19895a1 100644 --- a/comfy_extras/nodes_video_model.py +++ b/comfy_extras/nodes_video_model.py @@ -23,69 +23,6 @@ class ImageOnlyCheckpointLoader: return (out[0], out[3], out[2]) -class ImageOnlyCheckpointLoaderDevice: - @classmethod - def INPUT_TYPES(s): - device_options = comfy.model_management.get_gpu_device_options() - return { - "required": { - "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), - }, - "optional": { - "model_device": (device_options, {"advanced": True, "tooltip": "Device for the diffusion model (UNET)."}), - "clip_vision_device": (device_options, {"advanced": True, "tooltip": "Device for the CLIP vision encoder."}), - "vae_device": (device_options, {"advanced": True, "tooltip": "Device for the VAE."}), - } - } - RETURN_TYPES = ("MODEL", "CLIP_VISION", "VAE") - FUNCTION = "load_checkpoint" - - CATEGORY = "loaders/video_models" - - @classmethod - def VALIDATE_INPUTS(cls, model_device="default", clip_vision_device="default", vae_device="default"): - return True - - def load_checkpoint(self, ckpt_name, output_vae=True, output_clip=True, model_device="default", clip_vision_device="default", vae_device="default"): - ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name) - - model_options = {} - resolved_model = comfy.model_management.resolve_gpu_device_option(model_device) - if resolved_model is not None: - if resolved_model.type == "cpu": - model_options["load_device"] = model_options["offload_device"] = resolved_model - else: - model_options["load_device"] = resolved_model - - cv_model_options = {} - resolved_clip = comfy.model_management.resolve_gpu_device_option(clip_vision_device) - if resolved_clip is not None: - if resolved_clip.type == "cpu": - cv_model_options["load_device"] = cv_model_options["offload_device"] = resolved_clip - else: - cv_model_options["load_device"] = resolved_clip - - # VAE device is passed via model_options["load_device"] which - # load_state_dict_guess_config forwards to the VAE constructor. - # If vae_device differs from model_device, we override after loading. - resolved_vae = comfy.model_management.resolve_gpu_device_option(vae_device) - - out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=False, output_clipvision=True, embedding_directory=folder_paths.get_folder_paths("embeddings")) - model_patcher, clip, vae, clip_vision = out[:4] - - # Apply VAE device override if it differs from the model device - if resolved_vae is not None and vae is not None: - vae.device = resolved_vae - if resolved_vae.type == "cpu": - offload = resolved_vae - else: - offload = comfy.model_management.vae_offload_device() - vae.patcher.load_device = resolved_vae - vae.patcher.offload_device = offload - - return (model_patcher, clip_vision, vae) - - class SVD_img2vid_Conditioning: @classmethod def INPUT_TYPES(s): @@ -212,7 +149,6 @@ class ConditioningSetAreaPercentageVideo: NODE_CLASS_MAPPINGS = { "ImageOnlyCheckpointLoader": ImageOnlyCheckpointLoader, - "ImageOnlyCheckpointLoaderDevice": ImageOnlyCheckpointLoaderDevice, "SVD_img2vid_Conditioning": SVD_img2vid_Conditioning, "VideoLinearCFGGuidance": VideoLinearCFGGuidance, "VideoTriangleCFGGuidance": VideoTriangleCFGGuidance, @@ -222,7 +158,6 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { "ImageOnlyCheckpointLoader": "Load Checkpoint Image Only (img2vid model)", - "ImageOnlyCheckpointLoaderDevice": "Image Only Checkpoint Loader (Device)", "VideoLinearCFGGuidance": "Video Linear CFG Guidance", "VideoTriangleCFGGuidance": "Video Triangle CFG Guidance", } diff --git a/nodes.py b/nodes.py index 2f3856330..d1e9a2511 100644 --- a/nodes.py +++ b/nodes.py @@ -608,73 +608,6 @@ class CheckpointLoaderSimple: out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings")) return out[:3] - -class CheckpointLoaderDevice: - @classmethod - def INPUT_TYPES(s): - device_options = comfy.model_management.get_gpu_device_options() - return { - "required": { - "ckpt_name": (folder_paths.get_filename_list("checkpoints"), {"tooltip": "The name of the checkpoint (model) to load."}), - }, - "optional": { - "model_device": (device_options, {"advanced": True, "tooltip": "Device for the diffusion model (UNET)."}), - "clip_device": (device_options, {"advanced": True, "tooltip": "Device for the CLIP text encoder."}), - "vae_device": (device_options, {"advanced": True, "tooltip": "Device for the VAE."}), - } - } - RETURN_TYPES = ("MODEL", "CLIP", "VAE") - OUTPUT_TOOLTIPS = ("The model used for denoising latents.", - "The CLIP model used for encoding text prompts.", - "The VAE model used for encoding and decoding images to and from latent space.") - FUNCTION = "load_checkpoint" - - CATEGORY = "advanced/loaders" - DESCRIPTION = "Loads a diffusion model checkpoint with per-component device selection for multi-GPU setups." - - @classmethod - def VALIDATE_INPUTS(cls, model_device="default", clip_device="default", vae_device="default"): - return True - - def load_checkpoint(self, ckpt_name, model_device="default", clip_device="default", vae_device="default"): - ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name) - - model_options = {} - resolved_model = comfy.model_management.resolve_gpu_device_option(model_device) - if resolved_model is not None: - if resolved_model.type == "cpu": - model_options["load_device"] = model_options["offload_device"] = resolved_model - else: - model_options["load_device"] = resolved_model - - te_model_options = {} - resolved_clip = comfy.model_management.resolve_gpu_device_option(clip_device) - if resolved_clip is not None: - if resolved_clip.type == "cpu": - te_model_options["load_device"] = te_model_options["offload_device"] = resolved_clip - else: - te_model_options["load_device"] = resolved_clip - - # VAE device is passed via model_options["load_device"] which - # load_state_dict_guess_config forwards to the VAE constructor. - # If vae_device differs from model_device, we override after loading. - resolved_vae = comfy.model_management.resolve_gpu_device_option(vae_device) - - out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings"), model_options=model_options, te_model_options=te_model_options) - model_patcher, clip, vae = out[:3] - - # Apply VAE device override if it differs from the model device - if resolved_vae is not None and vae is not None: - vae.device = resolved_vae - if resolved_vae.type == "cpu": - offload = resolved_vae - else: - offload = comfy.model_management.vae_offload_device() - vae.patcher.load_device = resolved_vae - vae.patcher.offload_device = offload - - return (model_patcher, clip, vae) - class DiffusersLoader: SEARCH_ALIASES = ["load diffusers model"] @@ -853,21 +786,14 @@ class VAELoader: @classmethod def INPUT_TYPES(s): - return {"required": { "vae_name": (s.vae_list(s), )}, - "optional": { - "device": (comfy.model_management.get_gpu_device_options(), {"advanced": True}), - }} + return {"required": { "vae_name": (s.vae_list(s), )}} RETURN_TYPES = ("VAE",) FUNCTION = "load_vae" CATEGORY = "loaders" - @classmethod - def VALIDATE_INPUTS(cls, device="default"): - return True - #TODO: scale factor? - def load_vae(self, vae_name, device="default"): + def load_vae(self, vae_name): metadata = None if vae_name == "pixel_space": sd = {} @@ -885,8 +811,7 @@ class VAELoader: metadata = {"tae_latent_channels": 128} else: metadata["tae_latent_channels"] = 128 - resolved = comfy.model_management.resolve_gpu_device_option(device) - vae = comfy.sd.VAE(sd=sd, metadata=metadata, device=resolved) + vae = comfy.sd.VAE(sd=sd, metadata=metadata) vae.throw_exception_if_invalid() return (vae,) @@ -1012,20 +937,13 @@ class UNETLoader: def INPUT_TYPES(s): return {"required": { "unet_name": (folder_paths.get_filename_list("diffusion_models"), ), "weight_dtype": (["default", "fp8_e4m3fn", "fp8_e4m3fn_fast", "fp8_e5m2"], {"advanced": True}) - }, - "optional": { - "device": (comfy.model_management.get_gpu_device_options(), {"advanced": True}), }} RETURN_TYPES = ("MODEL",) FUNCTION = "load_unet" CATEGORY = "advanced/loaders" - @classmethod - def VALIDATE_INPUTS(cls, device="default"): - return True - - def load_unet(self, unet_name, weight_dtype, device="default"): + def load_unet(self, unet_name, weight_dtype): model_options = {} if weight_dtype == "fp8_e4m3fn": model_options["dtype"] = torch.float8_e4m3fn @@ -1035,13 +953,6 @@ class UNETLoader: elif weight_dtype == "fp8_e5m2": model_options["dtype"] = torch.float8_e5m2 - resolved = comfy.model_management.resolve_gpu_device_option(device) - if resolved is not None: - if resolved.type == "cpu": - model_options["load_device"] = model_options["offload_device"] = resolved - else: - model_options["load_device"] = resolved - unet_path = folder_paths.get_full_path_or_raise("diffusion_models", unet_name) model = comfy.sd.load_diffusion_model(unet_path, model_options=model_options) return (model,) @@ -1053,7 +964,7 @@ class CLIPLoader: "type": (["stable_diffusion", "stable_cascade", "sd3", "stable_audio", "mochi", "ltxv", "pixart", "cosmos", "lumina2", "wan", "hidream", "chroma", "ace", "omnigen2", "qwen_image", "hunyuan_image", "flux2", "ovis", "longcat_image", "cogvideox"], ), }, "optional": { - "device": (comfy.model_management.get_gpu_device_options(), {"advanced": True}), + "device": (["default", "cpu"], {"advanced": True}), }} RETURN_TYPES = ("CLIP",) FUNCTION = "load_clip" @@ -1062,20 +973,12 @@ class CLIPLoader: 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\ncogvideox: t5 xxl (226-token padding)\ncosmos: old t5 xxl\nlumina2: gemma 2 2B\nwan: umt5 xxl\n hidream: llama-3.1 (Recommend) or t5\nomnigen2: qwen vl 2.5 3B" - @classmethod - def VALIDATE_INPUTS(cls, device="default"): - return True - def load_clip(self, clip_name, type="stable_diffusion", device="default"): clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) model_options = {} - resolved = comfy.model_management.resolve_gpu_device_option(device) - if resolved is not None: - if resolved.type == "cpu": - model_options["load_device"] = model_options["offload_device"] = resolved - else: - model_options["load_device"] = resolved + if device == "cpu": + model_options["load_device"] = model_options["offload_device"] = torch.device("cpu") clip_path = folder_paths.get_full_path_or_raise("text_encoders", clip_name) 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) @@ -1089,7 +992,7 @@ class DualCLIPLoader: "type": (["sdxl", "sd3", "flux", "hunyuan_video", "hidream", "hunyuan_image", "hunyuan_video_15", "kandinsky5", "kandinsky5_image", "ltxv", "newbie", "ace"], ), }, "optional": { - "device": (comfy.model_management.get_gpu_device_options(), {"advanced": True}), + "device": (["default", "cpu"], {"advanced": True}), }} RETURN_TYPES = ("CLIP",) FUNCTION = "load_clip" @@ -1098,10 +1001,6 @@ class DualCLIPLoader: DESCRIPTION = "[Recipes]\n\nsdxl: clip-l, clip-g\nsd3: clip-l, clip-g / clip-l, t5 / clip-g, t5\nflux: clip-l, t5\nhidream: at least one of t5 or llama, recommended t5 and llama\nhunyuan_image: qwen2.5vl 7b and byt5 small\nnewbie: gemma-3-4b-it, jina clip v2" - @classmethod - def VALIDATE_INPUTS(cls, device="default"): - return True - def load_clip(self, clip_name1, clip_name2, type, device="default"): clip_type = getattr(comfy.sd.CLIPType, type.upper(), comfy.sd.CLIPType.STABLE_DIFFUSION) @@ -1109,12 +1008,8 @@ class DualCLIPLoader: clip_path2 = folder_paths.get_full_path_or_raise("text_encoders", clip_name2) model_options = {} - resolved = comfy.model_management.resolve_gpu_device_option(device) - if resolved is not None: - if resolved.type == "cpu": - model_options["load_device"] = model_options["offload_device"] = resolved - else: - model_options["load_device"] = resolved + if device == "cpu": + model_options["load_device"] = model_options["offload_device"] = torch.device("cpu") 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) return (clip,) @@ -2177,7 +2072,6 @@ NODE_CLASS_MAPPINGS = { "InpaintModelConditioning": InpaintModelConditioning, "CheckpointLoader": CheckpointLoader, - "CheckpointLoaderDevice": CheckpointLoaderDevice, "DiffusersLoader": DiffusersLoader, "LoadLatent": LoadLatent, @@ -2195,7 +2089,6 @@ NODE_DISPLAY_NAME_MAPPINGS = { # Loaders "CheckpointLoader": "Load Checkpoint With Config (DEPRECATED)", "CheckpointLoaderSimple": "Load Checkpoint", - "CheckpointLoaderDevice": "Load Checkpoint (Device)", "VAELoader": "Load VAE", "LoraLoader": "Load LoRA (Model and CLIP)", "LoraLoaderModelOnly": "Load LoRA",