From efe13beb7986285ce41c6020c957078ec4fec5ba Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Fri, 26 Jun 2026 22:53:34 +0200 Subject: [PATCH] Extract profile filter nodes --- __init__.py | 231 +----------------- docs/prompt-architecture-improvement-plan.md | 5 +- docs/prompt-pool-routing-map.md | 5 +- node_profile_filter.py | 243 +++++++++++++++++++ tools/prompt_smoke.py | 82 +++++++ 5 files changed, 342 insertions(+), 224 deletions(-) create mode 100644 node_profile_filter.py diff --git a/__init__.py b/__init__.py index f89d987..88acfab 100644 --- a/__init__.py +++ b/__init__.py @@ -404,6 +404,10 @@ try: NODE_CLASS_MAPPINGS as CAMERA_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as CAMERA_NODE_DISPLAY_NAME_MAPPINGS, ) + from .node_profile_filter import ( + NODE_CLASS_MAPPINGS as PROFILE_FILTER_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as PROFILE_FILTER_NODE_DISPLAY_NAME_MAPPINGS, + ) from .node_route_config import ( NODE_CLASS_MAPPINGS as ROUTE_CONFIG_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as ROUTE_CONFIG_NODE_DISPLAY_NAME_MAPPINGS, @@ -417,9 +421,6 @@ try: build_character_manual_config_json, build_character_profile_json, build_characteristics_config_json, - build_ethnicity_list_json, - build_filter_config_json, - build_generation_profile_json, build_hair_config_json, build_hardcore_action_filter_json, build_hardcore_position_pool_json, @@ -449,7 +450,6 @@ try: character_softcore_outfit_values, character_woman_body_choices, ethnicity_choices, - generation_profile_choices, hardcore_position_family_choices, hardcore_position_focus_choices, hardcore_position_key_choices, @@ -474,6 +474,10 @@ except ImportError: NODE_CLASS_MAPPINGS as CAMERA_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as CAMERA_NODE_DISPLAY_NAME_MAPPINGS, ) + from node_profile_filter import ( + NODE_CLASS_MAPPINGS as PROFILE_FILTER_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as PROFILE_FILTER_NODE_DISPLAY_NAME_MAPPINGS, + ) from node_route_config import ( NODE_CLASS_MAPPINGS as ROUTE_CONFIG_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as ROUTE_CONFIG_NODE_DISPLAY_NAME_MAPPINGS, @@ -487,9 +491,6 @@ except ImportError: build_character_manual_config_json, build_character_profile_json, build_characteristics_config_json, - build_ethnicity_list_json, - build_filter_config_json, - build_generation_profile_json, build_hair_config_json, build_hardcore_action_filter_json, build_hardcore_position_pool_json, @@ -519,7 +520,6 @@ except ImportError: character_softcore_outfit_values, character_woman_body_choices, ethnicity_choices, - generation_profile_choices, hardcore_position_family_choices, hardcore_position_focus_choices, hardcore_position_key_choices, @@ -724,213 +724,6 @@ class SxCPPromptBuilder: ) -class SxCPGenerationProfile: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "profile": (generation_profile_choices(), {"default": "balanced"}), - "clothing_override": (["profile_default", "random", "full", "minimal"], {"default": "profile_default"}), - "poses_override": (["profile_default", "random", "standard", "evocative"], {"default": "profile_default"}), - "expression_enabled": ("BOOLEAN", {"default": True}), - "expression_intensity_mode": (["profile_default", "random", "fixed"], {"default": "profile_default"}), - "expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), - "backside_bias": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), - "minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), - "standard_pose_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), - "trigger_policy": (["profile_default", "prepend_trigger", "do_not_prepend"], {"default": "profile_default"}), - } - } - - RETURN_TYPES = (SXCP_GENERATION_PROFILE, "STRING") - RETURN_NAMES = ("generation_profile", "summary") - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - profile, - clothing_override, - poses_override, - expression_enabled, - expression_intensity_mode, - expression_intensity, - backside_bias, - minimal_clothing_ratio, - standard_pose_ratio, - trigger_policy, - ): - config = build_generation_profile_json( - profile=profile, - clothing_override=clothing_override, - poses_override=poses_override, - expression_enabled=expression_enabled, - expression_intensity_mode=expression_intensity_mode, - expression_intensity=expression_intensity, - backside_bias=backside_bias, - minimal_clothing_ratio=minimal_clothing_ratio, - standard_pose_ratio=standard_pose_ratio, - trigger_policy=trigger_policy, - ) - parsed = json.loads(config) - if not parsed.get("expression_enabled", True): - expression_summary = "expression disabled" - elif float(parsed.get("expression_intensity", 0.5)) < 0: - expression_summary = "expression random" - else: - expression_summary = f"expression {parsed['expression_intensity']}" - summary = f"{parsed['profile']}: {parsed['clothing']}, {parsed['poses']}, {expression_summary}" - return config, summary - - -class SxCPAdvancedFilters: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "include_european": ("BOOLEAN", {"default": True}), - "include_mediterranean_mena": ("BOOLEAN", {"default": True}), - "include_latina": ("BOOLEAN", {"default": True}), - "include_east_asian": ("BOOLEAN", {"default": True}), - "include_southeast_asian": ("BOOLEAN", {"default": True}), - "include_south_asian": ("BOOLEAN", {"default": True}), - "include_black_african": ("BOOLEAN", {"default": True}), - "include_indigenous": ("BOOLEAN", {"default": True}), - "include_mixed": ("BOOLEAN", {"default": True}), - "include_plus_size": ("BOOLEAN", {"default": True}), - "figure": (["random", "curvy", "balanced", "bombshell"], {"default": "random"}), - } - } - - RETURN_TYPES = (SXCP_FILTER_CONFIG,) - RETURN_NAMES = ("filter_config",) - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - include_european, - include_mediterranean_mena, - include_latina, - include_east_asian, - include_southeast_asian, - include_south_asian, - include_black_african, - include_indigenous, - include_mixed, - include_plus_size, - figure, - ): - return ( - build_filter_config_json( - figure=figure, - include_european=include_european, - include_mediterranean_mena=include_mediterranean_mena, - include_latina=include_latina, - include_east_asian=include_east_asian, - include_southeast_asian=include_southeast_asian, - include_south_asian=include_south_asian, - include_black_african=include_black_african, - include_indigenous=include_indigenous, - include_mixed=include_mixed, - include_plus_size=include_plus_size, - ), - ) - - -class SxCPEthnicityList: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "include_european": ("BOOLEAN", {"default": False}), - "include_mediterranean_mena": ("BOOLEAN", {"default": False}), - "include_latina": ("BOOLEAN", {"default": False}), - "include_east_asian": ("BOOLEAN", {"default": False}), - "include_southeast_asian": ("BOOLEAN", {"default": False}), - "include_south_asian": ("BOOLEAN", {"default": False}), - "include_black_african": ("BOOLEAN", {"default": False}), - "include_indigenous": ("BOOLEAN", {"default": False}), - "include_mixed": ("BOOLEAN", {"default": False}), - "include_asian": ("BOOLEAN", {"default": False}), - "include_white_asian": ("BOOLEAN", {"default": False}), - "include_western_european": ("BOOLEAN", {"default": False}), - "include_french_european": ("BOOLEAN", {"default": False}), - "include_germanic_european": ("BOOLEAN", {"default": False}), - "include_nordic_european": ("BOOLEAN", {"default": False}), - "include_celtic_european": ("BOOLEAN", {"default": False}), - "include_slavic_european": ("BOOLEAN", {"default": False}), - "include_baltic_european": ("BOOLEAN", {"default": False}), - "include_alpine_european": ("BOOLEAN", {"default": False}), - "include_balkan_european": ("BOOLEAN", {"default": False}), - "include_greek_mediterranean": ("BOOLEAN", {"default": False}), - "include_italian_mediterranean": ("BOOLEAN", {"default": False}), - "include_iberian_mediterranean": ("BOOLEAN", {"default": False}), - "strict_excludes": ("BOOLEAN", {"default": True}), - } - } - - RETURN_TYPES = (SXCP_ETHNICITY_LIST, SXCP_FILTER_CONFIG, "STRING") - RETURN_NAMES = ("ethnicity_list", "filter_config", "summary") - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - include_european, - include_mediterranean_mena, - include_latina, - include_east_asian, - include_southeast_asian, - include_south_asian, - include_black_african, - include_indigenous, - include_mixed, - include_asian, - include_white_asian, - include_western_european, - include_french_european, - include_germanic_european, - include_nordic_european, - include_celtic_european, - include_slavic_european, - include_baltic_european, - include_alpine_european, - include_balkan_european, - include_greek_mediterranean, - include_italian_mediterranean, - include_iberian_mediterranean, - strict_excludes, - ): - result = build_ethnicity_list_json( - include_european=include_european, - include_mediterranean_mena=include_mediterranean_mena, - include_latina=include_latina, - include_east_asian=include_east_asian, - include_southeast_asian=include_southeast_asian, - include_south_asian=include_south_asian, - include_black_african=include_black_african, - include_indigenous=include_indigenous, - include_mixed=include_mixed, - include_asian=include_asian, - include_white_asian=include_white_asian, - include_western_european=include_western_european, - include_french_european=include_french_european, - include_germanic_european=include_germanic_european, - include_nordic_european=include_nordic_european, - include_celtic_european=include_celtic_european, - include_slavic_european=include_slavic_european, - include_baltic_european=include_baltic_european, - include_alpine_european=include_alpine_european, - include_balkan_european=include_balkan_european, - include_greek_mediterranean=include_greek_mediterranean, - include_italian_mediterranean=include_italian_mediterranean, - include_iberian_mediterranean=include_iberian_mediterranean, - strict_excludes=strict_excludes, - ) - return result["ethnicity"], result["filter_config"], result["summary"] - - class _SxCPHairAxisNode: AXIS = "color" PREFIX = "include" @@ -2199,9 +1992,8 @@ NODE_CLASS_MAPPINGS = { NODE_CLASS_MAPPINGS.update(SEED_RESOLUTION_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(CAMERA_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(ROUTE_CONFIG_NODE_CLASS_MAPPINGS) +NODE_CLASS_MAPPINGS.update(PROFILE_FILTER_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update({ - "SxCPGenerationProfile": SxCPGenerationProfile, - "SxCPEthnicityList": SxCPEthnicityList, "SxCPHairLength": SxCPHairLength, "SxCPHairColor": SxCPHairColor, "SxCPHairStyle": SxCPHairStyle, @@ -2214,7 +2006,6 @@ NODE_CLASS_MAPPINGS.update({ "SxCPHardcorePositionPool": SxCPHardcorePositionPool, "SxCPHardcoreActionFilter": SxCPHardcoreActionFilter, "SxCPCharacterManualDetails": SxCPCharacterManualDetails, - "SxCPAdvancedFilters": SxCPAdvancedFilters, "SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs, "SxCPWomanSlot": SxCPWomanSlot, "SxCPManSlot": SxCPManSlot, @@ -2236,9 +2027,8 @@ NODE_DISPLAY_NAME_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS.update(SEED_RESOLUTION_NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(CAMERA_NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(ROUTE_CONFIG_NODE_DISPLAY_NAME_MAPPINGS) +NODE_DISPLAY_NAME_MAPPINGS.update(PROFILE_FILTER_NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update({ - "SxCPGenerationProfile": "SxCP Generation Profile", - "SxCPEthnicityList": "SxCP Ethnicity List", "SxCPHairLength": "SxCP Hair Length", "SxCPHairColor": "SxCP Hair Color", "SxCPHairStyle": "SxCP Hair Style/Cut", @@ -2251,7 +2041,6 @@ NODE_DISPLAY_NAME_MAPPINGS.update({ "SxCPHardcorePositionPool": "SxCP Hardcore Position Pool", "SxCPHardcoreActionFilter": "SxCP Hardcore Action Filter", "SxCPCharacterManualDetails": "SxCP Character Manual Details", - "SxCPAdvancedFilters": "SxCP Advanced Filters", "SxCPPromptBuilderFromConfigs": "SxCP Prompt Builder From Configs", "SxCPWomanSlot": "SxCP Woman Slot", "SxCPManSlot": "SxCP Man Slot", diff --git a/docs/prompt-architecture-improvement-plan.md b/docs/prompt-architecture-improvement-plan.md index a0fa82f..a1bfade 100644 --- a/docs/prompt-architecture-improvement-plan.md +++ b/docs/prompt-architecture-improvement-plan.md @@ -273,7 +273,7 @@ Improve later: ### Node / UI Path Owner: `__init__.py`, `node_seed_resolution.py`, `node_camera.py`, -`node_route_config.py`, `loop_nodes.py`, `web/*.js`. +`node_route_config.py`, `node_profile_filter.py`, `loop_nodes.py`, `web/*.js`. Keep here: @@ -285,6 +285,7 @@ Keep here: - camera utility node declarations in `node_camera.py`. - route/category/location/composition/cast config node declarations in `node_route_config.py`. +- profile/filter/ethnicity-list node declarations in `node_profile_filter.py`. Already isolated: @@ -295,6 +296,8 @@ Already isolated: - category preset, location/composition pool, location theme, and cast config utility nodes live in `node_route_config.py`, with registration maps imported by `__init__.py`. +- generation profile, advanced filter, and ethnicity list utility nodes live in + `node_profile_filter.py`, with registration maps imported by `__init__.py`. Improve later: diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md index 25a66b7..d3bcbb0 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -25,8 +25,8 @@ When a result is wrong, first identify which layer owns the bad text: - Raw builder prompt acceptable, SDXL tags wrong: edit `sdxl_formatter.py`. - Natural caption/training caption wrong: edit `caption_naturalizer.py`. - UI/preview/loop behavior wrong: edit `__init__.py`, node family modules such - as `node_seed_resolution.py`, `node_camera.py`, or `node_route_config.py`, - `loop_nodes.py`, or `web/*.js`. + as `node_seed_resolution.py`, `node_camera.py`, `node_route_config.py`, or + `node_profile_filter.py`, `loop_nodes.py`, or `web/*.js`. ## High-Level Routes @@ -695,6 +695,7 @@ These do not own prompt pool wording, but they affect execution and review: | Seed and resolution utility nodes | `node_seed_resolution.py`, imported by `__init__.py` | Global/per-axis seed configs plus SDXL/Krea width/height helpers. | | Camera utility nodes | `node_camera.py`, imported by `__init__.py` | Direct camera config, orbit-to-camera config, and Qwen MultiAngle camera translation. | | Route config utility nodes | `node_route_config.py`, imported by `__init__.py` | Category preset, location/composition pool, location theme, and cast config helpers. | +| Profile/filter utility nodes | `node_profile_filter.py`, imported by `__init__.py` | Generation profile, advanced filter config, and ethnicity list helpers. | ## Drift Audit Helper diff --git a/node_profile_filter.py b/node_profile_filter.py new file mode 100644 index 0000000..af92af0 --- /dev/null +++ b/node_profile_filter.py @@ -0,0 +1,243 @@ +from __future__ import annotations + +import json + +try: + from .prompt_builder import ( + build_ethnicity_list_json, + build_filter_config_json, + build_generation_profile_json, + generation_profile_choices, + ) +except ImportError: # Allows local smoke tests from the repository root. + from prompt_builder import ( + build_ethnicity_list_json, + build_filter_config_json, + build_generation_profile_json, + generation_profile_choices, + ) + + +SXCP_GENERATION_PROFILE = "SXCP_GENERATION_PROFILE" +SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG" +SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST" + + +class SxCPGenerationProfile: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "profile": (generation_profile_choices(), {"default": "balanced"}), + "clothing_override": (["profile_default", "random", "full", "minimal"], {"default": "profile_default"}), + "poses_override": (["profile_default", "random", "standard", "evocative"], {"default": "profile_default"}), + "expression_enabled": ("BOOLEAN", {"default": True}), + "expression_intensity_mode": (["profile_default", "random", "fixed"], {"default": "profile_default"}), + "expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), + "backside_bias": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), + "minimal_clothing_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), + "standard_pose_ratio": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}), + "trigger_policy": (["profile_default", "prepend_trigger", "do_not_prepend"], {"default": "profile_default"}), + } + } + + RETURN_TYPES = (SXCP_GENERATION_PROFILE, "STRING") + RETURN_NAMES = ("generation_profile", "summary") + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + profile, + clothing_override, + poses_override, + expression_enabled, + expression_intensity_mode, + expression_intensity, + backside_bias, + minimal_clothing_ratio, + standard_pose_ratio, + trigger_policy, + ): + config = build_generation_profile_json( + profile=profile, + clothing_override=clothing_override, + poses_override=poses_override, + expression_enabled=expression_enabled, + expression_intensity_mode=expression_intensity_mode, + expression_intensity=expression_intensity, + backside_bias=backside_bias, + minimal_clothing_ratio=minimal_clothing_ratio, + standard_pose_ratio=standard_pose_ratio, + trigger_policy=trigger_policy, + ) + parsed = json.loads(config) + if not parsed.get("expression_enabled", True): + expression_summary = "expression disabled" + elif float(parsed.get("expression_intensity", 0.5)) < 0: + expression_summary = "expression random" + else: + expression_summary = f"expression {parsed['expression_intensity']}" + summary = f"{parsed['profile']}: {parsed['clothing']}, {parsed['poses']}, {expression_summary}" + return config, summary + + +class SxCPAdvancedFilters: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "include_european": ("BOOLEAN", {"default": True}), + "include_mediterranean_mena": ("BOOLEAN", {"default": True}), + "include_latina": ("BOOLEAN", {"default": True}), + "include_east_asian": ("BOOLEAN", {"default": True}), + "include_southeast_asian": ("BOOLEAN", {"default": True}), + "include_south_asian": ("BOOLEAN", {"default": True}), + "include_black_african": ("BOOLEAN", {"default": True}), + "include_indigenous": ("BOOLEAN", {"default": True}), + "include_mixed": ("BOOLEAN", {"default": True}), + "include_plus_size": ("BOOLEAN", {"default": True}), + "figure": (["random", "curvy", "balanced", "bombshell"], {"default": "random"}), + } + } + + RETURN_TYPES = (SXCP_FILTER_CONFIG,) + RETURN_NAMES = ("filter_config",) + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + include_european, + include_mediterranean_mena, + include_latina, + include_east_asian, + include_southeast_asian, + include_south_asian, + include_black_african, + include_indigenous, + include_mixed, + include_plus_size, + figure, + ): + return ( + build_filter_config_json( + figure=figure, + include_european=include_european, + include_mediterranean_mena=include_mediterranean_mena, + include_latina=include_latina, + include_east_asian=include_east_asian, + include_southeast_asian=include_southeast_asian, + include_south_asian=include_south_asian, + include_black_african=include_black_african, + include_indigenous=include_indigenous, + include_mixed=include_mixed, + include_plus_size=include_plus_size, + ), + ) + + +class SxCPEthnicityList: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "include_european": ("BOOLEAN", {"default": False}), + "include_mediterranean_mena": ("BOOLEAN", {"default": False}), + "include_latina": ("BOOLEAN", {"default": False}), + "include_east_asian": ("BOOLEAN", {"default": False}), + "include_southeast_asian": ("BOOLEAN", {"default": False}), + "include_south_asian": ("BOOLEAN", {"default": False}), + "include_black_african": ("BOOLEAN", {"default": False}), + "include_indigenous": ("BOOLEAN", {"default": False}), + "include_mixed": ("BOOLEAN", {"default": False}), + "include_asian": ("BOOLEAN", {"default": False}), + "include_white_asian": ("BOOLEAN", {"default": False}), + "include_western_european": ("BOOLEAN", {"default": False}), + "include_french_european": ("BOOLEAN", {"default": False}), + "include_germanic_european": ("BOOLEAN", {"default": False}), + "include_nordic_european": ("BOOLEAN", {"default": False}), + "include_celtic_european": ("BOOLEAN", {"default": False}), + "include_slavic_european": ("BOOLEAN", {"default": False}), + "include_baltic_european": ("BOOLEAN", {"default": False}), + "include_alpine_european": ("BOOLEAN", {"default": False}), + "include_balkan_european": ("BOOLEAN", {"default": False}), + "include_greek_mediterranean": ("BOOLEAN", {"default": False}), + "include_italian_mediterranean": ("BOOLEAN", {"default": False}), + "include_iberian_mediterranean": ("BOOLEAN", {"default": False}), + "strict_excludes": ("BOOLEAN", {"default": True}), + } + } + + RETURN_TYPES = (SXCP_ETHNICITY_LIST, SXCP_FILTER_CONFIG, "STRING") + RETURN_NAMES = ("ethnicity_list", "filter_config", "summary") + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + include_european, + include_mediterranean_mena, + include_latina, + include_east_asian, + include_southeast_asian, + include_south_asian, + include_black_african, + include_indigenous, + include_mixed, + include_asian, + include_white_asian, + include_western_european, + include_french_european, + include_germanic_european, + include_nordic_european, + include_celtic_european, + include_slavic_european, + include_baltic_european, + include_alpine_european, + include_balkan_european, + include_greek_mediterranean, + include_italian_mediterranean, + include_iberian_mediterranean, + strict_excludes, + ): + result = build_ethnicity_list_json( + include_european=include_european, + include_mediterranean_mena=include_mediterranean_mena, + include_latina=include_latina, + include_east_asian=include_east_asian, + include_southeast_asian=include_southeast_asian, + include_south_asian=include_south_asian, + include_black_african=include_black_african, + include_indigenous=include_indigenous, + include_mixed=include_mixed, + include_asian=include_asian, + include_white_asian=include_white_asian, + include_western_european=include_western_european, + include_french_european=include_french_european, + include_germanic_european=include_germanic_european, + include_nordic_european=include_nordic_european, + include_celtic_european=include_celtic_european, + include_slavic_european=include_slavic_european, + include_baltic_european=include_baltic_european, + include_alpine_european=include_alpine_european, + include_balkan_european=include_balkan_european, + include_greek_mediterranean=include_greek_mediterranean, + include_italian_mediterranean=include_italian_mediterranean, + include_iberian_mediterranean=include_iberian_mediterranean, + strict_excludes=strict_excludes, + ) + return result["ethnicity"], result["filter_config"], result["summary"] + + +NODE_CLASS_MAPPINGS = { + "SxCPGenerationProfile": SxCPGenerationProfile, + "SxCPAdvancedFilters": SxCPAdvancedFilters, + "SxCPEthnicityList": SxCPEthnicityList, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "SxCPGenerationProfile": "SxCP Generation Profile", + "SxCPAdvancedFilters": "SxCP Advanced Filters", + "SxCPEthnicityList": "SxCP Ethnicity List", +} diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 7c5baf6..4fa334a 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -1848,6 +1848,87 @@ def smoke_node_route_config_registration() -> None: _expect("weighted cast:" in bias_a[3], "Cast Bias summary lost weighted cast label") +def smoke_node_profile_filter_registration() -> None: + required_nodes = [ + "SxCPGenerationProfile", + "SxCPAdvancedFilters", + "SxCPEthnicityList", + ] + for node_name in required_nodes: + _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry") + _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry") + + profile_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPGenerationProfile"] + profile_inputs = profile_node.INPUT_TYPES().get("required") or {} + _expect("profile" in profile_inputs, "Generation Profile lost profile input") + _expect("tooltip" in profile_inputs["profile"][1], "Generation Profile tooltip injection missing") + profile_config, profile_summary = profile_node().build( + profile="balanced", + clothing_override="profile_default", + poses_override="profile_default", + expression_enabled=True, + expression_intensity_mode="fixed", + expression_intensity=0.5, + backside_bias=-1, + minimal_clothing_ratio=-1, + standard_pose_ratio=-1, + trigger_policy="profile_default", + ) + parsed_profile = json.loads(profile_config) + _expect(parsed_profile.get("profile") == "balanced", "Generation Profile output lost profile") + _expect(parsed_profile.get("expression_intensity") == 0.5, "Generation Profile output lost fixed expression intensity") + _expect("balanced:" in profile_summary, "Generation Profile summary changed unexpectedly") + + filter_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPAdvancedFilters"]().build( + include_european=True, + include_mediterranean_mena=False, + include_latina=False, + include_east_asian=False, + include_southeast_asian=False, + include_south_asian=False, + include_black_african=True, + include_indigenous=False, + include_mixed=False, + include_plus_size=False, + figure="curvy", + )[0] + parsed_filter = json.loads(filter_config) + _expect(parsed_filter.get("figure") == "curvy", "Advanced Filters lost figure") + _expect(parsed_filter.get("ethnicity_includes") == ["european", "black_african"], "Advanced Filters ethnicity includes changed") + _expect(parsed_filter.get("no_plus_women") is True, "Advanced Filters should set no_plus_women when plus size is excluded") + + ethnicity, ethnicity_filter_config, ethnicity_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPEthnicityList"]().build( + include_european=False, + include_mediterranean_mena=False, + include_latina=False, + include_east_asian=False, + include_southeast_asian=False, + include_south_asian=False, + include_black_african=False, + include_indigenous=False, + include_mixed=False, + include_asian=False, + include_white_asian=False, + include_western_european=False, + include_french_european=True, + include_germanic_european=False, + include_nordic_european=False, + include_celtic_european=False, + include_slavic_european=False, + include_baltic_european=False, + include_alpine_european=False, + include_balkan_european=False, + include_greek_mediterranean=False, + include_italian_mediterranean=False, + include_iberian_mediterranean=False, + strict_excludes=True, + ) + parsed_ethnicity_filter = json.loads(ethnicity_filter_config) + _expect("french_european" in ethnicity, "Ethnicity List output lost selected regional ethnicity") + _expect(parsed_ethnicity_filter.get("ethnicity_includes") == ["french_european"], "Ethnicity List filter output changed") + _expect("ethnicity list:" in ethnicity_summary, "Ethnicity List summary changed unexpectedly") + + SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [ ("builtin_single_woman", smoke_builtin_single), ("camera_scene_single", smoke_camera_scene_single), @@ -1874,6 +1955,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [ ("node_utility_registration", smoke_node_utility_registration), ("node_camera_registration", smoke_node_camera_registration), ("node_route_config_registration", smoke_node_route_config_registration), + ("node_profile_filter_registration", smoke_node_profile_filter_registration), ]