Files
IxMxAMAR-ComfyUI-Utility-Me…/mp_nodes/models_sampling/operations/seed_sampling.py
T
IxMxAMAR 15e4b0da4e release(v0.1.2): fix critical loader collision + 6 Gemini-review findings
Critical:
- rename nodes/ -> mp_nodes/, shared/ -> mp_shared/ to avoid shadowing
  ComfyUI core's nodes.py via sys.path injection (root cause of v0.1.1
  'nodes is not a package' startup failure)
- http_post now streams response and enforces 50MB cap mid-stream (DoS)
- AES key derivation: HKDF -> PBKDF2-HMAC-SHA256 600k iters + random salt
  (work factor against weak-key brute force; ciphertext format changed)
- fs_mkdir/copy/move/delete confined to ComfyUI input/output/temp + ~ by
  default; UTILITY_MEGAPACK_ALLOW_ARBITRARY_PATHS=1 to override

UX / correctness:
- combo settings now declare options array (themes dropdown was empty)
- LATENT default 1x4x8x8 zeros -> {} (avoids Flux/SD3 shape crashes)
- @op decorator rejects reserved widget names mode/theme at register time
- _pad_outputs docstring updated to match relaxed opaque-type behavior
2026-05-13 21:36:54 +05:30

130 lines
4.1 KiB
Python

"""Seed management and sampler/scheduler helpers."""
import hashlib
import json as _json
import random as _random
from .. import op
_SAMPLERS = [
"euler", "euler_ancestral", "heun", "heunpp2",
"dpm_2", "dpm_2_ancestral", "lms",
"dpmpp_2m", "dpmpp_2m_sde", "dpmpp_3m_sde",
"dpmpp_sde", "dpmpp_sde_gpu", "ddim", "uni_pc", "lcm",
]
_SCHEDULERS = [
"normal", "karras", "exponential", "sgm_uniform",
"simple", "ddim_uniform", "beta",
]
_SEED_HISTORY: list[int] = []
_SEED_HISTORY_MAX = 32
@op(
op_id="seed_cycle",
display_name="Seed Cycle (fixed/incr/decr/random/from-string)",
category="Seed",
input_schema={"required": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFF}),
"cycle_mode": (["fixed", "increment", "decrement", "random", "from_string"], {"default": "fixed"}),
"string_input": ("STRING", {"default": ""}),
}},
output_indices=(5,),
description="Various seed strategies. from_string deterministically derives a seed from text via SHA-256.",
)
def seed_cycle(self, seed, cycle_mode="fixed", string_input=""):
out = int(seed)
if cycle_mode == "increment":
out = (out + 1) & 0xFFFFFFFF
elif cycle_mode == "decrement":
out = (out - 1) & 0xFFFFFFFF
elif cycle_mode == "random":
out = _random.SystemRandom().randint(0, 0xFFFFFFFF)
elif cycle_mode == "from_string":
digest = hashlib.sha256(string_input.encode("utf-8")).digest()
out = int.from_bytes(digest[:4], "big")
_SEED_HISTORY.append(out)
if len(_SEED_HISTORY) > _SEED_HISTORY_MAX:
del _SEED_HISTORY[: len(_SEED_HISTORY) - _SEED_HISTORY_MAX]
return (out,)
@op(
op_id="multi_seed_batch",
display_name="Multi-Seed Batch (N seeds)",
category="Seed",
input_schema={"required": {
"base_seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFF}),
"count": ("INT", {"default": 4, "min": 1, "max": 256}),
}},
output_indices=(8,),
description="Returns DICT with `seeds` list of N consecutive seeds starting from base_seed.",
)
def multi_seed_batch(self, base_seed, count=4):
seeds = [(int(base_seed) + i) & 0xFFFFFFFF for i in range(int(count))]
return ({"seeds": seeds, "count": len(seeds), "base_seed": int(base_seed)},)
@op(
op_id="seed_history",
display_name="Seed History (recall recent)",
category="Seed",
input_schema={"required": {}},
output_indices=(8,),
description="Returns the last 32 seeds emitted by seed_cycle.",
)
def seed_history(self):
return ({"history": list(_SEED_HISTORY), "count": len(_SEED_HISTORY)},)
@op(
op_id="sampler_pick",
display_name="Sampler Picker",
category="Sampling",
input_schema={"required": {"sampler": (_SAMPLERS, {"default": "euler"})}},
output_indices=(4,),
description="Emit a sampler name as STRING (out:4).",
)
def sampler_pick(self, sampler="euler"):
return (sampler,)
@op(
op_id="scheduler_pick",
display_name="Scheduler Picker",
category="Sampling",
input_schema={"required": {"scheduler": (_SCHEDULERS, {"default": "normal"})}},
output_indices=(3,),
description="Emit a scheduler name as STRING (out:3 — name slot).",
)
def scheduler_pick(self, scheduler="normal"):
return (scheduler,)
@op(
op_id="sampler_params_bundle",
display_name="Sampler Params Bundle",
category="Sampling",
input_schema={"required": {
"sampler": (_SAMPLERS, {"default": "euler"}),
"scheduler": (_SCHEDULERS, {"default": "normal"}),
"steps": ("INT", {"default": 20, "min": 1, "max": 200}),
"cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 30.0}),
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0}),
}},
output_indices=(7, 8),
description="Bundle sampler+scheduler+steps+cfg+denoise into one DICT (out:8). Also exposes cfg as FLOAT (out:7).",
)
def sampler_params_bundle(self, sampler, scheduler, steps=20, cfg=7.0, denoise=1.0):
bundle = {
"sampler": sampler,
"scheduler": scheduler,
"steps": int(steps),
"cfg": float(cfg),
"denoise": float(denoise),
}
return (float(cfg), bundle)