diff --git a/.gitignore b/.gitignore index 7795992..d108db7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ +__pycache__/ _tmp/ .idea/ +*.pyc +*.pyo diff --git a/__init__.py b/__init__.py index 7e0fb79..533ccf4 100644 --- a/__init__.py +++ b/__init__.py @@ -7,6 +7,10 @@ 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.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.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 @@ -26,6 +30,11 @@ NODE_CLASS_MAPPINGS = { # Loaders "TT_SdxlModelsLoader": TT_SdxlModelsLoader, "TT_SdxlModelsLoaderAdvanced": TT_SdxlModelsLoaderAdvanced, + # Samplers + "TT_KSampler": TT_KSampler, + "TT_KSamplerAdvanced": TT_KSamplerAdvanced, + "TT_KSamplerContext": TT_KSamplerContext, + "TT_KSamplerTwoStage": TT_KSamplerTwoStage, # Workflow "TT_FluxWorkflowSettings": TT_FluxWorkflowSettings, "TT_FluxWorkflowSettingsAdvanced": TT_FluxWorkflowSettingsAdvanced, @@ -47,6 +56,11 @@ NODE_DISPLAY_NAME_MAPPINGS = { # Loaders "TT_SdxlModelsLoader": "TT SDXL Models Loader", "TT_SdxlModelsLoaderAdvanced": "TT SDXL Models Loader (Advanced)", + # Samplers + "TT_KSampler": "TT KSampler", + "TT_KSamplerAdvanced": "TT KSampler (Advanced)", + "TT_KSamplerContext": "TT KSampler (Context)", + "TT_KSamplerTwoStage": "TT KSampler (Two Stages)", # Workflow "TT_FluxWorkflowSettings": "TT FLUX Workflow Settings", "TT_FluxWorkflowSettingsAdvanced": "TT FLUX Workflow Settings (Advanced)", diff --git a/nodes/sampling/ksampler.py b/nodes/sampling/ksampler.py new file mode 100644 index 0000000..9bb5470 --- /dev/null +++ b/nodes/sampling/ksampler.py @@ -0,0 +1,29 @@ +import comfy.samplers + +from .ksampler_helpers import sample_latents + + +class TT_KSampler(): + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "model": ("MODEL",), + "positive": ("CONDITIONING",), + "negative": ("CONDITIONING",), + "latent": ("LATENT",), + "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,), + } + } + + RETURN_TYPES = ("LATENT",) + RETURN_NAMES = ("LATENT",) + FUNCTION = "execute_sampling" + CATEGORY = "TenserTensor/Sampling" + + def execute_sampling(self, **kwargs): + return (sample_latents(**kwargs),) diff --git a/nodes/sampling/ksampler_advanced.py b/nodes/sampling/ksampler_advanced.py new file mode 100644 index 0000000..d4a09b6 --- /dev/null +++ b/nodes/sampling/ksampler_advanced.py @@ -0,0 +1,34 @@ +import comfy.samplers + +from .ksampler_helpers import sample_latents + + +class TT_KSamplerAdvanced(): + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "model": ("MODEL",), + "positive": ("CONDITIONING",), + "negative": ("CONDITIONING",), + "latent": ("LATENT",), + "add_noise": ("BOOLEAN", {"default": True, "label_on": "Enabled", "label_off": "Disabled"}), + "full_denoise": ("BOOLEAN", {"default": False, "label_on": "Enabled", "label_off": "Disabled"}), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "steps": ("INT", {"default": 25, "min": 1, "max": 10000}), + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "last_step": ("INT", {"default": 10000, "min": 0, "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,), + "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), + } + } + + RETURN_TYPES = ("LATENT",) + RETURN_NAMES = ("LATENT",) + FUNCTION = "execute_sampling" + CATEGORY = "TenserTensor/Sampling" + + def execute_sampling(self, **kwargs): + return (sample_latents(**kwargs),) diff --git a/nodes/sampling/ksampler_context.py b/nodes/sampling/ksampler_context.py new file mode 100644 index 0000000..b0dfe12 --- /dev/null +++ b/nodes/sampling/ksampler_context.py @@ -0,0 +1,44 @@ +from .ksampler_helpers import sample_latents + + +class TT_KSamplerContext(): + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "context": ("TT_CONTEXT",) + } + } + + RETURN_TYPES = ("TT_CONTEXT", "LATENT",) + RETURN_NAMES = ("CONTEXT", "LATENT",) + FUNCTION = "execute_sampling" + CATEGORY = "TenserTensor/Sampling" + + def execute_sampling(self, context): + model = context.get("model", None) + latent = context.get("latent", None) + config = context.get("workflow_config", None) + + if model is None: + raise ValueError("Model is required for sampling") + if latent is None: + raise ValueError("Latent image is required for sampling") + if config is None: + raise ValueError("Workflow Config is required for sampling") + + args = { + "model": model, + "positive": context.get("positive", None), + "negative": context.get("negative", None), + "latent": latent, + "seed": config.get("seed", 0), + "steps": config.get("steps", 0), + "cfg": config.get("cfg", 0), + "sampler_name": config.get("sampler_name", None), + "scheduler": config.get("scheduler", None), + } + + samples = sample_latents(**args) + + return (context, samples) diff --git a/nodes/sampling/ksampler_helpers.py b/nodes/sampling/ksampler_helpers.py new file mode 100644 index 0000000..5464a2f --- /dev/null +++ b/nodes/sampling/ksampler_helpers.py @@ -0,0 +1,55 @@ +import comfy +import latent_preview +import torch + + +def sample_latents(**kwargs): + model = kwargs.get("model", None) + latent = kwargs.get("latent", None) + + if model is None: + raise ValueError("Model is required for sampling") + if latent is None: + raise ValueError("Latent image is required for sampling") + + steps = kwargs.get("steps", 0) + seed = kwargs.get("seed", 0) + + latent_img = comfy.sample.fix_empty_latent_channels(model, latent["samples"]) + + add_noise = kwargs.get("add_noise", True) + if add_noise: + bat_idx = latent.get("batch_index", None) + noise = comfy.sample.prepare_noise(latent_img, seed, bat_idx) + else: + noise = torch.zeros(latent_img.size(), dtype=latent_img.dtype, layout=latent_img.layout, device="cpu") + + callback = latent_preview.prepare_callback(model, steps) + + args = { + "model": model, + "noise": noise, + "steps": steps, + "cfg": kwargs.get("cfg", 0), + "sampler_name": kwargs.get("sampler_name", None), + "scheduler": kwargs.get("scheduler", None), + "positive": kwargs.get("positive", None), + "negative": kwargs.get("negative", None), + "latent_image": latent_img, + "denoise": kwargs.get("denoise", 1.0), + "disable_noise": not add_noise, + "start_step": kwargs.get("start_step", 0), + "last_step": kwargs.get("last_step", 10000), + "force_full_denoise": kwargs.get("full_denoise", True), + "noise_mask": latent.get("noise_mask", None), + "disable_pbar": not comfy.utils.PROGRESS_BAR_ENABLED, + "callback": callback, + "seed": seed, + } + + samples = comfy.sample.sample(**args) + + retval = latent.copy() + retval["samples"] = samples + + return retval diff --git a/nodes/sampling/ksampler_two_stage.py b/nodes/sampling/ksampler_two_stage.py new file mode 100644 index 0000000..3d2e668 --- /dev/null +++ b/nodes/sampling/ksampler_two_stage.py @@ -0,0 +1,63 @@ +import comfy.samplers + +from .ksampler_helpers import sample_latents + + +class TT_KSamplerTwoStage(): + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "model": ("MODEL",), + "positive": ("CONDITIONING",), + "negative": ("CONDITIONING",), + "latent": ("LATENT",), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "cfg": ("FLOAT", {"default": 1.5, "min": 0.0, "max": 100.0, "step": 0.1}), + "draft_steps": ("INT", {"default": 25, "min": 1, "max": 10000}), + "refiner_steps": ("INT", {"default": 25, "min": 1, "max": 10000}), + "draft_sampler_name": (comfy.samplers.KSampler.SAMPLERS,), + "draft_scheduler": (comfy.samplers.KSampler.SCHEDULERS,), + "refiner_sampler_name": (comfy.samplers.KSampler.SAMPLERS,), + "refiner_scheduler": (comfy.samplers.KSampler.SCHEDULERS,), + "draft_denoise": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}), + "refiner_denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), + } + } + + RETURN_TYPES = ("LATENT",) + RETURN_NAMES = ("LATENT",) + FUNCTION = "execute_sampling" + CATEGORY = "TenserTensor/Sampling" + + def execute_sampling(self, **kwargs): + draft_steps = kwargs["draft_steps"] + refiner_steps = kwargs["refiner_steps"] + + args = { + "model": kwargs["model"], + "positive": kwargs["positive"], + "negative": kwargs["negative"], + "latent": kwargs["latent"], + "seed": kwargs["seed"], + "steps": draft_steps, + "start_step": 0, + "cfg": kwargs["cfg"], + "sampler_name": kwargs["draft_sampler_name"], + "scheduler": kwargs["draft_scheduler"], + "add_noise": True, + "full_denoise": False, + "denoise": kwargs["refiner_denoise"], + } + + samples = sample_latents(**args) + + args["latent"] = samples + args["steps"] = draft_steps + refiner_steps + args["start_step"] = draft_steps + args["denoise"] = kwargs["draft_denoise"] + args["sampler_name"] = kwargs["refiner_sampler_name"] + args["scheduler"] = kwargs["refiner_scheduler"] + args["full_denoise"] = True + + return (sample_latents(**args),) diff --git a/web/docs/TT_KSampler.md b/web/docs/TT_KSampler.md new file mode 100644 index 0000000..b0ca8a1 --- /dev/null +++ b/web/docs/TT_KSampler.md @@ -0,0 +1,34 @@ +## TT_KSampler + +*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.* + +Standard sampler node for generating images from latents. + +### Inputs + +| Parameter | Type | Required | Description | +|----------------|--------------|----------|-----------------------------------------------------------| +| `model` | MODEL | Yes | The diffusion model to use for sampling | +| `positive` | CONDITIONING | Yes | Positive conditioning (what to generate) | +| `negative` | CONDITIONING | Yes | Negative conditioning (what to avoid) | +| `latent` | LATENT | Yes | Latent image to denoise | +| `seed` | INT | Yes | Random seed for reproducibility (0 to 2^64-1, default: 0) | +| `steps` | INT | Yes | Number of sampling steps (1-10000, default: 25) | +| `cfg` | FLOAT | Yes | Classifier-free guidance scale (0.0-100.0, default: 1.5) | +| `sampler_name` | SAMPLER | Yes | Sampler algorithm to use | +| `scheduler` | SCHEDULER | Yes | Noise schedule to use | + +### Outputs + +| Parameter | Type | Description | +|-----------|--------|-----------------------| +| `LATENT` | LATENT | Denoised latent image | + +### Usage + +Standard KSampler for generating images. Accepts all common sampling parameters and returns a denoised latent ready for +VAE decoding. + +--- + +[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor) diff --git a/web/docs/TT_KSamplerAdvanced.md b/web/docs/TT_KSamplerAdvanced.md new file mode 100644 index 0000000..3245041 --- /dev/null +++ b/web/docs/TT_KSamplerAdvanced.md @@ -0,0 +1,40 @@ +## TT_KSamplerAdvanced + +*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.* + +Advanced sampler with fine-grained control over denoising steps and noise addition. + +### Inputs + +| Parameter | Type | Required | Description | +|----------------|--------------|----------|-----------------------------------------------------------| +| `model` | MODEL | Yes | The diffusion model to use for sampling | +| `positive` | CONDITIONING | Yes | Positive conditioning (what to generate) | +| `negative` | CONDITIONING | Yes | Negative conditioning (what to avoid) | +| `latent` | LATENT | Yes | Latent image to denoise | +| `add_noise` | BOOLEAN | Yes | Add noise before sampling (default: True) | +| `full_denoise` | BOOLEAN | Yes | Complete full denoising (default: False) | +| `seed` | INT | Yes | Random seed for reproducibility (0 to 2^64-1, default: 0) | +| `steps` | INT | Yes | Total number of sampling steps (1-10000, default: 25) | +| `start_step` | INT | Yes | Step to start sampling from (0-10000, default: 0) | +| `last_step` | INT | Yes | Step to end sampling at (0-10000, default: 10000) | +| `cfg` | FLOAT | Yes | Classifier-free guidance scale (0.0-100.0, default: 1.5) | +| `sampler_name` | SAMPLER | Yes | Sampler algorithm to use | +| `scheduler` | SCHEDULER | Yes | Noise schedule to use | +| `denoise` | FLOAT | Yes | Denoising strength (0.0-1.0, default: 1.0) | + +### Outputs + +| Parameter | Type | Description | +|-----------|--------|-----------------------| +| `LATENT` | LATENT | Denoised latent image | + +### Usage + +Advanced sampler for multi-stage workflows and fine control. Use start_step/last_step for partial denoising, add_noise +to control noise injection, and denoise for strength control. Useful for img2img, refinement passes, and complex +sampling workflows. + +--- + +[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor) diff --git a/web/docs/TT_KSamplerContext.md b/web/docs/TT_KSamplerContext.md new file mode 100644 index 0000000..0d89f4d --- /dev/null +++ b/web/docs/TT_KSamplerContext.md @@ -0,0 +1,29 @@ +## TT_KSamplerContext + +Context-based sampler that extracts parameters from workflow context. + +### Inputs + +| Parameter | Type | Required | Description | +|-----------|------------|----------|---------------------------------------------------------------------| +| `context` | TT_CONTEXT | Yes | Context containing model, latent, conditioning, and workflow config | + +### Outputs + +| Parameter | Type | Description | +|-----------|------------|-----------------------| +| `CONTEXT` | TT_CONTEXT | Pass-through context | +| `LATENT` | LATENT | Denoised latent image | + +### Usage + +Simplified sampler for context-based workflows. Automatically extracts all sampling parameters from the context object: + +- Model, positive/negative conditioning, and latent from context +- Seed, steps, cfg, sampler_name, and scheduler from workflow_config + +Requires context to contain: model, latent, and workflow_config. Raises error if any required component is missing. + +--- + +[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor) diff --git a/web/docs/TT_KSamplerTwoStage.md b/web/docs/TT_KSamplerTwoStage.md new file mode 100644 index 0000000..72a45a8 --- /dev/null +++ b/web/docs/TT_KSamplerTwoStage.md @@ -0,0 +1,38 @@ +## TT_KSamplerTwoStage + +Two-stage sampler with separate draft and refinement passes. + +### Inputs + +| Parameter | Type | Required | Description | +|------------------------|--------------|----------|------------------------------------------------------------| +| `model` | MODEL | Yes | The diffusion model to use for sampling | +| `positive` | CONDITIONING | Yes | Positive conditioning (what to generate) | +| `negative` | CONDITIONING | Yes | Negative conditioning (what to avoid) | +| `latent` | LATENT | Yes | Latent image to denoise | +| `seed` | INT | Yes | Random seed for reproducibility (0 to 2^64-1, default: 0) | +| `cfg` | FLOAT | Yes | Classifier-free guidance scale (0.0-100.0, default: 1.5) | +| `draft_steps` | INT | Yes | Number of steps for draft pass (1-10000, default: 25) | +| `refiner_steps` | INT | Yes | Number of steps for refinement pass (1-10000, default: 25) | +| `draft_sampler_name` | SAMPLER | Yes | Sampler algorithm for draft pass | +| `draft_scheduler` | SCHEDULER | Yes | Noise schedule for draft pass | +| `refiner_sampler_name` | SAMPLER | Yes | Sampler algorithm for refinement pass | +| `refiner_scheduler` | SCHEDULER | Yes | Noise schedule for refinement pass | +| `draft_denoise` | FLOAT | Yes | Denoising strength for draft (0.0-1.0, default: 0.7) | +| `refiner_denoise` | FLOAT | Yes | Denoising strength for refinement (0.0-1.0, default: 1.0) | + +### Outputs + +| Parameter | Type | Description | +|-----------|--------|-----------------------------------------| +| `LATENT` | LATENT | Fully denoised latent after both passes | + +### Usage + +Automated two-stage sampling workflow. First performs a draft pass with partial denoising, then refines the result with +a second pass. Each stage can use different samplers, schedulers, and step counts. Useful for quick previews followed by +quality refinement, or combining different sampling strategies. + +--- + +[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor) diff --git a/web/docs/TT_SdxlModelsLoader.md b/web/docs/TT_SdxlModelsLoader.md index bfd1051..c2030e0 100644 --- a/web/docs/TT_SdxlModelsLoader.md +++ b/web/docs/TT_SdxlModelsLoader.md @@ -1,5 +1,7 @@ ## TT_SdxlModelsLoader +*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.* + All-in-one model loader for SDXL workflows with checkpoint merging support. ### Inputs diff --git a/web/docs/TT_SdxlModelsLoaderAdvanced.md b/web/docs/TT_SdxlModelsLoaderAdvanced.md index bfe7799..2d5812b 100644 --- a/web/docs/TT_SdxlModelsLoaderAdvanced.md +++ b/web/docs/TT_SdxlModelsLoaderAdvanced.md @@ -1,5 +1,7 @@ ## TT_SdxlModelsLoaderAdvanced +*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.* + Extended all-in-one model loader for SDXL with checkpoint merging and LoRA support. ### Inputs