WIP: 6 new nodes, minor reformat and refactoring, added docs

This commit is contained in:
TenserTensor
2026-02-22 00:28:52 +03:00
parent 009a0f5728
commit 063e00adba
40 changed files with 852 additions and 274 deletions
+8
View File
@@ -36,6 +36,14 @@ All-in-one model loader for SDXL workflows with checkpoint merging support.
Extended all-in-one model loader for SDXL with checkpoint merging and LoRA support.
#### TT GGUF Models Loader
All-in-one loader for GGUF-quantized models. Place your `.gguf` diffusion model files in the `diffusion_models` or `unet` directory, CLIP files in `text_encoders` or `clip` directory. VAE can be any standard ComfyUI-compatible VAE file.
#### TT GGUF Models Loader (Advanced)
Extended version of GGUF Models Loader. Supports up to 4 LoRAs loaded simultaneously. Enable `apply_sampling` to apply flux sampling shift patch — useful for improving quality at non-standard resolutions. Advanced parameters are hidden by default and can be revealed via the node's show/hide controls.
#### TT FLUX Models Loader
All-in-one model loader for Flux workflows.
+4 -1
View File
@@ -6,9 +6,12 @@ WEB_DIRECTORY = "./web"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
nodes_count = len(NODE_CLASS_MAPPINGS)
print()
SEP = f"{CYAN}{'=' * 60}{RESET}"
print(SEP)
print(f"{YELLOW}TenserTensor ComfyUI Nodes Pack (v1.3.1): {BRIGHT_GREEN}38 Nodes Loaded{RESET}")
print(f"{YELLOW}TenserTensor ComfyUI Nodes Pack (v1.4.0): {BRIGHT_GREEN}{nodes_count} Nodes Loaded{RESET}")
print(SEP)
print(f"{BRIGHT_CYAN}[V] Repository: https://github.com/tenser-tensor/ComfyUI-TenserTensor{RESET}")
print(f"{BRIGHT_CYAN}[V] ComfyUI Registry: https://registry.comfy.org/publishers/tenser-tensor{RESET}")
+28 -13
View File
@@ -21,17 +21,17 @@ try:
from .nodes.context.large_context_flux import TT_LargeContextFlux
from .nodes.context.large_context_sdxl import TT_LargeContextSdxl
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Context nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: Context nodes unavailable{RESET}")
try:
from .nodes.image.image_preview_save import TT_ImagePreviewSave
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Image nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: Image nodes unavailable{RESET}")
try:
from .nodes.latent.latent_factory import TT_LatentFactory
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Latent nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: Latent nodes unavailable{RESET}")
try:
from .nodes.loaders.flux_models_loader import TT_FluxModelsLoader
@@ -41,7 +41,7 @@ try:
from .nodes.loaders.sdxl_models_loader import TT_SdxlModelsLoader
from .nodes.loaders.sdxl_models_loader_advanced import TT_SdxlModelsLoaderAdvanced
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Loader nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: Loader nodes unavailable{RESET}")
try:
from .nodes.postproduction.add_film_grain import TT_AddFilmGrain
@@ -51,23 +51,26 @@ try:
from .nodes.postproduction.postproduction_advanced import TT_PostproductionAdvanced
from .nodes.postproduction.quick_image_upscaler import TT_QuickImageUpscaler
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Postproduction nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: Postproduction nodes unavailable{RESET}")
try:
from .nodes.sampling.ksampler import TT_KSampler
from .nodes.sampling.ksampler_advanced import TT_KSamplerAdvanced
from .nodes.sampling.ksampler_context import TT_KSamplerContext
from .nodes.sampling.ksampler_two_stage import TT_KSamplerTwoStage
from .nodes.sampling.ksampler_guided import TT_KSamplerGuided
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Sampler nodes unavailable{RESET}")
print("f{YELLOW}TenserTensor: {RED}ERROR: Sampler nodes unavailable{RESET}")
try:
from .nodes.text_encoders.clip_text_encode_flux import TT_ClipTextEncodeFlux
from .nodes.text_encoders.clip_text_encode_flux_context import TT_ClipTextEncodeFluxContext
from .nodes.text_encoders.clip_text_encode_sdxl import TT_ClipTextEncodeSdxl
from .nodes.text_encoders.clip_text_encode_sdxl_context import TT_ClipTextEncodeSdxlContext
from .nodes.text_encoders.clip_text_encode_flux2 import TT_ClipTextEncodeFlux2
from .nodes.text_encoders.clip_text_encode_flux2_context import TT_ClipTextEncodeFlux2Context
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Text Encoder nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: Text Encoder nodes unavailable{RESET}")
try:
from .nodes.vae.vae_decode_context import TT_VaeDecodeContext
@@ -75,15 +78,17 @@ try:
from .nodes.vae.vae_encode_context import TT_VaeEncodeContext
from .nodes.vae.vae_encode_tiled import TT_VaeEncodeTiled
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: VAE nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: VAE nodes unavailable{RESET}")
try:
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
from .nodes.workflow.sdxl_workfow_settings_advanced import TT_SdxlWorkflowSettingsAdvanced
from .nodes.workflow.flux_workflow_settings import TT_FluxWorkflowSettings
from .nodes.workflow.flux_workflow_settings_advanced import TT_FluxWorkflowSettingsAdvanced
from .nodes.workflow.sdxl_workflow_settings import TT_SdxlWorkflowSettings
from .nodes.workflow.sdxl_workflow_settings_advanced import TT_SdxlWorkflowSettingsAdvanced
from .nodes.workflow.flux2_workflow_settings import TT_Flux2WorkflowSettings
from .nodes.workflow.flux2_workflow_settings_advanced import TT_Flux2WorkflowSettingsAdvanced
except ImportError:
print("{YELLOW}TenserTensor: {RED}ERROR: Workflow nodes unavailable{RESET}")
print(f"{YELLOW}TenserTensor: {RED}ERROR: Workflow nodes unavailable{RESET}")
NODE_CLASS_MAPPINGS = {
# Context
@@ -119,11 +124,14 @@ NODE_CLASS_MAPPINGS = {
"TT_KSamplerAdvanced": TT_KSamplerAdvanced,
"TT_KSamplerContext": TT_KSamplerContext,
"TT_KSamplerTwoStage": TT_KSamplerTwoStage,
"TT_KSamplerGuided": TT_KSamplerGuided,
# Text Encoder
"TT_ClipTextEncodeFlux": TT_ClipTextEncodeFlux,
"TT_ClipTextEncodeFluxContext": TT_ClipTextEncodeFluxContext,
"TT_ClipTextEncodeSdxl": TT_ClipTextEncodeSdxl,
"TT_ClipTextEncodeSdxlContext": TT_ClipTextEncodeSdxlContext,
"TT_ClipTextEncodeFlux2": TT_ClipTextEncodeFlux2,
"TT_ClipTextEncodeFlux2Context": TT_ClipTextEncodeFlux2Context,
# VAE
"TT_VaeDecodeContext": TT_VaeDecodeContext,
"TT_VaeDecodeTiled": TT_VaeDecodeTiled,
@@ -134,6 +142,8 @@ NODE_CLASS_MAPPINGS = {
"TT_FluxWorkflowSettingsAdvanced": TT_FluxWorkflowSettingsAdvanced,
"TT_SdxlWorkflowSettings": TT_SdxlWorkflowSettings,
"TT_SdxlWorkflowSettingsAdvanced": TT_SdxlWorkflowSettingsAdvanced,
"TT_Flux2WorkflowSettings": TT_Flux2WorkflowSettings,
"TT_Flux2WorkflowSettingsAdvanced": TT_Flux2WorkflowSettingsAdvanced,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -170,11 +180,14 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"TT_KSamplerAdvanced": "TT KSampler (Advanced)",
"TT_KSamplerContext": "TT KSampler (Context)",
"TT_KSamplerTwoStage": "TT KSampler (Two Stages)",
"TT_KSamplerGuided": "TT KSampler (Guided)",
# Text Encoder
"TT_ClipTextEncodeFlux": "TT CLIP Text Encode FLUX",
"TT_ClipTextEncodeFluxContext": "TT CLIP Text Encode FLUX (Context)",
"TT_ClipTextEncodeSdxl": "TT CLIP Text Encode SDXL",
"TT_ClipTextEncodeSdxlContext": "TT CLIP Text Encode SDXL (Context)",
"TT_ClipTextEncodeFlux2": "TT CLIP Text Encode FLUX2",
"TT_ClipTextEncodeFlux2Context": "TT CLIP Text Encode FLUX2 (Context)",
# VAE
"TT_VaeDecodeContext": "TT VAE Decode (Context)",
"TT_VaeDecodeTiled": "TT VAE Decode (Tiled)",
@@ -185,4 +198,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"TT_FluxWorkflowSettingsAdvanced": "TT FLUX Workflow Settings (Advanced)",
"TT_SdxlWorkflowSettings": "TT SDXL Workflow Settings",
"TT_SdxlWorkflowSettingsAdvanced": "TT SDXL Workflow Settings (Advanced)",
"TT_Flux2WorkflowSettings": "TT FLUX2 Workflow Settings",
"TT_Flux2WorkflowSettingsAdvanced": "TT FLUX2 Workflow Settings (Advanced)",
}
+41 -5
View File
@@ -1,6 +1,6 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.samplers
import comfy.samplers as S
CONTEXT_FIELDS = [
# input_name, return_name, field_type
@@ -13,14 +13,18 @@ CONTEXT_FIELDS = [
("negative", "NEGATIVE", "CONDITIONING"),
("image", "IMAGE", "IMAGE"),
("mask", "MASK", "MASK"),
("guider", "GUIDER", "GUIDER"),
("control_net", "CONTROL_NET", "CONTROL_NET"),
("workflow_config", "WORKFLOW_CONFIG", "TT_WORKFLOW_CONFIG",),
("seed", "SEED", "INT"),
("steps", "STEPS", "INT"),
("cfg", "CFG", "FLOAT"),
("sampler_name", "SAMPLER_NAME", comfy.samplers.KSampler.SAMPLERS),
("scheduler", "SCHEDULER", comfy.samplers.KSampler.SCHEDULERS),
("sampler_name", "SAMPLER_NAME", S.KSampler.SAMPLERS),
("sampler", "SAMPLER", "SAMPLER"),
("scheduler", "SCHEDULER", S.KSampler.SCHEDULERS),
("guidance", "GUIDANCE", "FLOAT"),
("prompt", "PROMPT", "STRING"),
("lora_triggers", "LORA_TRIGGERS", "STRING"),
("clip_l_positive", "CLIP_L_POSITIVE", "STRING"),
("clip_g_positive", "CLIP_L_POSITIVE", "STRING"),
("t5xxl_positive", "T5XXL_POSITIVE", "STRING"),
@@ -93,6 +97,28 @@ CONTEXTS = {
"height",
)
},
"big_flux2": {
"optional": (
"context",
"workflow_config",
"model",
"clip",
"vae",
"latent",
"image",
"seed",
"steps",
"cfg",
"sampler_name",
"sampler",
"scheduler",
"guidance",
"prompt",
"lora_triggers",
"width",
"height",
)
},
"big_sdxl": {
"optional": (
"context",
@@ -130,7 +156,7 @@ CONTEXTS = {
"context",
)
},
"set_image":{
"set_image": {
"required": (
"context",
"image",
@@ -139,7 +165,7 @@ CONTEXTS = {
"context",
)
},
"set_latent":{
"set_latent": {
"required": (
"context",
"latent",
@@ -148,6 +174,15 @@ CONTEXTS = {
"context",
)
},
"set_guider": {
"required": (
"context",
"guider",
),
"return": (
"context",
)
},
}
FORCE_INPUT_TYPES = {
@@ -162,6 +197,7 @@ WORKFOW_SETTINGS_KEYS = [
"steps",
"cfg",
"sampler_name",
"sampler",
"scheduler",
"pos_a_score",
"clip_l_positive",
+18
View File
@@ -0,0 +1,18 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
from .context_helpers import create_context_schema
class TT_ContextSetGuider:
INPUT_TYPES_DICT, RETURN_TYPES, RETURN_NAMES = create_context_schema("set_guider")
FUNCTION = "process_context"
CATEGORY = "TenserTensor/Context"
@classmethod
def INPUT_TYPES(cls):
return cls.INPUT_TYPES_DICT
def process_context(self, context, guider):
context["guider"] = guider
return (context,)
+2
View File
@@ -16,9 +16,11 @@ FORMAT_EXT = {
"WEBP": ".webp",
}
class InvalidSavePathError(Exception):
pass
def _build_filename(save_path, filename_mask):
count = sum(1 for f in os.scandir(save_path) if fnmatch.fnmatch(f.name, filename_mask)) + 1
+14 -96
View File
@@ -1,40 +1,12 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import torch
from .latent_helpers import create_empty_latent
ASPECT_RATIOS = [
"1:1",
"4:3",
"3:2",
"16:9",
"21:9"
]
MEGAPIXELS = [
"0.25 MP",
"0.5 MP",
"1 MP",
"2 MP",
"4 MP",
"8 MP"
]
ORIENTATIONS = [
"landscape",
"portrait"
]
MODEL_TYPES = [
"FLUX1.D",
"FLUX2.D",
"SDXL"
]
CLIP_MULTIPLIERS = [
"1x",
"2x",
"4x"
]
ASPECT_RATIOS = ["1:1", "4:3", "3:2", "16:9", "21:9"]
MEGAPIXELS = ["0.25 MP", "0.5 MP", "1 MP", "2 MP", "4 MP", "8 MP"]
ORIENTATIONS = ["landscape", "portrait"]
MODEL_TYPES = ["FLUX1.D", "FLUX2.D", "SDXL"]
CLIP_MULTIPLIERS = ["1x", "2x", "4x"]
class TT_LatentFactory:
@@ -42,75 +14,21 @@ class TT_LatentFactory:
def INPUT_TYPES(cls):
return {
"required": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff},),
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff},),
"aspect_ratio": (ASPECT_RATIOS,),
"megapixels": (MEGAPIXELS,),
"orientation": (ORIENTATIONS,),
"model_type": (MODEL_TYPES,),
"batch_size": ("INT", {"default": 1, "min": 1, "max": 64}),
"clip_multiplier": (CLIP_MULTIPLIERS,)
"batch_size": ("INT", {"default": 1, "min": 1, "max": 64, "advanced": True},),
"clip_multiplier": (CLIP_MULTIPLIERS, {"advanced": True},)
}
}
RETURN_TYPES = ("LATENT", "INT", "INT", "INT", "INT", "INT")
RETURN_NAMES = ("LATENT", "SEED", "WIDTH", "HEIGHT", "TARGET_WIDTH", "TARGET_HEIGHT")
RETURN_TYPES = ("LATENT", "NOISE", "INT", "INT", "INT", "INT", "INT", "INT")
RETURN_NAMES = ("LATENT", "RANDOM_NOISE", "SEED", "NOISE_SEED", "WIDTH", "HEIGHT", "TARGET_WIDTH", "TARGET_HEIGHT")
FUNCTION = "create_latent"
CATEGORY = "TenserTensor/Latent"
def calculate_dimensions(self, total_pixels, ratio_w, ratio_h):
import math
height = math.sqrt(total_pixels * ratio_h / ratio_w)
width = height * ratio_w / ratio_h
width = int(width)
height = int(height)
width = round(width / 64) * 64
height = round(height / 64) * 64
return width, height
def create_latent(self, seed, aspect_ratio, megapixels, orientation, model_type, batch_size, clip_multiplier):
mp_value = float(megapixels.split()[0])
total_pixels = int(mp_value * 1_000_000)
ratio_parts = aspect_ratio.split(':')
ratio_w = int(ratio_parts[0])
ratio_h = int(ratio_parts[1])
if orientation == "portrait":
ratio_w, ratio_h = ratio_h, ratio_w
width, height = self.calculate_dimensions(total_pixels, ratio_w, ratio_h)
multiplier = int(clip_multiplier.replace('x', ''))
clip_width = width * multiplier
clip_height = height * multiplier
match model_type:
case "FLUX1.D":
scale_factor = 8
channels = 16
case "FLUX2.D":
scale_factor = 16
channels = 128
case "SDXL":
scale_factor = 8
channels = 4
latent_width = width // scale_factor
latent_height = height // scale_factor
generator = torch.Generator().manual_seed(seed)
latent = torch.randn(
batch_size,
channels,
latent_height,
latent_width,
generator=generator
)
latent_dict = {"samples": latent}
return (latent_dict, seed, width, height, clip_width, clip_height)
def create_latent(self, **kwargs):
return create_empty_latent(**kwargs)
+73
View File
@@ -0,0 +1,73 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.sample as S
import torch
class RandomNoise:
def __init__(self, seed):
self.seed = seed
def generate_noise(self, input_latent):
latent_image = input_latent["samples"]
batch_idx = input_latent["batch_index"] if "batch_index" in input_latent else None
return S.prepare_noise(latent_image, self.seed, batch_idx)
def _calculate_dimensions(total_pixels, ratio_w, ratio_h):
import math
height = math.sqrt(total_pixels * ratio_h / ratio_w)
width = height * ratio_w / ratio_h
width = int(width)
height = int(height)
width = round(width / 64) * 64
height = round(height / 64) * 64
return width, height
def create_empty_latent(seed, noise_seed, aspect_ratio, megapixels, orientation, model_type, batch_size, clip_multiplier):
mp_value = float(megapixels.split()[0])
total_pixels = int(mp_value * 1_000_000)
ratio_parts = aspect_ratio.split(':')
ratio_w = int(ratio_parts[0])
ratio_h = int(ratio_parts[1])
if orientation == "portrait":
ratio_w, ratio_h = ratio_h, ratio_w
width, height = _calculate_dimensions(total_pixels, ratio_w, ratio_h)
multiplier = int(clip_multiplier.replace('x', ''))
clip_width = width * multiplier
clip_height = height * multiplier
match model_type:
case "FLUX1.D":
scale_factor = 8
channels = 16
case "FLUX2.D":
scale_factor = 16
channels = 128
case "SDXL":
scale_factor = 8
channels = 4
latent_width = width // scale_factor
latent_height = height // scale_factor
generator = torch.Generator().manual_seed(seed)
latent = torch.randn(
batch_size,
channels,
latent_height,
latent_width,
generator=generator
)
latent_dict = {"samples": latent}
noise = RandomNoise(noise_seed)
return (latent_dict, noise, seed, noise_seed, width, height, clip_width, clip_height,)
@@ -196,6 +196,7 @@ def solarize(image, thresholds=0.5, additions=None):
return kornia.enhance.solarize(timage, thresholds, additions).movedim(1, -1)
def enhance_image(
timage,
brightness_factor,
@@ -26,7 +26,6 @@ class TT_QuickImageUpscaler():
FUNCTION = "upscale_image"
CATEGORY = "TenserTensor/Postproduction"
def upscale_image(self, image, device, upscale_model_name, tile, overlap):
timage = image.clone()
+1 -1
View File
@@ -23,7 +23,7 @@ class TT_KSampler():
}
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("LATENT",)
RETURN_NAMES = ("SAMPLES",)
FUNCTION = "execute_sampling"
CATEGORY = "TenserTensor/Sampling"
+1 -1
View File
@@ -28,7 +28,7 @@ class TT_KSamplerAdvanced():
}
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("LATENT",)
RETURN_NAMES = ("SAMPLES",)
FUNCTION = "execute_sampling"
CATEGORY = "TenserTensor/Sampling"
+1 -1
View File
@@ -13,7 +13,7 @@ class TT_KSamplerContext():
}
RETURN_TYPES = ("TT_CONTEXT", "LATENT",)
RETURN_NAMES = ("CONTEXT", "LATENT",)
RETURN_NAMES = ("CONTEXT", "SAMPLES",)
FUNCTION = "execute_sampling"
CATEGORY = "TenserTensor/Sampling"
+25
View File
@@ -0,0 +1,25 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
from .ksampler_helpers import guided_sample_latents
class TT_KSamplerGuided:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"latent": ("LATENT",),
"guider": ("GUIDER",),
"sigmas": ("SIGMAS",),
"sampler": ("SAMPLER",),
"random_noise": ("NOISE",),
}
}
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("SAMPLES",)
FUNCTION = "execute_sampling"
CATEGORY = "TenserTensor/Sampling"
def execute_sampling(self, latent, guider, sigmas, sampler, random_noise):
return (guided_sample_latents(latent, guider, sigmas, sampler, random_noise),)
+39 -2
View File
@@ -1,8 +1,9 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy
import latent_preview
import latent_preview as LP
import torch
from comfy import sample as S, utils as U, model_management as MM, nested_tensor as NT
def sample_latents(**kwargs):
@@ -26,7 +27,7 @@ def sample_latents(**kwargs):
else:
noise = torch.zeros(latent_img.size(), dtype=latent_img.dtype, layout=latent_img.layout, device="cpu")
callback = latent_preview.prepare_callback(model, steps)
callback = LP.prepare_callback(model, steps)
args = {
"model": model,
@@ -55,3 +56,39 @@ def sample_latents(**kwargs):
retval["samples"] = samples
return retval
def guided_sample_latents(latent, guider, sigmas, sampler, random_noise):
model_patcher = guider.model_patcher
latent_dict = latent.copy()
latent_tensor = S.fix_empty_latent_channels(model_patcher, latent_dict["samples"], latent.get("downscale_ratio_spacial", None))
latent_dict["samples"] = latent_tensor
noise_mask = latent.get("noise_mask", None)
interim_buffer = {}
callback = LP.prepare_callback(model_patcher, sigmas.shape[-1] - 1, interim_buffer)
disable_pbar = not U.PROGRESS_BAR_ENABLED
samples = guider.sample(
random_noise.generate_noise(latent),
latent_tensor,
sampler,
sigmas,
denoise_mask=noise_mask,
callback=callback,
disable_pbar=disable_pbar,
seed=random_noise.seed
).to(MM.intermediate_device())
retval = latent_dict.copy()
retval.pop("downscale_ratio_spacial", None)
retval["samples"] = samples
if "x0" in interim_buffer:
clean_latent = guider.model_patcher.model.process_latent_out(interim_buffer["x0"].cpu())
if samples.is_nested:
clean_latent = NT.NestedTensor(U.unpack_latents(clean_latent, [t.shape for t in samples.unbind()]))
retval["samples"] = clean_latent
return retval
+1 -1
View File
@@ -28,7 +28,7 @@ class TT_KSamplerTwoStage():
}
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("LATENT",)
RETURN_NAMES = ("SAMPLES",)
FUNCTION = "execute_sampling"
CATEGORY = "TenserTensor/Sampling"
+6 -18
View File
@@ -9,22 +9,10 @@ class TT_ClipTextEncodeFlux():
return {
"required": {
"clip": ("CLIP",),
"clip_l_positive": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}
),
"t5xxl_positive": (
"STRING",
{"multiline": True, "placeholder": "T5XXL Positive", "dynamicPrompts": True}
),
"clip_l_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}
),
"t5xxl_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}
),
"clip_l_positive": ("STRING", {"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}),
"t5xxl_positive": ("STRING", {"multiline": True, "placeholder": "T5XXL Positive", "dynamicPrompts": True}),
"clip_l_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}),
"t5xxl_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}),
"guidance": ("FLOAT", {"default": 3.5, "min": 1.0, "max": 10.0, "step": 0.1})
}
}
@@ -34,5 +22,5 @@ class TT_ClipTextEncodeFlux():
FUNCTION = "execute"
CATEGORY = "TenserTensor/Text Encoders/FLUX"
def execute(self, clip, clip_l_positive, t5xxl_positive, clip_l_negative, t5xxl_negative, guidance):
return encode_prompts_flux(clip, clip_l_positive, t5xxl_positive, clip_l_negative, t5xxl_negative, guidance)
def execute(self, **kwargs):
return encode_prompts_flux(**kwargs)
@@ -0,0 +1,25 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
from .encoder_helpers import encode_prompts_flux2
class TT_ClipTextEncodeFlux2():
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("MODEL",),
"clip": ("CLIP",),
"prompt": ("STRING", {"multiline": True, "placeholder": "Prompt", "dynamicPrompts": True}),
"lora_triggers": ("STRING", {"multiline": True, "placeholder": "LoRA Triggers", "dynamicPrompts": True}),
"guidance": ("FLOAT", {"default": 3.5, "min": 1.0, "max": 10.0, "step": 0.1})
}
}
RETURN_TYPES = ("GUIDER",)
RETURN_NAMES = ("GUIDER",)
FUNCTION = "execute"
CATEGORY = "TenserTensor/Text Encoders/FLUX2"
def execute(self, model, clip, prompt, lora_triggers, guidance):
return encode_prompts_flux2(model, clip, prompt, lora_triggers, guidance)
@@ -0,0 +1,41 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
from .encoder_helpers import encode_prompts_flux2
class TT_ClipTextEncodeFlux2Context():
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"context": ("TT_CONTEXT",)
}
}
RETURN_TYPES = ("TT_CONTEXT", "GUIDER",)
RETURN_NAMES = ("CONTEXT", "GUIDER",)
FUNCTION = "execute"
CATEGORY = "TenserTensor/Text Encoders/FLUX2"
def execute(self, context):
model = context.get("model", None)
clip = context.get("clip", None)
config = context.get("workflow_config", None)
if model is None:
raise ValueError("Model is required for text encoder")
if clip is None:
raise ValueError("CLIP is required for text encoder")
if config is None:
raise ValueError("Workflow Config is required for text encoder")
guider = encode_prompts_flux2(
model,
clip,
config.get("prompt", None),
config.get("lora_triggers", None),
config.get("guidance", None),
)
context["guider"] = guider
return (context, guider,)
+11 -25
View File
@@ -1,6 +1,6 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import nodes
from nodes import MAX_RESOLUTION
from .encoder_helpers import encode_prompts_sdxl
@@ -10,28 +10,16 @@ class TT_ClipTextEncodeSdxl():
return {
"required": {
"clip": ("CLIP",),
"clip_l_positive": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}
),
"clip_g_positive": (
"STRING",
{"multiline": True, "placeholder": "CLIP_G Positive", "dynamicPrompts": True}
),
"clip_l_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}
),
"clip_g_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_G Negative", "dynamicPrompts": True}
),
"clip_l_positive": ("STRING", {"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}),
"clip_g_positive": ("STRING", {"multiline": True, "placeholder": "CLIP_G Positive", "dynamicPrompts": True}),
"clip_l_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}),
"clip_g_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_G Negative", "dynamicPrompts": True}),
"ascore_positive": ("FLOAT", {"default": 9.0, "min": 0.0, "max": 1000.0, "step": 0.1}),
"ascore_negative": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 1000.0, "step": 0.1}),
"width": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"height": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"target_width": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"target_height": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"width": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
"height": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
"target_width": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
"target_height": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
}
}
@@ -40,7 +28,5 @@ class TT_ClipTextEncodeSdxl():
FUNCTION = "execute"
CATEGORY = "TenserTensor/Text Encoders/SDXL"
def execute(self, clip, clip_l_positive, clip_g_positive, clip_l_negative, clip_g_negative,
ascore_positive, ascore_negative, width, height, target_width, target_height):
return encode_prompts_sdxl(clip, clip_l_positive, clip_g_positive, clip_l_negative, clip_g_negative,
ascore_positive, ascore_negative, width, height, target_width, target_height)
def execute(self, **kwargs):
return encode_prompts_sdxl(**kwargs)
+41 -15
View File
@@ -1,25 +1,18 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
def encode_prompts_flux(clip, clip_l_positive, t5xxl_positive, clip_l_negative, t5xxl_negative, guidance):
positive_tokens = clip.tokenize(clip_l_positive)
positive_tokens["t5xxl"] = clip.tokenize(t5xxl_positive)["t5xxl"]
negative_tokens = clip.tokenize(clip_l_negative)
negative_tokens["t5xxl"] = clip.tokenize(t5xxl_negative)["t5xxl"]
import comfy.samplers as S
return (
clip.encode_from_tokens_scheduled(
positive_tokens,
add_dict={"guidance": guidance, }
),
clip.encode_from_tokens_scheduled(
negative_tokens,
add_dict={"guidance": guidance, }
)
)
class Guider_Basic(S.CFGGuider):
def set_conds(self, positive):
self.inner_set_conds({"positive": positive})
def encode_prompts_sdxl(clip, clip_l_positive, clip_g_positive, clip_l_negative, clip_g_negative,
ascore_positive, ascore_negative, width, height, target_width, target_height):
if clip is None:
raise ValueError("ERROR: CLIP is required for text encoder")
positive_tokens = clip.tokenize(clip_g_positive)
positive_tokens["l"] = clip.tokenize(clip_l_positive)["l"]
negative_tokens = clip.tokenize(clip_g_negative)
@@ -47,3 +40,36 @@ def encode_prompts_sdxl(clip, clip_l_positive, clip_g_positive, clip_l_negative,
}
)
)
def encode_prompts_flux(clip, clip_l_positive, t5xxl_positive, clip_l_negative, t5xxl_negative, guidance):
if clip is None:
raise ValueError("ERROR: CLIP is required for text encoder")
positive_tokens = clip.tokenize(clip_l_positive)
positive_tokens["t5xxl"] = clip.tokenize(t5xxl_positive)["t5xxl"]
negative_tokens = clip.tokenize(clip_l_negative)
negative_tokens["t5xxl"] = clip.tokenize(t5xxl_negative)["t5xxl"]
return (
clip.encode_from_tokens_scheduled(
positive_tokens,
add_dict={"guidance": guidance, }
),
clip.encode_from_tokens_scheduled(
negative_tokens,
add_dict={"guidance": guidance, }
)
)
def encode_prompts_flux2(model, clip, prompt, lora_triggers, guidance):
if clip is None:
raise ValueError("ERROR: CLIP is required for text encoder")
tokens = clip.tokenize(f"{lora_triggers} {prompt}")
conditioning = clip.encode_from_tokens_scheduled(tokens, add_dict={"guidance": guidance, })
guider = Guider_Basic(model)
guider.set_conds(conditioning)
return (guider,)
-1
View File
@@ -1,2 +1 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
+67
View File
@@ -0,0 +1,67 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.samplers as S
from nodes import MAX_RESOLUTION
from .workflow_settings_helpers import get_schedule
class TT_Flux2WorkflowSettings:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 25, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 1.5, "min": 0.0, "max": 100.0, "step": 0.1}),
"sampler_name": (S.KSampler.SAMPLERS,),
"width": ("INT", {"default": 1024, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
"height": ("INT", {"default": 1024, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
"guidance": ("FLOAT", {"default": 3.5, "min": 1.0, "max": 10.0, "step": 0.1})
}
}
RETURN_TYPES = (
"TT_WORKFLOW_CONFIG",
"INT",
"INT",
"FLOAT",
"SAMPLER",
S.KSampler.SAMPLERS,
"SIGMAS",
"INT",
"INT",
"FLOAT"
)
RETURN_NAMES = (
"WORKFLOW_CONFIG",
"SEED",
"STEPS",
"CFG",
"SAMPLER",
"SAMPLER_NAME",
"SCHEDULER",
"WIDTH",
"HEIGHT",
"GUIDANCE"
)
FUNCTION = "get_settings"
CATEGORY = "TenserTensor/Workflow/FLUX2"
def get_settings(self, seed, steps, cfg, sampler_name, width, height, guidance):
scheduler = get_schedule(seed, width, height)
sampler = S.sampler_object(sampler_name)
workflow_config = {
"seed": seed,
"steps": steps,
"cfg": cfg,
"sampler": sampler,
"sampler_name": sampler_name,
"scheduler": scheduler,
"width": width,
"height": height,
"guidance": guidance
}
return (workflow_config, seed, steps, cfg, sampler, sampler_name, scheduler, width, height, guidance)
@@ -0,0 +1,72 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.samplers as S
from nodes import MAX_RESOLUTION
from .workflow_settings_helpers import get_schedule
class TT_Flux2WorkflowSettingsAdvanced:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 25, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 1.5, "min": 0.0, "max": 100.0, "step": 0.1}),
"sampler_name": (S.KSampler.SAMPLERS,),
"width": ("INT", {"default": 1024, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
"height": ("INT", {"default": 1024, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
"prompt": ("STRING", {"multiline": True, "placeholder": "Prompt", "dynamicPrompts": True}),
"lora_triggers": ("STRING", {"multiline": True, "placeholder": "LoRA Triggers", "dynamicPrompts": True}),
"guidance": ("FLOAT", {"default": 3.5, "min": 1.0, "max": 10.0, "step": 0.1})
}
}
RETURN_TYPES = (
"TT_WORKFLOW_CONFIG",
"INT",
"INT",
"FLOAT",
"SAMPLER",
S.KSampler.SAMPLERS,
"SIGMAS",
"INT",
"INT",
"FLOAT"
)
RETURN_NAMES = (
"WORKFLOW_CONFIG",
"SEED",
"STEPS",
"CFG",
"SAMPLER",
"SAMPLER_NAME",
"SCHEDULER",
"WIDTH",
"HEIGHT",
"PROMPT",
"GUIDANCE"
)
FUNCTION = "get_settings"
CATEGORY = "TenserTensor/Workflow/FLUX2"
def get_settings(self, seed, steps, cfg, sampler_name, width, height, prompt, lora_triggers, guidance):
scheduler = get_schedule(seed, width, height)
sampler = S.sampler_object(sampler_name)
workflow_config = {
"seed": seed,
"steps": steps,
"cfg": cfg,
"sampler": sampler,
"sampler_name": sampler_name,
"scheduler": scheduler,
"width": width,
"height": height,
"prompt": prompt,
"lora_triggers": lora_triggers,
"guidance": guidance
}
return (workflow_config, seed, steps, cfg, sampler, scheduler, width, height, prompt, lora_triggers, guidance)
@@ -1,6 +1,6 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.samplers
import comfy.samplers as S
class TT_FluxWorkflowSettings:
@@ -11,14 +11,30 @@ class TT_FluxWorkflowSettings:
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 25, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 1.5, "min": 0.0, "max": 100.0, "step": 0.1}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
"sampler_name": (S.KSampler.SAMPLERS,),
"scheduler": (S.KSampler.SCHEDULERS,),
"guidance": ("FLOAT", {"default": 3.5, "min": 1.0, "max": 10.0, "step": 0.1})
}
}
RETURN_TYPES = ("TT_WORKFLOW_CONFIG", "INT", "INT", "FLOAT", "STRING", "STRING", "FLOAT")
RETURN_NAMES = ("WORKFLOW_CONFIG", "SEED", "STEPS", "CFG", "SAMPLER_NAME", "SCHEDULER", "GUIDANCE")
RETURN_TYPES = (
"TT_WORKFLOW_CONFIG",
"INT",
"INT",
"FLOAT",
S.KSampler.SAMPLERS,
S.KSampler.SCHEDULERS,
"FLOAT"
)
RETURN_NAMES = (
"WORKFLOW_CONFIG",
"SEED",
"STEPS",
"CFG",
"SAMPLER_NAME",
"SCHEDULER",
"GUIDANCE"
)
FUNCTION = "get_settings"
CATEGORY = "TenserTensor/Workflow/FLUX"
@@ -1,6 +1,6 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.samplers
import comfy.samplers as S
class TT_FluxWorkflowSettingsAdvanced:
@@ -11,24 +11,12 @@ class TT_FluxWorkflowSettingsAdvanced:
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 25, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 1.5, "min": 0.0, "max": 100.0, "step": 0.1}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
"clip_l_positive": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}
),
"t5xxl_positive": (
"STRING",
{"multiline": True, "placeholder": "T5XXL Positive", "dynamicPrompts": True}
),
"clip_l_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}
),
"t5xxl_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}
),
"sampler_name": (S.KSampler.SAMPLERS,),
"scheduler": (S.KSampler.SCHEDULERS,),
"clip_l_positive": ("STRING", {"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}),
"t5xxl_positive": ("STRING", {"multiline": True, "placeholder": "T5XXL Positive", "dynamicPrompts": True}),
"clip_l_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}),
"t5xxl_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}),
"guidance": ("FLOAT", {"default": 3.5, "min": 1.0, "max": 10.0, "step": 0.1})
}
}
@@ -38,8 +26,8 @@ class TT_FluxWorkflowSettingsAdvanced:
"INT",
"INT",
"FLOAT",
"STRING",
"STRING",
S.KSampler.SAMPLERS,
S.KSampler.SCHEDULERS,
"STRING",
"STRING",
"STRING",
@@ -62,8 +50,7 @@ class TT_FluxWorkflowSettingsAdvanced:
FUNCTION = "get_settings"
CATEGORY = "TenserTensor/Workflow/FLUX"
def get_settings(self, seed, steps, cfg, sampler_name, scheduler, guidance, clip_l_positive, t5xxl_positive,
clip_l_negative, t5xxl_negative):
def get_settings(self, seed, steps, cfg, sampler_name, scheduler, guidance, clip_l_positive, t5xxl_positive, clip_l_negative, t5xxl_negative):
workflow_config = {
"seed": seed,
"steps": steps,
@@ -77,5 +64,5 @@ class TT_FluxWorkflowSettingsAdvanced:
"guidance": guidance
}
return (workflow_config, seed, steps, cfg, sampler_name, scheduler, guidance,
clip_l_positive, t5xxl_positive, clip_l_negative, t5xxl_negative)
return (workflow_config, seed, steps, cfg, sampler_name, scheduler, guidance, clip_l_positive, t5xxl_positive, clip_l_negative,
t5xxl_negative)
@@ -1,6 +1,6 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.samplers
import comfy.samplers as S
class TT_SdxlWorkflowSettings:
@@ -11,8 +11,8 @@ class TT_SdxlWorkflowSettings:
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 30, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0, "step": 0.1}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
"sampler_name": (S.KSampler.SAMPLERS,),
"scheduler": (S.KSampler.SCHEDULERS,),
}
}
@@ -21,8 +21,8 @@ class TT_SdxlWorkflowSettings:
"INT",
"INT",
"FLOAT",
comfy.samplers.KSampler.SAMPLERS,
comfy.samplers.KSampler.SCHEDULERS,
S.KSampler.SAMPLERS,
S.KSampler.SCHEDULERS,
)
RETURN_NAMES = (
@@ -1,8 +1,8 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import comfy.samplers
import comfy.samplers as S
import nodes
from nodes import MAX_RESOLUTION
class TT_SdxlWorkflowSettingsAdvanced:
@@ -13,30 +13,18 @@ class TT_SdxlWorkflowSettingsAdvanced:
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 30, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0, "step": 0.1}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
"clip_l_positive": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}
),
"clip_g_positive": (
"STRING",
{"multiline": True, "placeholder": "CLIP_G Positive", "dynamicPrompts": True}
),
"clip_l_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}
),
"clip_g_negative": (
"STRING",
{"multiline": True, "placeholder": "CLIP_G Negative", "dynamicPrompts": True}
),
"sampler_name": (S.KSampler.SAMPLERS,),
"scheduler": (S.KSampler.SCHEDULERS,),
"clip_l_positive": ("STRING", {"multiline": True, "placeholder": "CLIP_L Positive", "dynamicPrompts": True}),
"clip_g_positive": ("STRING", {"multiline": True, "placeholder": "CLIP_G Positive", "dynamicPrompts": True}),
"clip_l_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_L Negative", "dynamicPrompts": True}),
"clip_g_negative": ("STRING", {"multiline": True, "placeholder": "CLIP_G Negative", "dynamicPrompts": True}),
"ascore_positive": ("FLOAT", {"default": 9.0, "min": 0.0, "max": 1000.0, "step": 0.1}),
"ascore_negative": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 1000.0, "step": 0.1}),
"width": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"height": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"target_width": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"target_height": ("INT", {"default": 512, "min": 0.0, "max": nodes.MAX_RESOLUTION}),
"width": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
"height": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
"target_width": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
"target_height": ("INT", {"default": 512, "min": 0.0, "max": MAX_RESOLUTION}),
}
}
@@ -45,8 +33,8 @@ class TT_SdxlWorkflowSettingsAdvanced:
"INT",
"INT",
"FLOAT",
"STRING",
"STRING",
S.KSampler.SAMPLERS,
S.KSampler.SCHEDULERS,
"STRING",
"STRING",
"STRING",
@@ -0,0 +1,36 @@
# (c) TenserTensor || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
import math
import torch
def _compute_empirical_mu(image_seq_len, num_steps):
a1, b1 = 8.73809524e-05, 1.89833333
a2, b2 = 0.00016927, 0.45666666
if image_seq_len > 4300:
mu = a2 * image_seq_len + b2
return float(mu)
m_200 = a2 * image_seq_len + b2
m_10 = a1 * image_seq_len + b1
a = (m_200 - m_10) / 190.0
b = m_200 - 200.0 * a
mu = a * num_steps + b
return float(mu)
def _generalized_time_snr_shift(t, mu, sigma):
return math.exp(mu) / (math.exp(mu) + (1 / t - 1) ** sigma)
def get_schedule(num_steps, width, height):
image_seq_len = (width * height / (16 * 16))
mu = _compute_empirical_mu(image_seq_len, num_steps)
timesteps = torch.linspace(1, 0, num_steps + 1)
timesteps = _generalized_time_snr_shift(timesteps, mu, 1.0)
return (timesteps,)
+2 -2
View File
@@ -1,8 +1,8 @@
# pyproject.toml
[project]
name = "ComfyUI-TenserTensor"
description = "A collection of custom nodes for ComfyUI that streamline common workflows. The main goal is to reduce visual clutter by combining frequently used node chains into single, convenient loaders."
version = "1.3.1"
description = "A collection of custom nodes for ComfyUI that streamline common workflows. Includes all-in-one model loaders (checkpoint, LoRA, CLIP, VAE), GGUF quantized model support (Flux, Flux.2, SDXL), workflow settings nodes, latent factory (aspect ratio, megapixel, Flux/SDXL), context nodes, CLIP text encoders, KSamplers, VAE decoders (basic, tiled, two-pass), and post-production tools (brightness, contrast, LUT color grading, film grain, upscaler)."
version = "1.4.0"
license = { file = "LICENSE" }
dependencies = ["gguf"]
+31
View File
@@ -0,0 +1,31 @@
## TT Clip Text Encode Flux2 *TT_ClipTextEncodeFlux2*
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
Text prompt encoder for FLUX2 models with LoRA trigger word support and built-in guidance configuration.
### Inputs
| Parameter | Type | Required | Description |
|-----------------|--------|----------|--------------------------------------------------------------|
| `model` | MODEL | Yes | Diffusion model |
| `clip` | CLIP | Yes | CLIP model for text encoding |
| `prompt` | STRING | Yes | Main generation prompt (multiline, supports dynamic prompts) |
| `lora_triggers` | STRING | Yes | LoRA trigger words (multiline, supports dynamic prompts) |
| `guidance` | FLOAT | Yes | Guidance scale for generation (1.0–10.0, default: 3.5) |
### Outputs
| Parameter | Type | Description |
|-----------|--------|----------------------------------------------------|
| `GUIDER` | GUIDER | Configured guider with encoded prompt and guidance |
### Usage
Encodes text prompts specifically for FLUX2 models. Enter your main prompt in `prompt` and any LoRA-specific trigger
words separately in `lora_triggers` — the node combines them internally. Adjust `guidance` to control how strongly
the model follows the prompt (3.5 is a good starting point for FLUX2).
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
+29
View File
@@ -0,0 +1,29 @@
## TT Clip Text Encode Flux2 (Context) *TT_ClipTextEncodeFlux2Context*
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
Context-based text prompt encoder for FLUX2 models. Extracts model, CLIP, and prompt configuration directly from a TT_CONTEXT object.
### Inputs
| Parameter | Type | Required | Description |
|-----------|------------|----------|---------------------------------------------------------------------------------------|
| `context` | TT_CONTEXT | Yes | Context object containing `model`, `clip`, and `workflow_config` with prompt settings |
### Outputs
| Parameter | Type | Description |
|-----------|------------|----------------------------------------------------|
| `CONTEXT` | TT_CONTEXT | Updated context with `guider` written back into it |
| `GUIDER` | GUIDER | Configured guider with encoded prompt and guidance |
### Usage
Context-aware version of the FLUX2 text encoder. Instead of individual inputs, it reads `model`, `clip`, and
`workflow_config` (containing `prompt`, `lora_triggers`, and `guidance`) from the passed `context`. The resulting
guider is both returned as an output and stored back into the context under the `guider` key. Raises an error if
any of the required fields are missing from the context.
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
+27
View File
@@ -0,0 +1,27 @@
## TT Context Set Guider *TT_ContextSetGuider*
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
Writes a GUIDER object into the TT_CONTEXT, making it available for downstream context-aware nodes.
### Inputs
| Parameter | Type | Required | Description |
|-----------|------------|----------|--------------------------------|
| `context` | TT_CONTEXT | Yes | Context object to update |
| `guider` | GUIDER | Yes | Guider to store in the context |
### Outputs
| Parameter | Type | Description |
|-----------|------------|-----------------------------------------------|
| `CONTEXT` | TT_CONTEXT | Updated context with `guider` written into it |
### Usage
Sets the `guider` field inside a TT_CONTEXT object. Use this node when you have a guider produced outside the
context pipeline and need to inject it back in for use by subsequent context-aware nodes.
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
+7 -7
View File
@@ -6,16 +6,16 @@ Adds or updates image in context object.
### Inputs
| Parameter | Type | Required | Description |
|-----------|------------|----------|--------------------------------|
| `context` | TT_CONTEXT | Yes | Context to update |
| `image` | IMAGE | Yes | Image to store in context |
| Parameter | Type | Required | Description |
|-----------|------------|----------|---------------------------|
| `context` | TT_CONTEXT | Yes | Context to update |
| `image` | IMAGE | Yes | Image to store in context |
### Outputs
| Parameter | Type | Description |
|-----------|------------|-------------------------|
| `CONTEXT` | TT_CONTEXT | Updated context object |
| Parameter | Type | Description |
|-----------|------------|------------------------|
| `CONTEXT` | TT_CONTEXT | Updated context object |
### Usage
+44
View File
@@ -0,0 +1,44 @@
## TT Flux2 Workflow Settings *TT_Flux2WorkflowSettings*
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
Central workflow configuration node for FLUX2 pipelines. Consolidates all sampling parameters into a single
TT_WORKFLOW_CONFIG object while also exposing each value as an individual output.
### Inputs
| Parameter | Type | Required | Description |
|----------------|--------|----------|---------------------------------------------------------------------------|
| `seed` | INT | Yes | Generation seed (0 – 18446744073709551615, default: 0) |
| `steps` | INT | Yes | Number of sampling steps (1–10000, default: 25) |
| `cfg` | FLOAT | Yes | CFG scale (0.0–100.0, default: 1.5) |
| `sampler_name` | STRING | Yes | Sampler algorithm (KSampler sampler list) |
| `width` | INT | Yes | Output image width in pixels (16–MAX_RESOLUTION, step: 8, default: 1024) |
| `height` | INT | Yes | Output image height in pixels (16–MAX_RESOLUTION, step: 8, default: 1024) |
| `guidance` | FLOAT | Yes | Guidance scale for text encoder (1.0–10.0, default: 3.5) |
### Outputs
| Parameter | Type | Description |
|-------------------|--------------------|------------------------------------------------------|
| `WORKFLOW_CONFIG` | TT_WORKFLOW_CONFIG | Full configuration object with all settings bundled |
| `SEED` | INT | Generation seed |
| `STEPS` | INT | Number of sampling steps |
| `CFG` | FLOAT | CFG scale |
| `SAMPLER` | SAMPLER | Instantiated sampler object |
| `SAMPLER_NAME` | STRING | Name of the selected sampler |
| `SCHEDULER` | SIGMAS | Computed sigma schedule based on seed and resolution |
| `WIDTH` | INT | Output image width |
| `HEIGHT` | INT | Output image height |
| `GUIDANCE` | FLOAT | Guidance scale |
### Usage
Use this node as the single source of truth for FLUX2 workflow parameters. The `WORKFLOW_CONFIG` output is
designed for context-aware nodes that consume a TT_WORKFLOW_CONFIG directly. Individual outputs are available
for connecting to standard ComfyUI nodes. The sigma schedule (`SCHEDULER`) is computed automatically from
the seed and resolution via `get_schedule`.
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
@@ -0,0 +1,47 @@
## TT Flux2 Workflow Settings (Advanced) *TT_Flux2WorkflowSettingsAdvanced*
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
Extended workflow configuration node for FLUX2 pipelines. Includes all parameters from the base version plus
built-in prompt and LoRA trigger fields, bundling everything into a single TT_WORKFLOW_CONFIG object.
### Inputs
| Parameter | Type | Required | Description |
|-----------------|--------|----------|---------------------------------------------------------------------------|
| `seed` | INT | Yes | Generation seed (0 – 18446744073709551615, default: 0) |
| `steps` | INT | Yes | Number of sampling steps (1–10000, default: 25) |
| `cfg` | FLOAT | Yes | CFG scale (0.0–100.0, default: 1.5) |
| `sampler_name` | STRING | Yes | Sampler algorithm (KSampler sampler list) |
| `width` | INT | Yes | Output image width in pixels (16–MAX_RESOLUTION, step: 8, default: 1024) |
| `height` | INT | Yes | Output image height in pixels (16–MAX_RESOLUTION, step: 8, default: 1024) |
| `prompt` | STRING | Yes | Main generation prompt (multiline, supports dynamic prompts) |
| `lora_triggers` | STRING | Yes | LoRA trigger words (multiline, supports dynamic prompts) |
| `guidance` | FLOAT | Yes | Guidance scale for text encoder (1.0–10.0, default: 3.5) |
### Outputs
| Parameter | Type | Description |
|-------------------|--------------------|------------------------------------------------------|
| `WORKFLOW_CONFIG` | TT_WORKFLOW_CONFIG | Full configuration object with all settings bundled |
| `SEED` | INT | Generation seed |
| `STEPS` | INT | Number of sampling steps |
| `CFG` | FLOAT | CFG scale |
| `SAMPLER` | SAMPLER | Instantiated sampler object |
| `SAMPLER_NAME` | STRING | Name of the selected sampler |
| `SCHEDULER` | SIGMAS | Computed sigma schedule based on seed and resolution |
| `WIDTH` | INT | Output image width |
| `HEIGHT` | INT | Output image height |
| `PROMPT` | STRING | Main generation prompt |
| `GUIDANCE` | FLOAT | Guidance scale |
### Usage
Advanced version of TT Flux2 Workflow Settings with `prompt` and `lora_triggers` fields included directly in the
node. Both are stored in the `WORKFLOW_CONFIG` output alongside all other sampling parameters, making this node
sufficient as the sole configuration source for full context-driven FLUX2 pipelines. Individual outputs remain
available for connecting to standard ComfyUI nodes.
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
+7 -6
View File
@@ -14,15 +14,16 @@ Load GGUF-quantized diffusion model (UNet), CLIP text encoder, and VAE in a sing
### Outputs
| Parameter | Type | Description |
|-----------|-------|-------------------------|
| `MODEL` | MODEL | Loaded diffusion model |
| `CLIP` | CLIP | Loaded text encoder |
| `VAE` | VAE | Loaded VAE decoder |
| Parameter | Type | Description |
|-----------|-------|------------------------|
| `MODEL` | MODEL | Loaded diffusion model |
| `CLIP` | CLIP | Loaded text encoder |
| `VAE` | VAE | Loaded VAE decoder |
### Usage
All-in-one loader for GGUF-quantized models. Place your `.gguf` diffusion model files in the `diffusion_models` or `unet` directory, CLIP files in `text_encoders` or `clip` directory. VAE can be any standard ComfyUI-compatible VAE file.
All-in-one loader for GGUF-quantized models. Place your `.gguf` diffusion model files in the `diffusion_models` or `unet` directory, CLIP files in
`text_encoders` or `clip` directory. VAE can be any standard ComfyUI-compatible VAE file.
---
+18 -16
View File
@@ -6,22 +6,22 @@ All-in-one loader for GGUF-quantized models with extended control over quantizat
### Inputs
| Parameter | Type | Required | Advanced | Description |
|-----------------------|---------|----------|----------|----------------------------------------------------------|
| `unet_name` | STRING | Yes | | GGUF UNet/diffusion model filename |
| Parameter | Type | Required | Advanced | Description |
|-----------------------|---------|----------|----------|------------------------------------------------------------------------|
| `unet_name` | STRING | Yes | | GGUF UNet/diffusion model filename |
| `dequant_dtype` | STRING | Yes | Yes | Data type for dequantization (default/target/float32/float16/bfloat16) |
| `patch_dtype` | STRING | Yes | Yes | Data type for weight patching |
| `apply_sampling` | BOOLEAN | Yes | | Enable/disable flux sampling shift patch |
| `base_sampling_shift` | FLOAT | Yes | Yes | Base shift value for flux sampling (default: 0.5) |
| `max_sampling_shift` | FLOAT | Yes | Yes | Maximum shift value for flux sampling (default: 1.15) |
| `sampling_width` | INT | Yes | Yes | Reference width for sampling shift calculation |
| `sampling_height` | INT | Yes | Yes | Reference height for sampling shift calculation |
| `lora_name_1..4` | STRING | Yes | Yes | LoRA filename (None to skip) |
| `strength_1..4` | FLOAT | Yes | Yes | LoRA blend strength (-10.0 to 10.0, default: 1.0) |
| `clip_name` | STRING | Yes | | GGUF CLIP text encoder filename |
| `vae_name` | STRING | Yes | | VAE model filename |
| `vae_device` | STRING | Yes | Yes | Device for VAE (default/cpu) |
| `vae_dtype` | STRING | Yes | Yes | Data type for VAE (bfloat16/float16/float32) |
| `patch_dtype` | STRING | Yes | Yes | Data type for weight patching |
| `apply_sampling` | BOOLEAN | Yes | | Enable/disable flux sampling shift patch |
| `base_sampling_shift` | FLOAT | Yes | Yes | Base shift value for flux sampling (default: 0.5) |
| `max_sampling_shift` | FLOAT | Yes | Yes | Maximum shift value for flux sampling (default: 1.15) |
| `sampling_width` | INT | Yes | Yes | Reference width for sampling shift calculation |
| `sampling_height` | INT | Yes | Yes | Reference height for sampling shift calculation |
| `lora_name_1..4` | STRING | Yes | Yes | LoRA filename (None to skip) |
| `strength_1..4` | FLOAT | Yes | Yes | LoRA blend strength (-10.0 to 10.0, default: 1.0) |
| `clip_name` | STRING | Yes | | GGUF CLIP text encoder filename |
| `vae_name` | STRING | Yes | | VAE model filename |
| `vae_device` | STRING | Yes | Yes | Device for VAE (default/cpu) |
| `vae_dtype` | STRING | Yes | Yes | Data type for VAE (bfloat16/float16/float32) |
### Outputs
@@ -33,7 +33,9 @@ All-in-one loader for GGUF-quantized models with extended control over quantizat
### Usage
Extended version of GGUF Models Loader. Supports up to 4 LoRAs loaded simultaneously. Enable `apply_sampling` to apply flux sampling shift patch — useful for improving quality at non-standard resolutions. Advanced parameters are hidden by default and can be revealed via the node's show/hide controls.
Extended version of GGUF Models Loader. Supports up to 4 LoRAs loaded simultaneously. Enable `apply_sampling` to apply flux sampling shift patch —
useful for improving quality at non-standard resolutions. Advanced parameters are hidden by default and can be revealed via the node's show/hide
controls.
---
+31
View File
@@ -0,0 +1,31 @@
## TT KSampler (Guided) *TT_KSamplerGuided*
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
Guided latent sampler that takes a pre-configured guider, sigmas, sampler, and noise to produce denoised latent samples.
### Inputs
| Parameter | Type | Required | Description |
|----------------|---------|----------|-----------------------------------------|
| `latent` | LATENT | Yes | Input latent to sample from |
| `guider` | GUIDER | Yes | Configured guider with encoded prompt |
| `sigmas` | SIGMAS | Yes | Sigma schedule for the sampling process |
| `sampler` | SAMPLER | Yes | Instantiated sampler object |
| `random_noise` | NOISE | Yes | Noise source for the diffusion process |
### Outputs
| Parameter | Type | Description |
|-----------|--------|-------------------------|
| `SAMPLES` | LATENT | Denoised latent samples |
### Usage
Runs guided diffusion sampling on the input latent. Connect a guider from a text encoder node, sigmas from a
scheduler, and a sampler from a workflow settings node. `random_noise` controls stochasticity — use a seeded
noise node for reproducible results.
---
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
+1 -1
View File
@@ -41,7 +41,7 @@ Extended post-production node with image enhancement, LUT color grading, film gr
| Parameter | Type | Description |
|-----------|-------|--------------------------------------|
| `IMAGE` | IMAGE | Processed image with applied effects |
| `IMAGE` | IMAGE | Processed image with applied effects |
### Usage