Files
xmarre-ComfyUI-ScaleLockedR…/nodes.py
T
2026-03-11 04:43:52 +01:00

696 lines
28 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from .slrd_core import build_nested_noise, clone_latent
from .slrd_runtime import (
LOCK_SCHEDULE_OPTIONS,
MID_SCHEDULE_OPTIONS,
ScaleLockConfig,
build_runtime_context_from_advanced,
clean_latent,
compat_sampler_names,
compat_scheduler_names,
fix_latent_channels,
make_lowres_latent,
prepare_noise,
run_scale_locked_ksampler,
sample_with_runtime,
apply_scale_lock_to_guider,
clone_guider_for_scale_lock,
)
class ScaleLockedResidualKSampler:
CATEGORY = "sampling/scale_locked"
RETURN_TYPES = ("LATENT", "LATENT", "LATENT")
RETURN_NAMES = ("output", "lowres_planner", "denoised_output")
FUNCTION = "sample"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("MODEL",),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
"latent_image": ("LATENT",),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}),
"steps": ("INT", {"default": 24, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1, "round": 0.01}),
"sampler_name": (compat_sampler_names(),),
"scheduler": (compat_scheduler_names(),),
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"target_megapixels": ("FLOAT", {"default": 1.0, "min": 0.10, "max": 16.0, "step": 0.05, "round": 0.01}),
"lock_strength": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_start": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_end": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_schedule": (LOCK_SCHEDULE_OPTIONS,),
"lock_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"lock_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
"coarse_cutoff": ("FLOAT", {"default": 0.33, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_cutoff": ("FLOAT", {"default": 0.60, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 2.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_end": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_schedule": (MID_SCHEDULE_OPTIONS,),
"mid_band_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"mid_band_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
"nested_noise_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 4.0, "step": 0.01, "round": 0.001}),
"add_noise": ("BOOLEAN", {"default": True}),
"pin_anchors": ("BOOLEAN", {"default": True}),
"sampler_guard": (["warn", "error", "off"],),
},
"optional": {
"lock_mask": ("MASK",),
},
}
def sample(
self,
model,
positive,
negative,
latent_image,
seed,
steps,
cfg,
sampler_name,
scheduler,
denoise,
target_megapixels,
lock_strength,
lock_strength_start,
lock_strength_end,
lock_schedule,
lock_schedule_hold,
lock_schedule_power,
coarse_cutoff,
mid_band_cutoff,
mid_band_strength,
mid_band_strength_start,
mid_band_strength_end,
mid_band_schedule,
mid_band_schedule_hold,
mid_band_schedule_power,
nested_noise_strength,
add_noise,
pin_anchors,
sampler_guard,
lock_mask=None,
):
result = run_scale_locked_ksampler(
model=model,
positive=positive,
negative=negative,
latent_image=latent_image,
seed=seed,
steps=steps,
cfg=cfg,
sampler_name=sampler_name,
scheduler=scheduler,
denoise=denoise,
target_megapixels=target_megapixels,
nested_noise_strength=nested_noise_strength,
add_noise=add_noise,
pin_anchors=pin_anchors,
sampler_guard=sampler_guard,
config=_scale_lock_config(
lock_strength=lock_strength,
lock_strength_start=lock_strength_start,
lock_strength_end=lock_strength_end,
coarse_cutoff=coarse_cutoff,
mid_band_cutoff=mid_band_cutoff,
mid_band_strength=mid_band_strength,
lock_schedule=lock_schedule,
lock_schedule_hold=lock_schedule_hold,
lock_schedule_power=lock_schedule_power,
mid_band_strength_start=mid_band_strength_start,
mid_band_strength_end=mid_band_strength_end,
mid_band_schedule=mid_band_schedule,
mid_band_schedule_hold=mid_band_schedule_hold,
mid_band_schedule_power=mid_band_schedule_power,
lock_mask=lock_mask,
),
)
return (result.output, result.lowres_planner, result.denoised_output)
class ScaleLockedRuntimeContextBuilder:
CATEGORY = "sampling/scale_locked"
RETURN_TYPES = ("SLRD_RUNTIME", "NOISE", "LATENT")
RETURN_NAMES = ("runtime", "prepared_noise", "lowres_planner")
FUNCTION = "build"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"noise": ("NOISE",),
"guider": ("GUIDER",),
"sampler": ("SAMPLER",),
"sigmas": ("SIGMAS",),
"latent_image": ("LATENT",),
"target_megapixels": ("FLOAT", {"default": 1.0, "min": 0.10, "max": 16.0, "step": 0.05, "round": 0.01}),
"nested_noise_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 4.0, "step": 0.01, "round": 0.001}),
"pin_anchors": ("BOOLEAN", {"default": True}),
}
}
def build(self, noise, guider, sampler, sigmas, latent_image, target_megapixels, nested_noise_strength, pin_anchors):
runtime = build_runtime_context_from_advanced(
noise=noise,
guider=guider,
sampler=sampler,
sigmas=sigmas,
latent_image=latent_image,
target_megapixels=target_megapixels,
nested_noise_strength=nested_noise_strength,
pin_anchors=pin_anchors,
)
return (runtime, runtime.prepared_noise(), clean_latent(runtime.lowres_out))
class ScaleLockedCFGGuiderNode:
CATEGORY = "sampling/scale_locked"
RETURN_TYPES = ("GUIDER",)
RETURN_NAMES = ("guider",)
FUNCTION = "build"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"guider": ("GUIDER",),
"runtime": ("SLRD_RUNTIME",),
"lock_strength": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_start": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_end": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_schedule": (LOCK_SCHEDULE_OPTIONS,),
"lock_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"lock_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
"coarse_cutoff": ("FLOAT", {"default": 0.33, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_cutoff": ("FLOAT", {"default": 0.60, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 2.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_end": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_schedule": (MID_SCHEDULE_OPTIONS,),
"mid_band_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"mid_band_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
},
"optional": {
"lock_mask": ("MASK",),
},
}
def build(
self,
guider,
runtime,
lock_strength,
lock_strength_start,
lock_strength_end,
lock_schedule,
lock_schedule_hold,
lock_schedule_power,
coarse_cutoff,
mid_band_cutoff,
mid_band_strength,
mid_band_strength_start,
mid_band_strength_end,
mid_band_schedule,
mid_band_schedule_hold,
mid_band_schedule_power,
lock_mask=None,
):
patched_guider = clone_guider_for_scale_lock(guider)
apply_scale_lock_to_guider(
patched_guider,
runtime,
_scale_lock_config(
lock_strength=lock_strength,
lock_strength_start=lock_strength_start,
lock_strength_end=lock_strength_end,
coarse_cutoff=coarse_cutoff,
mid_band_cutoff=mid_band_cutoff,
mid_band_strength=mid_band_strength,
lock_schedule=lock_schedule,
lock_schedule_hold=lock_schedule_hold,
lock_schedule_power=lock_schedule_power,
mid_band_strength_start=mid_band_strength_start,
mid_band_strength_end=mid_band_strength_end,
mid_band_schedule=mid_band_schedule,
mid_band_schedule_hold=mid_band_schedule_hold,
mid_band_schedule_power=mid_band_schedule_power,
lock_mask=lock_mask,
),
)
return (patched_guider,)
class ScaleLockedResidualSamplerCustomAdvanced:
CATEGORY = "sampling/scale_locked"
RETURN_TYPES = ("LATENT", "LATENT", "LATENT")
RETURN_NAMES = ("output", "lowres_planner", "denoised_output")
FUNCTION = "sample"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"noise": ("NOISE",),
"guider": ("GUIDER",),
"sampler": ("SAMPLER",),
"sigmas": ("SIGMAS",),
"latent_image": ("LATENT",),
"target_megapixels": ("FLOAT", {"default": 1.0, "min": 0.10, "max": 16.0, "step": 0.05, "round": 0.01}),
"lock_strength": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_start": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_end": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_schedule": (LOCK_SCHEDULE_OPTIONS,),
"lock_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"lock_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
"coarse_cutoff": ("FLOAT", {"default": 0.33, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_cutoff": ("FLOAT", {"default": 0.60, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 2.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_end": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_schedule": (MID_SCHEDULE_OPTIONS,),
"mid_band_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"mid_band_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
"nested_noise_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 4.0, "step": 0.01, "round": 0.001}),
"pin_anchors": ("BOOLEAN", {"default": True}),
},
"optional": {
"lock_mask": ("MASK",),
},
}
def sample(
self,
noise,
guider,
sampler,
sigmas,
latent_image,
target_megapixels,
lock_strength,
lock_strength_start,
lock_strength_end,
lock_schedule,
lock_schedule_hold,
lock_schedule_power,
coarse_cutoff,
mid_band_cutoff,
mid_band_strength,
mid_band_strength_start,
mid_band_strength_end,
mid_band_schedule,
mid_band_schedule_hold,
mid_band_schedule_power,
nested_noise_strength,
pin_anchors,
lock_mask=None,
):
runtime = build_runtime_context_from_advanced(
noise=noise,
guider=guider,
sampler=sampler,
sigmas=sigmas,
latent_image=latent_image,
target_megapixels=target_megapixels,
nested_noise_strength=nested_noise_strength,
pin_anchors=pin_anchors,
)
result = sample_with_runtime(
guider=guider,
sampler=sampler,
runtime=runtime,
config=_scale_lock_config(
lock_strength=lock_strength,
lock_strength_start=lock_strength_start,
lock_strength_end=lock_strength_end,
coarse_cutoff=coarse_cutoff,
mid_band_cutoff=mid_band_cutoff,
mid_band_strength=mid_band_strength,
lock_schedule=lock_schedule,
lock_schedule_hold=lock_schedule_hold,
lock_schedule_power=lock_schedule_power,
mid_band_strength_start=mid_band_strength_start,
mid_band_strength_end=mid_band_strength_end,
mid_band_schedule=mid_band_schedule,
mid_band_schedule_hold=mid_band_schedule_hold,
mid_band_schedule_power=mid_band_schedule_power,
lock_mask=lock_mask,
),
)
return (result.output, result.lowres_planner, result.denoised_output)
class ScaleLockedNestedNoisePreview:
CATEGORY = "sampling/scale_locked"
RETURN_TYPES = ("LATENT", "LATENT")
RETURN_NAMES = ("nested_noise_latent", "lowres_reference_latent")
FUNCTION = "build"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"latent_image": ("LATENT",),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}),
"target_megapixels": ("FLOAT", {"default": 1.0, "min": 0.10, "max": 16.0, "step": 0.05, "round": 0.01}),
"nested_noise_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 4.0, "step": 0.01, "round": 0.001}),
}
}
def build(self, latent_image, seed, target_megapixels, nested_noise_strength):
high = clone_latent(latent_image)
low = make_lowres_latent(high, target_megapixels)
low_samples = low["samples"]
high_samples = high["samples"]
low_noise = prepare_noise(low_samples, int(seed), low.get("batch_index", None))
high_noise = build_nested_noise(
lowres_noise=low_noise,
target_shape=tuple(high_samples.shape),
seed=int(seed) ^ 0x9E3779B97F4A7C15,
hf_strength=nested_noise_strength,
)
out = clone_latent(high)
out["samples"] = high_noise
return (out, low)
@dataclass
class _ImpactSampleRequest:
model: Any
seed: int
steps: int
cfg: float
sampler_name: str
scheduler: str
positive: Any
negative: Any
latent: dict
denoise: float
_IMPACT_FIELD_ALIASES = {
"model": ("model",),
"seed": ("seed",),
"steps": ("steps",),
"cfg": ("cfg",),
"sampler_name": ("sampler_name", "sampler"),
"scheduler": ("scheduler",),
"positive": ("positive", "positive_cond", "cond"),
"negative": ("negative", "negative_cond", "uncond"),
"latent": ("latent", "latent_image"),
"denoise": ("denoise",),
}
@dataclass
class _ImpactHookSettings:
target_megapixels: float
nested_noise_strength: float
add_noise: bool
pin_anchors: bool
sampler_guard: str
config: ScaleLockConfig
class _ScaleLockedImpactSamplerAdapter:
def __init__(self, hook: "_ScaleLockedDetailerHook"):
self._hook = hook
def sample(self, *args, **kwargs):
return self._hook.sample_latent(*args, **kwargs)
def ksample(self, *args, **kwargs):
return self._hook.sample_latent(*args, **kwargs)
def sample_full(self, *args, **kwargs):
return self._hook.sample_full(*args, **kwargs)
def __call__(self, *args, **kwargs):
return self._hook.sample_latent(*args, **kwargs)
class _ScaleLockedDetailerHook:
def __init__(self, settings: _ImpactHookSettings):
self._settings = settings
self._pending_request: _ImpactSampleRequest | None = None
self._adapter = _ScaleLockedImpactSamplerAdapter(self)
def post_crop_region(self, w, h, item_bbox, crop_region):
del w, h, item_bbox
return crop_region
def post_detection(self, segs):
return segs
def get_custom_sampler(self, *args, **kwargs):
self._remember_request(args, kwargs, strict=False)
return self._adapter
def get_custom_sampler_provider(self, *args, **kwargs):
return self.get_custom_sampler(*args, **kwargs)
def get_custom_ksampler_provider(self, *args, **kwargs):
return self.get_custom_sampler(*args, **kwargs)
def pre_ksample(self, *args, **kwargs):
self._remember_request(args, kwargs, strict=False)
if kwargs:
return kwargs
return args
def post_ksample(self, *args, **kwargs):
self._pending_request = None
if kwargs:
return kwargs
if len(args) == 1:
return args[0]
return args
def sample_latent(self, *args, **kwargs):
return self.sample_full(*args, **kwargs).output
def sample_full(self, *args, **kwargs):
request = self._remember_request(args, kwargs, strict=not bool(self._pending_request))
result = run_scale_locked_ksampler(
model=request.model,
positive=request.positive,
negative=request.negative,
latent_image=request.latent,
seed=request.seed,
steps=request.steps,
cfg=request.cfg,
sampler_name=request.sampler_name,
scheduler=request.scheduler,
denoise=request.denoise,
target_megapixels=self._settings.target_megapixels,
nested_noise_strength=self._settings.nested_noise_strength,
add_noise=self._settings.add_noise,
pin_anchors=self._settings.pin_anchors,
sampler_guard=self._settings.sampler_guard,
config=self._settings.config,
)
self._pending_request = request
return result
def _remember_request(self, args, kwargs, strict: bool) -> _ImpactSampleRequest | None:
try:
request = _coerce_impact_request(args, kwargs, fallback=self._pending_request)
except TypeError:
if strict:
raise
request = self._pending_request
if request is not None:
self._pending_request = request
return request
class ScaleLockedDetailerHookProvider:
CATEGORY = "sampling/scale_locked/impact"
RETURN_TYPES = ("DETAILER_HOOK",)
RETURN_NAMES = ("detailer_hook",)
FUNCTION = "build"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"target_megapixels": ("FLOAT", {"default": 1.0, "min": 0.10, "max": 16.0, "step": 0.05, "round": 0.01}),
"lock_strength": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_start": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_strength_end": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"lock_schedule": (LOCK_SCHEDULE_OPTIONS,),
"lock_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"lock_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
"coarse_cutoff": ("FLOAT", {"default": 0.33, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_cutoff": ("FLOAT", {"default": 0.60, "min": 0.05, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 2.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_strength_end": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "round": 0.001}),
"mid_band_schedule": (MID_SCHEDULE_OPTIONS,),
"mid_band_schedule_hold": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 0.95, "step": 0.01, "round": 0.001}),
"mid_band_schedule_power": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 8.0, "step": 0.1, "round": 0.01}),
"nested_noise_strength": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 4.0, "step": 0.01, "round": 0.001}),
"add_noise": ("BOOLEAN", {"default": True}),
"pin_anchors": ("BOOLEAN", {"default": True}),
"sampler_guard": (["warn", "error", "off"],),
},
"optional": {
"lock_mask": ("MASK",),
},
}
def build(
self,
target_megapixels,
lock_strength,
lock_strength_start,
lock_strength_end,
lock_schedule,
lock_schedule_hold,
lock_schedule_power,
coarse_cutoff,
mid_band_cutoff,
mid_band_strength,
mid_band_strength_start,
mid_band_strength_end,
mid_band_schedule,
mid_band_schedule_hold,
mid_band_schedule_power,
nested_noise_strength,
add_noise,
pin_anchors,
sampler_guard,
lock_mask=None,
):
settings = _ImpactHookSettings(
target_megapixels=target_megapixels,
nested_noise_strength=nested_noise_strength,
add_noise=add_noise,
pin_anchors=pin_anchors,
sampler_guard=sampler_guard,
config=_scale_lock_config(
lock_strength=lock_strength,
lock_strength_start=lock_strength_start,
lock_strength_end=lock_strength_end,
coarse_cutoff=coarse_cutoff,
mid_band_cutoff=mid_band_cutoff,
mid_band_strength=mid_band_strength,
lock_schedule=lock_schedule,
lock_schedule_hold=lock_schedule_hold,
lock_schedule_power=lock_schedule_power,
mid_band_strength_start=mid_band_strength_start,
mid_band_strength_end=mid_band_strength_end,
mid_band_schedule=mid_band_schedule,
mid_band_schedule_hold=mid_band_schedule_hold,
mid_band_schedule_power=mid_band_schedule_power,
lock_mask=lock_mask,
),
)
return (_ScaleLockedDetailerHook(settings),)
def _scale_lock_config(
*,
lock_strength,
lock_strength_start,
lock_strength_end,
coarse_cutoff,
mid_band_cutoff,
mid_band_strength,
lock_schedule,
lock_schedule_hold,
lock_schedule_power,
mid_band_strength_start,
mid_band_strength_end,
mid_band_schedule,
mid_band_schedule_hold,
mid_band_schedule_power,
lock_mask,
):
return ScaleLockConfig(
lock_strength=float(lock_strength),
lock_strength_start=float(lock_strength_start),
lock_strength_end=float(lock_strength_end),
cutoff=float(coarse_cutoff),
mid_cutoff=float(mid_band_cutoff),
mid_strength=float(mid_band_strength),
schedule=lock_schedule,
schedule_power=float(lock_schedule_power),
schedule_hold=float(lock_schedule_hold),
mid_strength_start=float(mid_band_strength_start),
mid_strength_end=float(mid_band_strength_end),
mid_schedule=mid_band_schedule,
mid_schedule_power=float(mid_band_schedule_power),
mid_schedule_hold=float(mid_band_schedule_hold),
spatial_mask=lock_mask,
)
def _coerce_impact_request(args, kwargs, fallback=None) -> _ImpactSampleRequest:
data = {}
if fallback is not None:
data.update(fallback.__dict__)
if len(args) >= 10:
names = ("model", "seed", "steps", "cfg", "sampler_name", "scheduler", "positive", "negative", "latent", "denoise")
for name, value in zip(names, args[:10]):
data[name] = value
for field, aliases in _IMPACT_FIELD_ALIASES.items():
for alias in aliases:
if alias in kwargs:
data[field] = kwargs[alias]
break
required = ("model", "seed", "steps", "cfg", "sampler_name", "scheduler", "positive", "negative", "latent", "denoise")
missing = [field for field in required if field not in data or data[field] is None]
if missing:
raise TypeError(f"Unable to resolve Impact detailer sample request fields: {', '.join(missing)}")
latent = data["latent"]
if not isinstance(latent, dict) or "samples" not in latent:
raise TypeError("Impact detailer sample request did not provide a LATENT dict with a 'samples' entry.")
return _ImpactSampleRequest(
model=data["model"],
seed=int(data["seed"]),
steps=int(data["steps"]),
cfg=float(data["cfg"]),
sampler_name=str(data["sampler_name"]),
scheduler=str(data["scheduler"]),
positive=data["positive"],
negative=data["negative"],
latent=data["latent"],
denoise=float(data["denoise"]),
)
NODE_CLASS_MAPPINGS = {
"ScaleLockedResidualKSampler": ScaleLockedResidualKSampler,
"ScaleLockedRuntimeContextBuilder": ScaleLockedRuntimeContextBuilder,
"ScaleLockedCFGGuider": ScaleLockedCFGGuiderNode,
"ScaleLockedResidualSamplerCustomAdvanced": ScaleLockedResidualSamplerCustomAdvanced,
"ScaleLockedNestedNoisePreview": ScaleLockedNestedNoisePreview,
"ScaleLockedDetailerHookProvider": ScaleLockedDetailerHookProvider,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ScaleLockedResidualKSampler": "Scale-Locked Residual KSampler",
"ScaleLockedRuntimeContextBuilder": "Scale-Locked Runtime Context",
"ScaleLockedCFGGuider": "Scale-Locked CFG Guider",
"ScaleLockedResidualSamplerCustomAdvanced": "Scale-Locked Residual SamplerCustomAdvanced",
"ScaleLockedNestedNoisePreview": "Scale-Locked Nested Noise Preview",
"ScaleLockedDetailerHookProvider": "Scale-Locked Detailer Hook Provider",
}