Add optional seed inputs to pop-random nodes.

This commit is contained in:
Comahar
2026-07-14 23:06:54 +03:00
parent fd37841b70
commit b1f9824fa2
8 changed files with 81 additions and 16 deletions
+19 -4
View File
@@ -760,13 +760,17 @@ class DataListPopRandom(ComfyNodeABC):
This node takes a list as input and returns the list with the random element removed
and the removed element itself. If the list is empty, it returns None for the element.
An optional seed can be provided for reproducible selection.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": (IO.ANY, {}),
}
},
"optional": {
"seed": (IO.INT, {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
},
}
RETURN_TYPES = (IO.ANY, IO.ANY)
@@ -779,13 +783,24 @@ class DataListPopRandom(ComfyNodeABC):
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN") # Not equal to anything -> trigger recalculation
seed = kwargs.get("seed")
if seed is None:
return float("NaN") # Not equal to anything -> trigger recalculation
# INPUT_IS_LIST wraps scalars in a list
if isinstance(seed, list):
seed = seed[0] if seed else None
if seed is None:
return float("NaN")
return seed
def pop_random_element(self, **kwargs: list[Any]) -> tuple[list[Any], Any]:
from random import randrange
import random
input_list = kwargs.get('list', []).copy()
seed_values = kwargs.get('seed')
seed = seed_values[0] if seed_values is not None else None
rng = random.Random(seed) if seed is not None else random
if input_list:
random_element = input_list.pop(randrange(len(input_list)))
random_element = input_list.pop(rng.randrange(len(input_list)))
return input_list, random_element
return input_list, None
+12 -4
View File
@@ -826,13 +826,17 @@ class DictPopRandom(ComfyNodeABC):
This node takes a dictionary as input, removes a random key-value pair,
and returns the modified dictionary along with the removed key and value.
If the dictionary is empty, it returns empty values.
An optional seed can be provided for reproducible selection.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_dict": ("DICT", {}),
}
},
"optional": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
},
}
RETURN_TYPES = ("DICT", IO.STRING, IO.ANY, IO.BOOLEAN)
@@ -843,15 +847,19 @@ class DictPopRandom(ComfyNodeABC):
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN") # Not equal to anything -> trigger recalculation
seed = kwargs.get("seed")
if seed is None:
return float("NaN") # Not equal to anything -> trigger recalculation
return seed
def pop_random(self, input_dict: Mapping[KT, VT]) -> tuple[Mapping[KT, VT], _U[KT, str], _O[VT], bool]:
def pop_random(self, input_dict: Mapping[KT, VT], seed: _O[int] = None) -> tuple[Mapping[KT, VT], _U[KT, str], _O[VT], bool]:
if not input_dict:
return input_dict, "", None, False
rng = random.Random(seed) if seed is not None else random
result = dict(input_dict)
try:
random_key = random.choice(list(result.keys()))
random_key = rng.choice(list(result.keys()))
random_value = result.pop(random_key)
except Exception:
return input_dict, "", None, False
+12 -4
View File
@@ -595,13 +595,17 @@ class ListPopRandom(ComfyNodeABC):
This node takes a LIST as input and returns the LIST with the random element removed
and the removed element itself. If the LIST is empty, it returns None for the element.
An optional seed can be provided for reproducible selection.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("LIST", {}),
}
},
"optional": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
},
}
RETURN_TYPES = ("LIST", IO.ANY)
@@ -612,13 +616,17 @@ class ListPopRandom(ComfyNodeABC):
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN") # Not equal to anything -> trigger recalculation
seed = kwargs.get("seed")
if seed is None:
return float("NaN") # Not equal to anything -> trigger recalculation
return seed
def pop_random_element(self, list: list[Any]) -> tuple[list[Any], Any]:
def pop_random_element(self, list: list[Any], seed=None) -> tuple[list[Any], Any]:
import random
rng = random.Random(seed) if seed is not None else random
result = list.copy()
if result:
random_index = random.randrange(len(result))
random_index = rng.randrange(len(result))
random_element = result.pop(random_index)
return result, random_element
return result, None
+12 -4
View File
@@ -505,13 +505,17 @@ class SetPopRandom(ComfyNodeABC):
This node takes a SET as input and returns the SET with a random element removed
and the removed element itself. If the SET is empty, it returns None for the element.
An optional seed can be provided for reproducible selection.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"set": ("SET", {}),
}
},
"optional": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
},
}
RETURN_TYPES = ("SET", IO.ANY)
@@ -522,13 +526,17 @@ class SetPopRandom(ComfyNodeABC):
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN") # Not equal to anything -> trigger recalculation
seed = kwargs.get("seed")
if seed is None:
return float("NaN") # Not equal to anything -> trigger recalculation
return seed
def pop_random_element(self, set: set[Any]) -> tuple[set[Any], Any]:
def pop_random_element(self, set: set[Any], seed=None) -> tuple[set[Any], Any]:
import random
rng = random.Random(seed) if seed is not None else random
result = set.copy()
if result:
random_element = random.choice(list(result))
random_element = rng.choice(list(result))
result.remove(random_element)
return result, random_element
return result, None