From 7bc08ada4769cedbc53e97f74b9412b05ae269b2 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sat, 27 Jun 2026 14:16:03 +0200 Subject: [PATCH] Centralize helper seed selection --- node_route_config.py | 26 +++++++------------------- node_seed_resolution.py | 26 +++++++------------------- seed_config.py | 23 ++++++++++++++++++++++- tools/prompt_smoke.py | 21 +++++++++++++++++++++ 4 files changed, 57 insertions(+), 39 deletions(-) diff --git a/node_route_config.py b/node_route_config.py index 72876de..639967d 100644 --- a/node_route_config.py +++ b/node_route_config.py @@ -13,6 +13,7 @@ try: from .prompt_builder import ( subcategory_choices, ) + from .seed_config import configured_seed_from_axes from .location_config import ( build_composition_pool_json, build_location_pool_json, @@ -31,6 +32,7 @@ except ImportError: # Allows local smoke tests from the repository root. from prompt_builder import ( subcategory_choices, ) + from seed_config import configured_seed_from_axes from location_config import ( build_composition_pool_json, build_location_pool_json, @@ -224,25 +226,11 @@ class SxCPCastBias: @staticmethod def _configured_cast_seed(seed_config): - if not seed_config: - return None - if isinstance(seed_config, dict): - raw = seed_config - else: - try: - raw = json.loads(str(seed_config)) - except (TypeError, ValueError, json.JSONDecodeError): - return None - if not isinstance(raw, dict): - return None - for key in ("category_seed", "content_seed", "role_seed", "seed", "global_seed"): - try: - value = int(raw.get(key)) - except (TypeError, ValueError): - continue - if value >= 0: - return value - return None + return configured_seed_from_axes( + seed_config, + ("category", "content", "role"), + extra_keys=("seed", "global_seed"), + ) @staticmethod def _weight_pairs(weights_text, start_count): diff --git a/node_seed_resolution.py b/node_seed_resolution.py index 1c5159d..7da8b77 100644 --- a/node_seed_resolution.py +++ b/node_seed_resolution.py @@ -8,6 +8,7 @@ try: from .seed_config import ( build_seed_config_json, build_seed_lock_config_json, + configured_seed_from_axes, normalize_reroll_axis, seed_reroll_axis_choices, seed_mode_choices, @@ -16,6 +17,7 @@ except ImportError: # Allows local smoke tests from the repository root. from seed_config import ( build_seed_config_json, build_seed_lock_config_json, + configured_seed_from_axes, normalize_reroll_axis, seed_reroll_axis_choices, seed_mode_choices, @@ -225,25 +227,11 @@ class SxCPSDXLBucketSize: @staticmethod def _configured_bucket_seed(seed_config): - if not seed_config: - return None - if isinstance(seed_config, dict): - raw = seed_config - else: - try: - raw = json.loads(str(seed_config)) - except (TypeError, ValueError, json.JSONDecodeError): - return None - if not isinstance(raw, dict): - return None - for key in ("composition_seed", "content_seed", "seed", "global_seed"): - try: - value = int(raw.get(key)) - except (TypeError, ValueError): - continue - if value >= 0: - return value - return None + return configured_seed_from_axes( + seed_config, + ("composition", "content"), + extra_keys=("seed", "global_seed"), + ) @classmethod def IS_CHANGED(cls, *args, **kwargs): diff --git a/seed_config.py b/seed_config.py index ebf6b7e..7bd6f46 100644 --- a/seed_config.py +++ b/seed_config.py @@ -2,7 +2,7 @@ from __future__ import annotations import json import random -from typing import Any +from typing import Any, Iterable SEED_AXIS_SALTS = { @@ -189,6 +189,27 @@ def configured_axis_seed(seed_config: dict[str, int], axis: str) -> int | None: return None +def configured_seed_from_axes( + seed_config: str | dict[str, Any] | None, + axes: Iterable[str], + *, + extra_keys: Iterable[str] = (), +) -> int | None: + try: + parsed = parse_seed_config(seed_config) + except ValueError: + return None + for axis in axes: + value = configured_axis_seed(parsed, axis) + if value is not None: + return value + for key in extra_keys: + value = parsed.get(str(key)) + if value is not None and value >= 0: + return value + return None + + def axis_rng(seed_config: dict[str, int], axis: str, base_seed: int, row_number: int) -> random.Random: configured = configured_axis_seed(seed_config, axis) salt = SEED_AXIS_SALTS.get(axis, 0) diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 4390c7e..7bdc7e5 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -5762,6 +5762,14 @@ def smoke_node_utility_registration() -> None: bucket_b = bucket_node.build("portrait", 77, 3, 0) _expect(bucket_a == bucket_b, "SDXL bucket should be deterministic for fixed seed and row") _expect(bucket_a[3] == "portrait", "SDXL bucket ignored orientation filter") + bucket_seed_config = json.dumps({"camera_seed": 9001}) + bucket_seeded_a = bucket_node.build("landscape", -1, 4, 0, bucket_seed_config) + bucket_seeded_b = bucket_node.build("landscape", -1, 4, 0, bucket_seed_config) + _expect(bucket_seeded_a == bucket_seeded_b, "SDXL bucket should honor seed_config aliases deterministically") + _expect( + bucket_node._configured_bucket_seed({"camera_seed": "42"}) == 42, + "SDXL bucket seed should delegate through composition/camera seed aliases", + ) krea_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2ResolutionSelector"]() krea_width, krea_height, _resolution, aspect_ratio, api_aspect, _api_resolution, *_rest = krea_node.select("1.0MP", "9:16") @@ -5888,6 +5896,14 @@ def smoke_seed_config_policy() -> None: _expect(parsed == {"item_seed": 44, "pose_seed": 55}, "seed parser should keep integer-like values only") _expect(pb._configured_axis_seed(parsed, "content") == 44, "content axis should honor item_seed alias") _expect(pb._configured_axis_seed(parsed, "role") == 55, "role axis should honor pose seed alias") + _expect( + seed_config.configured_seed_from_axes({"camera_seed": "88", "content_seed": "99"}, ("composition", "content")) == 88, + "seed helper should honor axis aliases in precedence order", + ) + _expect( + seed_config.configured_seed_from_axes('{"bad": "json"', ("content",)) is None, + "seed helper should return no seed for invalid config JSON", + ) locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="content pose", reroll_seed=999)) _expect(locked["content_seed"] == 999, "content_pose reroll should alter content seed") @@ -6089,6 +6105,11 @@ def smoke_node_route_config_registration() -> None: _expect(bias_a == bias_b, "Cast Bias should be deterministic for fixed seed and row") _expect(bias_a[1] + bias_a[2] >= 1, "Cast Bias empty behavior allowed empty cast") _expect("weighted cast:" in bias_a[3], "Cast Bias summary lost weighted cast label") + bias_seed_config = json.dumps({"category": 702}) + seeded_bias_a = cast_bias.build(-1, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman", bias_seed_config) + seeded_bias_b = cast_bias.build(-1, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman", bias_seed_config) + _expect(seeded_bias_a == seeded_bias_b, "Cast Bias should honor seed_config aliases deterministically") + _expect(cast_bias._configured_cast_seed({"category": "702"}) == 702, "Cast Bias seed should delegate through category aliases") def smoke_node_character_registration() -> None: