diff --git a/__init__.py b/__init__.py index 853cb7e..3cdfea7 100644 --- a/__init__.py +++ b/__init__.py @@ -416,6 +416,10 @@ try: NODE_CLASS_MAPPINGS as FORMATTER_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as FORMATTER_NODE_DISPLAY_NAME_MAPPINGS, ) + from .node_insta import ( + NODE_CLASS_MAPPINGS as INSTA_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as INSTA_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, @@ -429,15 +433,10 @@ try: NODE_DISPLAY_NAME_MAPPINGS as SEED_RESOLUTION_NODE_DISPLAY_NAME_MAPPINGS, ) from .prompt_builder import ( - build_insta_of_options_json, - build_insta_of_pair, build_prompt, build_prompt_from_configs, - camera_detail_choices, - camera_mode_choices, category_choices, ethnicity_choices, - hardcore_detail_density_choices, save_character_profile_payload, subcategory_choices, ) @@ -466,6 +465,10 @@ except ImportError: NODE_CLASS_MAPPINGS as FORMATTER_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as FORMATTER_NODE_DISPLAY_NAME_MAPPINGS, ) + from node_insta import ( + NODE_CLASS_MAPPINGS as INSTA_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as INSTA_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, @@ -479,15 +482,10 @@ except ImportError: NODE_DISPLAY_NAME_MAPPINGS as SEED_RESOLUTION_NODE_DISPLAY_NAME_MAPPINGS, ) from prompt_builder import ( - build_insta_of_options_json, - build_insta_of_pair, build_prompt, build_prompt_from_configs, - camera_detail_choices, - camera_mode_choices, category_choices, ethnicity_choices, - hardcore_detail_density_choices, save_character_profile_payload, subcategory_choices, ) @@ -764,186 +762,6 @@ class SxCPPromptBuilderFromConfigs: ) -class SxCPInstaOFOptions: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "softcore_cast": (["solo", "same_as_hardcore"], {"default": "solo"}), - "hardcore_cast": (["use_counts", "couple", "threesome", "group"], {"default": "use_counts"}), - "hardcore_women_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}), - "hardcore_men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}), - "softcore_level": (["social_tease", "lingerie_tease", "implied_nude", "explicit_tease", "explicit_nude"], {"default": "lingerie_tease"}), - "hardcore_level": (["explicit", "hardcore"], {"default": "hardcore"}), - "softcore_expression_enabled": ("BOOLEAN", {"default": True}), - "hardcore_expression_enabled": ("BOOLEAN", {"default": True}), - "softcore_expression_intensity": ("FLOAT", {"default": 0.45, "min": 0.0, "max": 1.0, "step": 0.01}), - "hardcore_expression_intensity": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01}), - "platform_style": (["hybrid", "instagram", "onlyfans"], {"default": "hybrid"}), - "continuity": (["same_creator_same_room", "same_creator_new_scene"], {"default": "same_creator_same_room"}), - "hardcore_clothing_continuity": (["none", "same_outfit", "partially_removed", "implied_nude", "explicit_nude"], {"default": "partially_removed"}), - "softcore_camera_mode": (["from_camera_config"] + camera_mode_choices(), {"default": "handheld_selfie"}), - "hardcore_camera_mode": (["from_camera_config", "same_as_softcore"] + camera_mode_choices(), {"default": "from_camera_config"}), - "camera_detail": (["from_camera_config"] + camera_detail_choices(), {"default": "from_camera_config"}), - "hardcore_detail_density": (hardcore_detail_density_choices(), {"default": "balanced"}), - } - } - - RETURN_TYPES = (SXCP_INSTA_OF_OPTIONS,) - RETURN_NAMES = ("options_json",) - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - softcore_cast, - hardcore_cast, - hardcore_women_count, - hardcore_men_count, - softcore_level, - hardcore_level, - softcore_expression_enabled, - hardcore_expression_enabled, - softcore_expression_intensity, - hardcore_expression_intensity, - platform_style, - continuity, - hardcore_clothing_continuity, - softcore_camera_mode, - hardcore_camera_mode, - camera_detail, - hardcore_detail_density, - ): - return ( - build_insta_of_options_json( - softcore_cast=softcore_cast, - hardcore_cast=hardcore_cast, - hardcore_women_count=hardcore_women_count, - hardcore_men_count=hardcore_men_count, - softcore_level=softcore_level, - hardcore_level=hardcore_level, - softcore_expression_enabled=softcore_expression_enabled, - hardcore_expression_enabled=hardcore_expression_enabled, - softcore_expression_intensity=softcore_expression_intensity, - hardcore_expression_intensity=hardcore_expression_intensity, - platform_style=platform_style, - continuity=continuity, - hardcore_clothing_continuity=hardcore_clothing_continuity, - softcore_camera_mode=softcore_camera_mode, - hardcore_camera_mode=hardcore_camera_mode, - camera_detail=camera_detail, - hardcore_detail_density=hardcore_detail_density, - ), - ) - - -class SxCPInstaOFPromptPair: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "row_number": ("INT", {"default": 1, "min": 1, "max": 1000000, "step": 1}), - "start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}), - "seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}), - "ethnicity": (ethnicity_choices(), {"default": "any"}), - "figure": (["random", "curvy", "balanced", "bombshell"], {"default": "random"}), - "trigger": ("STRING", {"default": "sxcpinup_coloredpencil"}), - "prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}), - }, - "optional": { - "seed_config": (SXCP_SEED_CONFIG,), - "options_json": (SXCP_INSTA_OF_OPTIONS,), - "filter_config": (SXCP_FILTER_CONFIG,), - "ethnicity_list": (SXCP_ETHNICITY_LIST,), - "camera_config": (SXCP_CAMERA_CONFIG,), - "softcore_camera_config": (SXCP_CAMERA_CONFIG,), - "hardcore_camera_config": (SXCP_CAMERA_CONFIG,), - "location_config": (SXCP_LOCATION_CONFIG,), - "composition_config": (SXCP_COMPOSITION_CONFIG,), - "character_profile": (SXCP_CHARACTER_PROFILE,), - "character_cast": (SXCP_CHARACTER_CAST,), - "hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,), - "extra_positive": ("STRING", {"default": "", "multiline": True}), - "extra_negative": ("STRING", {"default": "", "multiline": True}), - }, - } - - RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") - RETURN_NAMES = ( - "softcore_prompt", - "hardcore_prompt", - "softcore_negative_prompt", - "hardcore_negative_prompt", - "softcore_caption", - "hardcore_caption", - "shared_descriptor", - "metadata_json", - ) - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - row_number, - start_index, - seed, - ethnicity, - figure, - trigger, - prepend_trigger_to_prompt, - seed_config="", - options_json="", - filter_config="", - ethnicity_list="", - camera_config="", - softcore_camera_config="", - hardcore_camera_config="", - location_config="", - composition_config="", - character_profile="", - character_cast="", - hardcore_position_config="", - extra_positive="", - extra_negative="", - no_plus_women=False, - no_black=False, - ): - row = build_insta_of_pair( - row_number=row_number, - start_index=start_index, - seed=seed, - ethnicity=ethnicity, - figure=figure, - no_plus_women=no_plus_women, - no_black=no_black, - trigger=trigger, - prepend_trigger_to_prompt=prepend_trigger_to_prompt, - seed_config=seed_config or "", - options_json=options_json or "", - filter_config=ethnicity_list or filter_config or "", - camera_config=camera_config or "", - softcore_camera_config=softcore_camera_config or "", - hardcore_camera_config=hardcore_camera_config or "", - location_config=location_config or "", - composition_config=composition_config or "", - character_profile=character_profile or "", - character_cast=character_cast or "", - hardcore_position_config=hardcore_position_config or "", - extra_positive=extra_positive or "", - extra_negative=extra_negative or "", - ) - return ( - row["softcore_prompt"], - row["hardcore_prompt"], - row["softcore_negative_prompt"], - row["hardcore_negative_prompt"], - row["softcore_caption"], - row["hardcore_caption"], - row["shared_descriptor"], - json.dumps(row, ensure_ascii=True, sort_keys=True), - ) - - NODE_CLASS_MAPPINGS = { "SxCPPromptBuilder": SxCPPromptBuilder, } @@ -952,12 +770,11 @@ NODE_CLASS_MAPPINGS.update(CAMERA_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(CHARACTER_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(HARDCORE_POSITION_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(FORMATTER_NODE_CLASS_MAPPINGS) +NODE_CLASS_MAPPINGS.update(INSTA_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({ "SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs, - "SxCPInstaOFOptions": SxCPInstaOFOptions, - "SxCPInstaOFPromptPair": SxCPInstaOFPromptPair, }) NODE_CLASS_MAPPINGS.update(LOOP_NODE_CLASS_MAPPINGS) _install_input_tooltips(NODE_CLASS_MAPPINGS) @@ -970,12 +787,11 @@ NODE_DISPLAY_NAME_MAPPINGS.update(CAMERA_NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(CHARACTER_NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(HARDCORE_POSITION_NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(FORMATTER_NODE_DISPLAY_NAME_MAPPINGS) +NODE_DISPLAY_NAME_MAPPINGS.update(INSTA_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({ "SxCPPromptBuilderFromConfigs": "SxCP Prompt Builder From Configs", - "SxCPInstaOFOptions": "SxCP Insta/OF Options", - "SxCPInstaOFPromptPair": "SxCP Insta/OF Prompt Pair", }) NODE_DISPLAY_NAME_MAPPINGS.update(LOOP_NODE_DISPLAY_NAME_MAPPINGS) diff --git a/docs/prompt-architecture-improvement-plan.md b/docs/prompt-architecture-improvement-plan.md index 87f3b6d..cf39039 100644 --- a/docs/prompt-architecture-improvement-plan.md +++ b/docs/prompt-architecture-improvement-plan.md @@ -274,7 +274,8 @@ Improve later: Owner: `__init__.py`, `node_seed_resolution.py`, `node_camera.py`, `node_character.py`, `node_hardcore_position.py`, `node_formatter.py`, -`node_route_config.py`, `node_profile_filter.py`, `loop_nodes.py`, `web/*.js`. +`node_insta.py`, `node_route_config.py`, `node_profile_filter.py`, +`loop_nodes.py`, `web/*.js`. Keep here: @@ -288,6 +289,7 @@ Keep here: - hardcore position pool/filter node declarations in `node_hardcore_position.py`. - caption/Krea2/SDXL formatter node declarations in `node_formatter.py`. +- Insta/OF options and prompt-pair node declarations in `node_insta.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`. @@ -306,6 +308,8 @@ Already isolated: `__init__.py`. - caption naturalizer, Krea2 formatter, and SDXL formatter nodes live in `node_formatter.py`, with registration maps imported by `__init__.py`. +- Insta/OF options and dual prompt-pair nodes live in `node_insta.py`, with + registration maps imported by `__init__.py`. - 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`. diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md index 0bc5192..4ba9b6e 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -26,8 +26,9 @@ When a result is wrong, first identify which layer owns the bad text: - 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`, `node_character.py`, - `node_hardcore_position.py`, `node_formatter.py`, `node_route_config.py`, or - `node_profile_filter.py`, `loop_nodes.py`, or `web/*.js`. + `node_hardcore_position.py`, `node_formatter.py`, `node_insta.py`, + `node_route_config.py`, or `node_profile_filter.py`, `loop_nodes.py`, or + `web/*.js`. ## High-Level Routes @@ -698,6 +699,7 @@ These do not own prompt pool wording, but they affect execution and review: | Character utility nodes | `node_character.py`, imported by `__init__.py` | Hair, age/body/eyes/clothing pools, manual details, character slots, and profile save/load nodes. | | Hardcore position utility nodes | `node_hardcore_position.py`, imported by `__init__.py` | Position-family pool and action/filter gates for hardcore routes. | | Formatter utility nodes | `node_formatter.py`, imported by `__init__.py` | Caption naturalizer, Krea2 formatter, and SDXL formatter node wrappers. | +| Insta/OF utility nodes | `node_insta.py`, imported by `__init__.py` | Insta/OF option config and dual prompt-pair node wrappers. | | 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. | diff --git a/node_insta.py b/node_insta.py new file mode 100644 index 0000000..6006661 --- /dev/null +++ b/node_insta.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +import json + +try: + from .prompt_builder import ( + build_insta_of_options_json, + build_insta_of_pair, + camera_detail_choices, + camera_mode_choices, + ethnicity_choices, + hardcore_detail_density_choices, + ) +except ImportError: # Allows local smoke tests from the repository root. + from prompt_builder import ( + build_insta_of_options_json, + build_insta_of_pair, + camera_detail_choices, + camera_mode_choices, + ethnicity_choices, + hardcore_detail_density_choices, + ) + + +SXCP_SEED_CONFIG = "SXCP_SEED_CONFIG" +SXCP_CAMERA_CONFIG = "SXCP_CAMERA_CONFIG" +SXCP_LOCATION_CONFIG = "SXCP_LOCATION_CONFIG" +SXCP_COMPOSITION_CONFIG = "SXCP_COMPOSITION_CONFIG" +SXCP_INSTA_OF_OPTIONS = "SXCP_INSTA_OF_OPTIONS" +SXCP_HARDCORE_POSITION_CONFIG = "SXCP_HARDCORE_POSITION_CONFIG" +SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST" +SXCP_CHARACTER_PROFILE = "SXCP_CHARACTER_PROFILE" +SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST" +SXCP_FILTER_CONFIG = "SXCP_FILTER_CONFIG" + + +class SxCPInstaOFOptions: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "softcore_cast": (["solo", "same_as_hardcore"], {"default": "solo"}), + "hardcore_cast": (["use_counts", "couple", "threesome", "group"], {"default": "use_counts"}), + "hardcore_women_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}), + "hardcore_men_count": ("INT", {"default": 1, "min": 0, "max": 12, "step": 1}), + "softcore_level": (["social_tease", "lingerie_tease", "implied_nude", "explicit_tease", "explicit_nude"], {"default": "lingerie_tease"}), + "hardcore_level": (["explicit", "hardcore"], {"default": "hardcore"}), + "softcore_expression_enabled": ("BOOLEAN", {"default": True}), + "hardcore_expression_enabled": ("BOOLEAN", {"default": True}), + "softcore_expression_intensity": ("FLOAT", {"default": 0.45, "min": 0.0, "max": 1.0, "step": 0.01}), + "hardcore_expression_intensity": ("FLOAT", {"default": 0.85, "min": 0.0, "max": 1.0, "step": 0.01}), + "platform_style": (["hybrid", "instagram", "onlyfans"], {"default": "hybrid"}), + "continuity": (["same_creator_same_room", "same_creator_new_scene"], {"default": "same_creator_same_room"}), + "hardcore_clothing_continuity": (["none", "same_outfit", "partially_removed", "implied_nude", "explicit_nude"], {"default": "partially_removed"}), + "softcore_camera_mode": (["from_camera_config"] + camera_mode_choices(), {"default": "handheld_selfie"}), + "hardcore_camera_mode": (["from_camera_config", "same_as_softcore"] + camera_mode_choices(), {"default": "from_camera_config"}), + "camera_detail": (["from_camera_config"] + camera_detail_choices(), {"default": "from_camera_config"}), + "hardcore_detail_density": (hardcore_detail_density_choices(), {"default": "balanced"}), + } + } + + RETURN_TYPES = (SXCP_INSTA_OF_OPTIONS,) + RETURN_NAMES = ("options_json",) + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + softcore_cast, + hardcore_cast, + hardcore_women_count, + hardcore_men_count, + softcore_level, + hardcore_level, + softcore_expression_enabled, + hardcore_expression_enabled, + softcore_expression_intensity, + hardcore_expression_intensity, + platform_style, + continuity, + hardcore_clothing_continuity, + softcore_camera_mode, + hardcore_camera_mode, + camera_detail, + hardcore_detail_density, + ): + return ( + build_insta_of_options_json( + softcore_cast=softcore_cast, + hardcore_cast=hardcore_cast, + hardcore_women_count=hardcore_women_count, + hardcore_men_count=hardcore_men_count, + softcore_level=softcore_level, + hardcore_level=hardcore_level, + softcore_expression_enabled=softcore_expression_enabled, + hardcore_expression_enabled=hardcore_expression_enabled, + softcore_expression_intensity=softcore_expression_intensity, + hardcore_expression_intensity=hardcore_expression_intensity, + platform_style=platform_style, + continuity=continuity, + hardcore_clothing_continuity=hardcore_clothing_continuity, + softcore_camera_mode=softcore_camera_mode, + hardcore_camera_mode=hardcore_camera_mode, + camera_detail=camera_detail, + hardcore_detail_density=hardcore_detail_density, + ), + ) + + +class SxCPInstaOFPromptPair: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "row_number": ("INT", {"default": 1, "min": 1, "max": 1000000, "step": 1}), + "start_index": ("INT", {"default": 41, "min": 1, "max": 1000000, "step": 1}), + "seed": ("INT", {"default": 20260614, "min": 0, "max": 0xFFFFFFFF, "step": 1}), + "ethnicity": (ethnicity_choices(), {"default": "any"}), + "figure": (["random", "curvy", "balanced", "bombshell"], {"default": "random"}), + "trigger": ("STRING", {"default": "sxcpinup_coloredpencil"}), + "prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}), + }, + "optional": { + "seed_config": (SXCP_SEED_CONFIG,), + "options_json": (SXCP_INSTA_OF_OPTIONS,), + "filter_config": (SXCP_FILTER_CONFIG,), + "ethnicity_list": (SXCP_ETHNICITY_LIST,), + "camera_config": (SXCP_CAMERA_CONFIG,), + "softcore_camera_config": (SXCP_CAMERA_CONFIG,), + "hardcore_camera_config": (SXCP_CAMERA_CONFIG,), + "location_config": (SXCP_LOCATION_CONFIG,), + "composition_config": (SXCP_COMPOSITION_CONFIG,), + "character_profile": (SXCP_CHARACTER_PROFILE,), + "character_cast": (SXCP_CHARACTER_CAST,), + "hardcore_position_config": (SXCP_HARDCORE_POSITION_CONFIG,), + "extra_positive": ("STRING", {"default": "", "multiline": True}), + "extra_negative": ("STRING", {"default": "", "multiline": True}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ( + "softcore_prompt", + "hardcore_prompt", + "softcore_negative_prompt", + "hardcore_negative_prompt", + "softcore_caption", + "hardcore_caption", + "shared_descriptor", + "metadata_json", + ) + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + row_number, + start_index, + seed, + ethnicity, + figure, + trigger, + prepend_trigger_to_prompt, + seed_config="", + options_json="", + filter_config="", + ethnicity_list="", + camera_config="", + softcore_camera_config="", + hardcore_camera_config="", + location_config="", + composition_config="", + character_profile="", + character_cast="", + hardcore_position_config="", + extra_positive="", + extra_negative="", + no_plus_women=False, + no_black=False, + ): + row = build_insta_of_pair( + row_number=row_number, + start_index=start_index, + seed=seed, + ethnicity=ethnicity, + figure=figure, + no_plus_women=no_plus_women, + no_black=no_black, + trigger=trigger, + prepend_trigger_to_prompt=prepend_trigger_to_prompt, + seed_config=seed_config or "", + options_json=options_json or "", + filter_config=ethnicity_list or filter_config or "", + camera_config=camera_config or "", + softcore_camera_config=softcore_camera_config or "", + hardcore_camera_config=hardcore_camera_config or "", + location_config=location_config or "", + composition_config=composition_config or "", + character_profile=character_profile or "", + character_cast=character_cast or "", + hardcore_position_config=hardcore_position_config or "", + extra_positive=extra_positive or "", + extra_negative=extra_negative or "", + ) + return ( + row["softcore_prompt"], + row["hardcore_prompt"], + row["softcore_negative_prompt"], + row["hardcore_negative_prompt"], + row["softcore_caption"], + row["hardcore_caption"], + row["shared_descriptor"], + json.dumps(row, ensure_ascii=True, sort_keys=True), + ) + + +NODE_CLASS_MAPPINGS = { + "SxCPInstaOFOptions": SxCPInstaOFOptions, + "SxCPInstaOFPromptPair": SxCPInstaOFPromptPair, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "SxCPInstaOFOptions": "SxCP Insta/OF Options", + "SxCPInstaOFPromptPair": "SxCP Insta/OF Prompt Pair", +} diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index d5f6876..0a3b81b 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -2115,6 +2115,60 @@ def smoke_node_formatter_registration() -> None: _expect(sdxl_output[6].startswith("text("), "SDXL Formatter method changed unexpectedly") +def smoke_node_insta_registration() -> None: + required_nodes = [ + "SxCPInstaOFOptions", + "SxCPInstaOFPromptPair", + ] + 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") + + options_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPInstaOFOptions"] + options_inputs = options_node.INPUT_TYPES().get("required") or {} + _expect("hardcore_detail_density" in options_inputs, "Insta/OF Options lost hardcore_detail_density input") + _expect("tooltip" in options_inputs["hardcore_detail_density"][1], "Insta/OF Options tooltip injection missing") + options_json = options_node().build( + "same_as_hardcore", + "couple", + 1, + 1, + "lingerie_tease", + "hardcore", + True, + True, + 0.45, + 0.85, + "hybrid", + "same_creator_same_room", + "explicit_nude", + "standard", + "standard", + "compact", + "balanced", + )[0] + parsed_options = json.loads(options_json) + _expect(parsed_options.get("softcore_cast") == "same_as_hardcore", "Insta/OF Options lost softcore cast") + _expect(parsed_options.get("hardcore_cast") == "couple", "Insta/OF Options lost hardcore cast") + _expect(parsed_options.get("hardcore_clothing_continuity") == "explicit_nude", "Insta/OF Options lost clothing continuity") + + pair_output = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPInstaOFPromptPair"]().build( + 1, + 41, + 123, + "any", + "random", + Trigger, + True, + options_json=options_json, + ) + _expect_text("node_insta.softcore_prompt", pair_output[0], 20) + _expect_text("node_insta.hardcore_prompt", pair_output[1], 20) + pair = json.loads(pair_output[7]) + _expect_pair(pair, "node_insta_pair") + _expect(pair.get("options", {}).get("hardcore_cast") == "couple", "Insta/OF Prompt Pair lost options metadata") + + def smoke_node_profile_filter_registration() -> None: required_nodes = [ "SxCPGenerationProfile", @@ -2225,6 +2279,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [ ("node_character_registration", smoke_node_character_registration), ("node_hardcore_position_registration", smoke_node_hardcore_position_registration), ("node_formatter_registration", smoke_node_formatter_registration), + ("node_insta_registration", smoke_node_insta_registration), ("node_profile_filter_registration", smoke_node_profile_filter_registration), ]