Files
IsulionandClaude Opus 5.5 96553dee1c feat: v6.1.0 — Censored Part & Prompt Vault nodes, selector RNG, hardening
New nodes:
- Isulion Censored Part: blur/pixelate/black bar/noise over a mask and/or
  a percent rectangle; accepts 2D/3D/4D masks and fails closed (blacks the
  item out) instead of passing an uncensored image through
- Prompt Vault Save/Load: named prompt library stored in ComfyUI's user
  directory (outside the checkout, so updates never conflict), seeded from
  prompt_vault.example.json; Load re-runs when the vault changes

Refactor:
- SimpleSelectorNode base: ~20 selector nodes use an instance RNG instead
  of reseeding the global random module (same seed => same output)
- Theme handlers declare DISPLAY_NAME; theme package auto-discovers modules
- __init__ imports node classes directly

Fixes:
- net_utils: re-validate every redirect hop, unwrap IPv4-mapped IPv6,
  block NAT64 and all non-global addresses (SSRF)
- Multiple Prompt Generator: no duplicate prompts for themes listed in two
  categories; 6 themes missing from every category are now reachable
- Shutdown node: parameter renamed to match its "any" input (TypeError)
- ConfigManager: deterministic (sorted) merge order, warns on key
  collisions; drop stale space_colony copy from tropical_paradise_config
- Mega Prompt V3: seed honored again with randomize=disable

Breaking: Civitai api_key widget removed (use CIVITAI_API_TOKEN), absolute
paths refused by Load Images, Negative Prompt pools de-duplicated (shorter
"strict" output), Dynamic Random theme order is now alphabetical.

Tooling: pytest suite (51 tests), GitHub CI (compileall, pytest, ruff),
ruff config, uv.lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:35:56 +02:00

41 lines
1.6 KiB
Python

"""Shared seeded-selection core for the simple 'pick from a pool' nodes.
Subclasses declare their option pool as a class attribute (POOL or POOLS)
and call _make_rng() instead of touching the process-global random module,
so every node's output is reproducible from its own inputs alone.
"""
import random
from typing import ClassVar, Dict, List, Optional, Sequence, Tuple
class SimpleSelectorNode:
"""Base for single-choice selector nodes (no ComfyUI imports here)."""
POOL: ClassVar[Optional[Sequence[str]]] = None
POOLS: ClassVar[Optional[Dict[str, Sequence[str]]]] = None
MAX_SEED: int = 0xFFFFFFFFFFFFFFFF
@classmethod
def _pool_for(cls, category: str = "any") -> List[str]:
"""Resolve the candidate list, optionally filtered by POOLS key."""
if cls.POOLS is not None:
if category and category != "any":
return list(cls.POOLS.get(category, []))
return [item for sub in cls.POOLS.values() for item in sub]
return list(cls.POOL or [])
def _make_rng(self, randomize: str, seed: int) -> Tuple[random.Random, int]:
"""Instance-local RNG honoring the legacy seed contract.
enable + seed>0 -> deterministic stream seeded by `seed`.
enable + seed==0 -> fresh entropy, reported via returned seed.
disable -> rng is unused; caller keeps the dropdown value.
"""
if randomize == "enable":
if seed is not None and seed > 0:
return random.Random(seed), seed
new_seed = random.Random().randint(0, self.MAX_SEED)
return random.Random(new_seed), new_seed
return random.Random(seed), seed