Add SDXL model loaders with documentation

Implement SDXL loading nodes:
- sdxl_models_loader.py: basic all-in-one loader with checkpoint merging
- sdxl_models_loader_advanced.py: extended loader with 4 LoRA slots and advanced VAE/CLIP settings

Add markdown documentation for both nodes
This commit is contained in:
TenserTensor
2026-02-13 16:32:12 +03:00
parent 915d55e35d
commit 3a0b6bd856
6 changed files with 277 additions and 0 deletions
+8
View File
@@ -5,6 +5,8 @@ from .nodes.context.even_larger_context import TT_EvenLargerContext
from .nodes.context.large_context_flux import TT_LargeContextFlux
from .nodes.context.large_context_sdxl import TT_LargeContextSdxl
from .nodes.latent.latent_factory import TT_LatentFactory
from .nodes.loaders.sdxl_models_loader import TT_SdxlModelsLoader
from .nodes.loaders.sdxl_models_loader_advanced import TT_SdxlModelsLoaderAdvanced
from .nodes.workflow.flux_workfow_settings import TT_FluxWorkflowSettings
from .nodes.workflow.flux_workfow_settings_advanced import TT_FluxWorkflowSettingsAdvanced
from .nodes.workflow.sdxl_workfow_settings import TT_SdxlWorkflowSettings
@@ -21,6 +23,9 @@ NODE_CLASS_MAPPINGS = {
"TT_ContextPassthrough": TT_ContextPassthrough,
# Latent
"TT_LatentFactory": TT_LatentFactory,
# Loaders
"TT_SdxlModelsLoader": TT_SdxlModelsLoader,
"TT_SdxlModelsLoaderAdvanced": TT_SdxlModelsLoaderAdvanced,
# Workflow
"TT_FluxWorkflowSettings": TT_FluxWorkflowSettings,
"TT_FluxWorkflowSettingsAdvanced": TT_FluxWorkflowSettingsAdvanced,
@@ -39,6 +44,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"TT_ContextPassthrough": "TT Context Passthrough",
# Latent
"TT_LatentFactory": "TT Latent Factory",
# Loaders
"TT_SdxlModelsLoader": "TT SDXL Models Loader",
"TT_SdxlModelsLoaderAdvanced": "TT SDXL Models Loader (Advanced)",
# Workflow
"TT_FluxWorkflowSettings": "TT FLUX Workflow Settings",
"TT_FluxWorkflowSettingsAdvanced": "TT FLUX Workflow Settings (Advanced)",
+77
View File
@@ -0,0 +1,77 @@
import os
import comfy
import folder_paths
import torch
from nodes import VAELoader
VIDEO_TAES = ["taehv", "lighttaew2_2", "lighttaew2_1", "lighttaehy1_5"]
IMAGE_TAES = ["taesd", "taesdxl", "taesd3", "taef1"]
def load_checkpoint(ckpt_name):
ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name)
return comfy.sd.load_checkpoint_guess_config(
ckpt_path, output_vae=False, output_clip=False,
embedding_directory=folder_paths.get_folder_paths("embeddings")
)
def load_clip(clip_l, clip_g, device):
clip_type = getattr(comfy.sd.CLIPType, "SDXL", comfy.sd.CLIPType.STABLE_DIFFUSION)
clip_l_path = folder_paths.get_full_path_or_raise("text_encoders", clip_l)
clip_g_path = folder_paths.get_full_path_or_raise("text_encoders", clip_g)
model_options = {}
if device == "cpu":
model_options["load_device"] = model_options["offload_device"] = torch.device("cpu")
return comfy.sd.load_clip(
ckpt_paths=[clip_l_path, clip_g_path],
embedding_directory=folder_paths.get_folder_paths("embeddings"),
clip_type=clip_type,
model_options=model_options
)
def apply_lora(loaded_lora, model, clip, lora_name, strength):
if loaded_lora == None:
lora_path = folder_paths.get_full_path_or_raise("loras", lora_name)
lora = comfy.utils.load_torch_file(lora_path, safe_load=True)
else:
lora = loaded_lora
patched_model, patched_clip = comfy.sd.load_lora_for_models(model, clip, lora, strength, strength)
return (patched_model, patched_clip, lora)
def load_vae(vae_name, vae_device, vae_dtype):
dtype = {"bfloat16": torch.bfloat16, "float16": torch.float16, "float32": torch.float32}[vae_dtype]
device = None
if vae_device == "default":
device = comfy.model_management.get_torch_device()
elif vae_device == "cpu":
device = torch.device("cpu")
metadata = None
if vae_name == "pixel_space":
state_dict = {}
state_dict["pixel_space_vae"] = torch.tensor(1.0)
elif vae_name in IMAGE_TAES:
state_dict = VAELoader.load_taesd(vae_name)
else:
if os.path.splitext(vae_name)[0] in VIDEO_TAES:
vae_path = folder_paths.get_full_path_or_raise("vae_approx", vae_name)
else:
vae_path = folder_paths.get_full_path_or_raise("vae", vae_name)
state_dict, metadata = comfy.utils.load_torch_file(vae_path, return_metadata=True)
vae = comfy.sd.VAE(sd=state_dict, device=device, dtype=dtype, metadata=metadata)
vae.throw_exception_if_invalid()
return vae
+41
View File
@@ -0,0 +1,41 @@
import folder_paths
from nodes import VAELoader
from .loader_helpers import load_checkpoint, load_clip, load_vae
class TT_SdxlModelsLoader:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"primary_ckpt": (folder_paths.get_filename_list("checkpoints"),),
"secondary_ckpt": (["None"] + folder_paths.get_filename_list("checkpoints"),),
"primary_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"clip_l": (folder_paths.get_filename_list("text_encoders"),),
"clip_g": (folder_paths.get_filename_list("text_encoders"),),
"clip_device": (["default", "cpu"], {"advanced": True}),
"vae_name": (VAELoader.vae_list(VAELoader),)
}
}
RETURN_TYPES = ("MODEL", "CLIP", "VAE",)
RETURN_NAMES = ("MODEL", "CLIP", "VAE",)
FUNCTION = "load_models"
CATEGORY = "TenserTensor/Loaders/SDXL"
def load_models(self, primary_ckpt, secondary_ckpt, primary_weight, clip_l, clip_g, clip_device, vae_name):
model = load_checkpoint(primary_ckpt)[0]
if secondary_ckpt != "None":
secondary_model = load_checkpoint(secondary_ckpt)[0]
model = model.clone()
key_patches = secondary_model.get_key_patches("diffusion_model.")
for key in key_patches:
model.add_patches({key: key_patches[key]}, 1.0 - primary_weight, primary_weight)
clip = load_clip(clip_l, clip_g, clip_device)
vae = load_vae(vae_name, "cpu", "bfloat16")
return (model, clip, vae,)
@@ -0,0 +1,76 @@
import folder_paths
from nodes import VAELoader
from .loader_helpers import load_checkpoint, load_clip, apply_lora, load_vae
class TT_SdxlModelsLoaderAdvanced:
def __init__(self):
self.loaded_lora_1 = None
self.loaded_lora_2 = None
self.loaded_lora_3 = None
self.loaded_lora_4 = None
@classmethod
def INPUT_TYPES(cls):
DEVICES = ["default", "cpu"]
DTYPES = ["bfloat16", "float16", "float32"]
return {
"required": {
"primary_ckpt": (folder_paths.get_filename_list("checkpoints"),),
"secondary_ckpt": (["None"] + folder_paths.get_filename_list("checkpoints"),),
"primary_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"clip_l": (folder_paths.get_filename_list("text_encoders"),),
"clip_g": (folder_paths.get_filename_list("text_encoders"),),
"clip_device": (DEVICES, {"advanced": True}),
"lora_name_1": (["None"] + folder_paths.get_filename_list("loras"),),
"strength_1": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.1}),
"lora_name_2": (["None"] + folder_paths.get_filename_list("loras"),),
"strength_2": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.1}),
"lora_name_3": (["None"] + folder_paths.get_filename_list("loras"),),
"strength_3": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.1}),
"lora_name_4": (["None"] + folder_paths.get_filename_list("loras"),),
"strength_4": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.1}),
"vae_name": (VAELoader.vae_list(VAELoader),),
"vae_device": (DEVICES, {"advanced": True}),
"vae_dtype": (DTYPES, {"advanced": True}),
}
}
RETURN_TYPES = ("MODEL", "CLIP", "VAE",)
RETURN_NAMES = ("MODEL", "CLIP", "VAE",)
FUNCTION = "load_models"
CATEGORY = "TenserTensor/Loaders/SDXL"
def load_models(self, primary_ckpt, secondary_ckpt, primary_weight, clip_l, clip_g, clip_device,
lora_name_1, strength_1, lora_name_2, strength_2, lora_name_3, strength_3, lora_name_4, strength_4,
vae_name, vae_device, vae_dtype):
model = load_checkpoint(primary_ckpt)[0]
if secondary_ckpt != "None":
secondary_model = load_checkpoint(secondary_ckpt)[0]
model = model.clone()
key_patches = secondary_model.get_key_patches("diffusion_model.")
for key in key_patches:
model.add_patches({key: key_patches[key]}, 1.0 - primary_weight, primary_weight)
clip = load_clip(clip_l, clip_g, clip_device)
loras = [
(lora_name_1, strength_1, 'loaded_lora_1'),
(lora_name_2, strength_2, 'loaded_lora_2'),
(lora_name_3, strength_3, 'loaded_lora_3'),
(lora_name_4, strength_4, 'loaded_lora_4'),
]
for name, strength, attr in loras:
if name != "None" and strength != 0:
cached = getattr(self, attr)
model, clip, lora = apply_lora(cached, model, clip, name, strength)
setattr(self, attr, lora)
vae = load_vae(vae_name, vae_device, vae_dtype)
return (model, clip, vae,)
+32
View File
@@ -0,0 +1,32 @@
## TT_SdxlModelsLoader
All-in-one model loader for SDXL workflows with checkpoint merging support.
### Inputs
| Parameter | Type | Required | Description |
|------------------|--------------|----------|----------------------------------------------------------------|
| `primary_ckpt` | CHECKPOINT | Yes | Primary SDXL checkpoint to load |
| `secondary_ckpt` | CHECKPOINT | Yes | Secondary checkpoint to merge (select "None" to skip) |
| `primary_weight` | FLOAT | Yes | Weight for primary checkpoint in merge (0.0-1.0, default: 1.0) |
| `clip_l` | TEXT_ENCODER | Yes | CLIP-L text encoder model |
| `clip_g` | TEXT_ENCODER | Yes | CLIP-G text encoder model |
| `clip_device` | STRING | Yes | Device for CLIP ("default" or "cpu") - advanced |
| `vae_name` | VAE | Yes | VAE model to load |
### Outputs
| Parameter | Type | Description |
|-----------|-------|-------------------------------------------|
| `MODEL` | MODEL | Loaded (and optionally merged) SDXL model |
| `CLIP` | CLIP | Combined CLIP-L and CLIP-G text encoders |
| `VAE` | VAE | Loaded VAE |
### Usage
Use this node to load all SDXL model components in one place. Optionally merge two checkpoints by selecting a secondary
checkpoint and adjusting the primary_weight (1.0 = 100% primary, 0.5 = 50/50 mix, 0.0 = 100% secondary).
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
+43
View File
@@ -0,0 +1,43 @@
## TT_SdxlModelsLoaderAdvanced
Extended all-in-one model loader for SDXL with checkpoint merging and LoRA support.
### Inputs
| Parameter | Type | Required | Description |
|------------------|--------------|----------|-----------------------------------------------------------------|
| `primary_ckpt` | CHECKPOINT | Yes | Primary SDXL checkpoint to load |
| `secondary_ckpt` | CHECKPOINT | Yes | Secondary checkpoint to merge (select "None" to skip) |
| `primary_weight` | FLOAT | Yes | Weight for primary checkpoint in merge (0.0-1.0, default: 1.0) |
| `clip_l` | TEXT_ENCODER | Yes | CLIP-L text encoder model |
| `clip_g` | TEXT_ENCODER | Yes | CLIP-G text encoder model |
| `clip_device` | STRING | Yes | Device for CLIP ("default" or "cpu") - advanced |
| `lora_name_1` | LORA | Yes | First LoRA to apply (select "None" to skip) |
| `strength_1` | FLOAT | Yes | Strength for first LoRA (-10.0 to 10.0, default: 1.0) |
| `lora_name_2` | LORA | Yes | Second LoRA to apply (select "None" to skip) |
| `strength_2` | FLOAT | Yes | Strength for second LoRA (-10.0 to 10.0, default: 1.0) |
| `lora_name_3` | LORA | Yes | Third LoRA to apply (select "None" to skip) |
| `strength_3` | FLOAT | Yes | Strength for third LoRA (-10.0 to 10.0, default: 1.0) |
| `lora_name_4` | LORA | Yes | Fourth LoRA to apply (select "None" to skip) |
| `strength_4` | FLOAT | Yes | Strength for fourth LoRA (-10.0 to 10.0, default: 1.0) |
| `vae_name` | VAE | Yes | VAE model to load |
| `vae_device` | STRING | Yes | Device for VAE ("default" or "cpu") - advanced |
| `vae_dtype` | STRING | Yes | Data type for VAE ("bfloat16", "float16", "float32") - advanced |
### Outputs
| Parameter | Type | Description |
|-----------|-------|-------------------------------------------------------------|
| `MODEL` | MODEL | Loaded model with applied LoRAs and optional merge |
| `CLIP` | CLIP | Combined CLIP-L and CLIP-G text encoders with applied LoRAs |
| `VAE` | VAE | Loaded VAE |
### Usage
Extended version of the SDXL loader with support for up to 4 LoRAs and additional VAE configuration options. Load all
model components, optionally merge checkpoints, apply LoRAs sequentially, and configure device/dtype settings for CLIP
and VAE. LoRAs are cached for efficiency.
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)