From c8c95db8353254dec3476b199dfce76c7691c6d3 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Fri, 26 Jun 2026 23:16:20 +0200 Subject: [PATCH] Extract formatter nodes --- __init__.py | 224 +----------------- docs/prompt-architecture-improvement-plan.md | 7 +- docs/prompt-pool-routing-map.md | 3 +- node_formatter.py | 225 +++++++++++++++++++ tools/prompt_smoke.py | 57 +++++ 5 files changed, 299 insertions(+), 217 deletions(-) create mode 100644 node_formatter.py diff --git a/__init__.py b/__init__.py index 9d3bf30..853cb7e 100644 --- a/__init__.py +++ b/__init__.py @@ -412,6 +412,10 @@ try: NODE_CLASS_MAPPINGS as HARDCORE_POSITION_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as HARDCORE_POSITION_NODE_DISPLAY_NAME_MAPPINGS, ) + from .node_formatter import ( + NODE_CLASS_MAPPINGS as FORMATTER_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as FORMATTER_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, @@ -437,9 +441,6 @@ try: save_character_profile_payload, subcategory_choices, ) - from .caption_naturalizer import naturalize_caption - from .krea_formatter import format_krea2_prompt - from .sdxl_formatter import format_sdxl_prompt, sdxl_quality_preset_choices, sdxl_style_preset_choices except ImportError: from loop_nodes import ( LOOP_NODE_CLASS_MAPPINGS, @@ -461,6 +462,10 @@ except ImportError: NODE_CLASS_MAPPINGS as HARDCORE_POSITION_NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as HARDCORE_POSITION_NODE_DISPLAY_NAME_MAPPINGS, ) + from node_formatter import ( + NODE_CLASS_MAPPINGS as FORMATTER_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as FORMATTER_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, @@ -486,9 +491,6 @@ except ImportError: save_character_profile_payload, subcategory_choices, ) - from caption_naturalizer import naturalize_caption - from krea_formatter import format_krea2_prompt - from sdxl_formatter import format_sdxl_prompt, sdxl_quality_preset_choices, sdxl_style_preset_choices if PromptServer is not None and web is not None: @@ -762,208 +764,6 @@ class SxCPPromptBuilderFromConfigs: ) -class SxCPCaptionNaturalizer: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "source_text": ("STRING", {"default": "", "multiline": True}), - "input_hint": (["auto", "metadata_json", "caption_or_prompt"], {"default": "auto"}), - "detail_level": (["balanced", "concise", "dense"], {"default": "balanced"}), - "style_policy": (["drop_style_tail", "keep_style_terms"], {"default": "drop_style_tail"}), - "trigger": ("STRING", {"default": "sxcppnl7"}), - "include_trigger": ("BOOLEAN", {"default": True}), - }, - "optional": { - "source_text_input": ("STRING", {"forceInput": True}), - "metadata_json": ("STRING", {"forceInput": True}), - }, - } - - RETURN_TYPES = ("STRING", "STRING") - RETURN_NAMES = ("natural_caption", "method") - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - source_text, - input_hint, - detail_level, - style_policy, - trigger, - include_trigger, - source_text_input="", - metadata_json="", - ): - active_source_text = source_text_input or source_text or "" - return naturalize_caption( - source_text=active_source_text, - metadata_json=metadata_json or "", - input_hint=input_hint, - trigger=trigger, - include_trigger=include_trigger, - detail_level=detail_level, - style_policy=style_policy, - ) - - -class SxCPKrea2Formatter: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "source_text": ("STRING", {"default": "", "multiline": True}), - "input_hint": (["auto", "metadata_json", "prompt"], {"default": "auto"}), - "target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}), - "detail_level": (["balanced", "concise", "dense"], {"default": "balanced"}), - "style_mode": (["preserve", "photographic", "minimal"], {"default": "preserve"}), - "preserve_trigger": ("BOOLEAN", {"default": False}), - }, - "optional": { - "metadata_json": ("STRING", {"default": "", "multiline": True}), - "negative_prompt": ("STRING", {"default": "", "multiline": True}), - "extra_positive": ("STRING", {"default": "", "multiline": True}), - "extra_negative": ("STRING", {"default": "", "multiline": True}), - }, - } - - RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") - RETURN_NAMES = ( - "krea_prompt", - "negative_prompt", - "krea_softcore_prompt", - "krea_hardcore_prompt", - "softcore_negative_prompt", - "hardcore_negative_prompt", - "method", - ) - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - source_text, - input_hint, - target, - detail_level, - style_mode, - preserve_trigger, - metadata_json="", - negative_prompt="", - extra_positive="", - extra_negative="", - ): - row = format_krea2_prompt( - source_text=source_text or "", - metadata_json=metadata_json or "", - negative_prompt=negative_prompt or "", - input_hint=input_hint, - target=target, - detail_level=detail_level, - style_mode=style_mode, - preserve_trigger=preserve_trigger, - extra_positive=extra_positive or "", - extra_negative=extra_negative or "", - ) - return ( - row["krea_prompt"], - row["negative_prompt"], - row["krea_softcore_prompt"], - row["krea_hardcore_prompt"], - row["softcore_negative_prompt"], - row["hardcore_negative_prompt"], - row["method"], - ) - - -class SxCPSDXLFormatter: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "source_text": ("STRING", {"default": "", "multiline": True}), - "input_hint": (["auto", "metadata_json", "prompt"], {"default": "auto"}), - "target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}), - "style_preset": (sdxl_style_preset_choices(), {"default": "flat_vector_pony"}), - "quality_preset": (sdxl_quality_preset_choices(), {"default": "pony_high"}), - "trigger": ("STRING", {"default": "mythp0rt", "multiline": False}), - "prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}), - "preserve_trigger": ("BOOLEAN", {"default": False}), - "nude_weight": ("FLOAT", {"default": 1.29, "min": 0.1, "max": 3.0, "step": 0.01}), - }, - "optional": { - "source_text_input": ("STRING", {"forceInput": True}), - "metadata_json": ("STRING", {"forceInput": True}), - "negative_prompt": ("STRING", {"forceInput": True}), - "custom_style": ("STRING", {"default": "", "multiline": True}), - "custom_quality": ("STRING", {"default": "", "multiline": True}), - "extra_positive": ("STRING", {"default": "", "multiline": True}), - "extra_negative": ("STRING", {"default": "", "multiline": True}), - }, - } - - RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") - RETURN_NAMES = ( - "sdxl_prompt", - "negative_prompt", - "sdxl_softcore_prompt", - "sdxl_hardcore_prompt", - "softcore_negative_prompt", - "hardcore_negative_prompt", - "method", - ) - FUNCTION = "build" - CATEGORY = "prompt_builder" - - def build( - self, - source_text, - input_hint, - target, - style_preset, - quality_preset, - trigger, - prepend_trigger_to_prompt, - preserve_trigger, - nude_weight, - source_text_input="", - metadata_json="", - negative_prompt="", - custom_style="", - custom_quality="", - extra_positive="", - extra_negative="", - ): - active_source_text = source_text_input or source_text or "" - row = format_sdxl_prompt( - source_text=active_source_text, - metadata_json=metadata_json or "", - negative_prompt=negative_prompt or "", - input_hint=input_hint, - target=target, - style_preset=style_preset, - quality_preset=quality_preset, - trigger=trigger, - prepend_trigger=prepend_trigger_to_prompt, - preserve_trigger=preserve_trigger, - nude_weight=nude_weight, - custom_style=custom_style or "", - custom_quality=custom_quality or "", - extra_positive=extra_positive or "", - extra_negative=extra_negative or "", - ) - return ( - row["sdxl_prompt"], - row["negative_prompt"], - row["sdxl_softcore_prompt"], - row["sdxl_hardcore_prompt"], - row["softcore_negative_prompt"], - row["hardcore_negative_prompt"], - row["method"], - ) - - class SxCPInstaOFOptions: @classmethod def INPUT_TYPES(cls): @@ -1151,13 +951,11 @@ NODE_CLASS_MAPPINGS.update(SEED_RESOLUTION_NODE_CLASS_MAPPINGS) 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(ROUTE_CONFIG_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(PROFILE_FILTER_NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update({ "SxCPPromptBuilderFromConfigs": SxCPPromptBuilderFromConfigs, - "SxCPCaptionNaturalizer": SxCPCaptionNaturalizer, - "SxCPKrea2Formatter": SxCPKrea2Formatter, - "SxCPSDXLFormatter": SxCPSDXLFormatter, "SxCPInstaOFOptions": SxCPInstaOFOptions, "SxCPInstaOFPromptPair": SxCPInstaOFPromptPair, }) @@ -1171,13 +969,11 @@ 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(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(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", - "SxCPCaptionNaturalizer": "SxCP Caption Naturalizer", - "SxCPKrea2Formatter": "SxCP Krea2 Formatter", - "SxCPSDXLFormatter": "SxCP SDXL Formatter", "SxCPInstaOFOptions": "SxCP Insta/OF Options", "SxCPInstaOFPromptPair": "SxCP Insta/OF Prompt Pair", }) diff --git a/docs/prompt-architecture-improvement-plan.md b/docs/prompt-architecture-improvement-plan.md index aea286a..87f3b6d 100644 --- a/docs/prompt-architecture-improvement-plan.md +++ b/docs/prompt-architecture-improvement-plan.md @@ -273,8 +273,8 @@ Improve later: ### Node / UI Path Owner: `__init__.py`, `node_seed_resolution.py`, `node_camera.py`, -`node_character.py`, `node_hardcore_position.py`, `node_route_config.py`, -`node_profile_filter.py`, `loop_nodes.py`, `web/*.js`. +`node_character.py`, `node_hardcore_position.py`, `node_formatter.py`, +`node_route_config.py`, `node_profile_filter.py`, `loop_nodes.py`, `web/*.js`. Keep here: @@ -287,6 +287,7 @@ Keep here: - character pool, slot, and profile node declarations in `node_character.py`. - hardcore position pool/filter node declarations in `node_hardcore_position.py`. +- caption/Krea2/SDXL formatter node declarations in `node_formatter.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`. @@ -303,6 +304,8 @@ Already isolated: - hardcore position pool and action filter nodes live in `node_hardcore_position.py`, with registration maps imported by `__init__.py`. +- caption naturalizer, Krea2 formatter, and SDXL formatter nodes live in + `node_formatter.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 7d3eb95..0bc5192 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -26,7 +26,7 @@ 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_route_config.py`, or + `node_hardcore_position.py`, `node_formatter.py`, `node_route_config.py`, or `node_profile_filter.py`, `loop_nodes.py`, or `web/*.js`. ## High-Level Routes @@ -697,6 +697,7 @@ These do not own prompt pool wording, but they affect execution and review: | Camera utility nodes | `node_camera.py`, imported by `__init__.py` | Direct camera config, orbit-to-camera config, and Qwen MultiAngle camera translation. | | 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. | | 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_formatter.py b/node_formatter.py new file mode 100644 index 0000000..202c857 --- /dev/null +++ b/node_formatter.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +try: + from .caption_naturalizer import naturalize_caption + from .krea_formatter import format_krea2_prompt + from .sdxl_formatter import format_sdxl_prompt, sdxl_quality_preset_choices, sdxl_style_preset_choices +except ImportError: # Allows local smoke tests from the repository root. + from caption_naturalizer import naturalize_caption + from krea_formatter import format_krea2_prompt + from sdxl_formatter import format_sdxl_prompt, sdxl_quality_preset_choices, sdxl_style_preset_choices + + +class SxCPCaptionNaturalizer: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "source_text": ("STRING", {"default": "", "multiline": True}), + "input_hint": (["auto", "metadata_json", "caption_or_prompt"], {"default": "auto"}), + "detail_level": (["balanced", "concise", "dense"], {"default": "balanced"}), + "style_policy": (["drop_style_tail", "keep_style_terms"], {"default": "drop_style_tail"}), + "trigger": ("STRING", {"default": "sxcppnl7"}), + "include_trigger": ("BOOLEAN", {"default": True}), + }, + "optional": { + "source_text_input": ("STRING", {"forceInput": True}), + "metadata_json": ("STRING", {"forceInput": True}), + }, + } + + RETURN_TYPES = ("STRING", "STRING") + RETURN_NAMES = ("natural_caption", "method") + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + source_text, + input_hint, + detail_level, + style_policy, + trigger, + include_trigger, + source_text_input="", + metadata_json="", + ): + active_source_text = source_text_input or source_text or "" + return naturalize_caption( + source_text=active_source_text, + metadata_json=metadata_json or "", + input_hint=input_hint, + trigger=trigger, + include_trigger=include_trigger, + detail_level=detail_level, + style_policy=style_policy, + ) + + +class SxCPKrea2Formatter: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "source_text": ("STRING", {"default": "", "multiline": True}), + "input_hint": (["auto", "metadata_json", "prompt"], {"default": "auto"}), + "target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}), + "detail_level": (["balanced", "concise", "dense"], {"default": "balanced"}), + "style_mode": (["preserve", "photographic", "minimal"], {"default": "preserve"}), + "preserve_trigger": ("BOOLEAN", {"default": False}), + }, + "optional": { + "metadata_json": ("STRING", {"default": "", "multiline": True}), + "negative_prompt": ("STRING", {"default": "", "multiline": True}), + "extra_positive": ("STRING", {"default": "", "multiline": True}), + "extra_negative": ("STRING", {"default": "", "multiline": True}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ( + "krea_prompt", + "negative_prompt", + "krea_softcore_prompt", + "krea_hardcore_prompt", + "softcore_negative_prompt", + "hardcore_negative_prompt", + "method", + ) + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + source_text, + input_hint, + target, + detail_level, + style_mode, + preserve_trigger, + metadata_json="", + negative_prompt="", + extra_positive="", + extra_negative="", + ): + row = format_krea2_prompt( + source_text=source_text or "", + metadata_json=metadata_json or "", + negative_prompt=negative_prompt or "", + input_hint=input_hint, + target=target, + detail_level=detail_level, + style_mode=style_mode, + preserve_trigger=preserve_trigger, + extra_positive=extra_positive or "", + extra_negative=extra_negative or "", + ) + return ( + row["krea_prompt"], + row["negative_prompt"], + row["krea_softcore_prompt"], + row["krea_hardcore_prompt"], + row["softcore_negative_prompt"], + row["hardcore_negative_prompt"], + row["method"], + ) + + +class SxCPSDXLFormatter: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "source_text": ("STRING", {"default": "", "multiline": True}), + "input_hint": (["auto", "metadata_json", "prompt"], {"default": "auto"}), + "target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}), + "style_preset": (sdxl_style_preset_choices(), {"default": "flat_vector_pony"}), + "quality_preset": (sdxl_quality_preset_choices(), {"default": "pony_high"}), + "trigger": ("STRING", {"default": "mythp0rt", "multiline": False}), + "prepend_trigger_to_prompt": ("BOOLEAN", {"default": True}), + "preserve_trigger": ("BOOLEAN", {"default": False}), + "nude_weight": ("FLOAT", {"default": 1.29, "min": 0.1, "max": 3.0, "step": 0.01}), + }, + "optional": { + "source_text_input": ("STRING", {"forceInput": True}), + "metadata_json": ("STRING", {"forceInput": True}), + "negative_prompt": ("STRING", {"forceInput": True}), + "custom_style": ("STRING", {"default": "", "multiline": True}), + "custom_quality": ("STRING", {"default": "", "multiline": True}), + "extra_positive": ("STRING", {"default": "", "multiline": True}), + "extra_negative": ("STRING", {"default": "", "multiline": True}), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") + RETURN_NAMES = ( + "sdxl_prompt", + "negative_prompt", + "sdxl_softcore_prompt", + "sdxl_hardcore_prompt", + "softcore_negative_prompt", + "hardcore_negative_prompt", + "method", + ) + FUNCTION = "build" + CATEGORY = "prompt_builder" + + def build( + self, + source_text, + input_hint, + target, + style_preset, + quality_preset, + trigger, + prepend_trigger_to_prompt, + preserve_trigger, + nude_weight, + source_text_input="", + metadata_json="", + negative_prompt="", + custom_style="", + custom_quality="", + extra_positive="", + extra_negative="", + ): + active_source_text = source_text_input or source_text or "" + row = format_sdxl_prompt( + source_text=active_source_text, + metadata_json=metadata_json or "", + negative_prompt=negative_prompt or "", + input_hint=input_hint, + target=target, + style_preset=style_preset, + quality_preset=quality_preset, + trigger=trigger, + prepend_trigger=prepend_trigger_to_prompt, + preserve_trigger=preserve_trigger, + nude_weight=nude_weight, + custom_style=custom_style or "", + custom_quality=custom_quality or "", + extra_positive=extra_positive or "", + extra_negative=extra_negative or "", + ) + return ( + row["sdxl_prompt"], + row["negative_prompt"], + row["sdxl_softcore_prompt"], + row["sdxl_hardcore_prompt"], + row["softcore_negative_prompt"], + row["hardcore_negative_prompt"], + row["method"], + ) + + +NODE_CLASS_MAPPINGS = { + "SxCPCaptionNaturalizer": SxCPCaptionNaturalizer, + "SxCPKrea2Formatter": SxCPKrea2Formatter, + "SxCPSDXLFormatter": SxCPSDXLFormatter, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "SxCPCaptionNaturalizer": "SxCP Caption Naturalizer", + "SxCPKrea2Formatter": "SxCP Krea2 Formatter", + "SxCPSDXLFormatter": "SxCP SDXL Formatter", +} diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 68668b9..d5f6876 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -2059,6 +2059,62 @@ def smoke_node_hardcore_position_registration() -> None: _expect("blocked=" in filter_summary, "Hardcore Action Filter summary lost blocked-gate details") +def smoke_node_formatter_registration() -> None: + required_nodes = [ + "SxCPCaptionNaturalizer", + "SxCPKrea2Formatter", + "SxCPSDXLFormatter", + ] + 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") + + krea_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2Formatter"] + krea_inputs = krea_node.INPUT_TYPES().get("required") or {} + _expect("source_text" in krea_inputs, "Krea2 Formatter lost source_text input") + _expect("tooltip" in krea_inputs["source_text"][1], "Krea2 Formatter tooltip injection missing") + + caption, caption_method = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"]().build( + "A woman standing by a window, best quality", + "caption_or_prompt", + "concise", + "drop_style_tail", + "sxcppnl7", + True, + ) + _expect_text("node_formatter.caption", caption, 20) + _expect(caption.startswith("sxcppnl7"), "Caption Naturalizer did not prepend trigger") + _expect("text(" in caption_method, "Caption Naturalizer method changed unexpectedly") + + krea_output = krea_node().build( + "sxcppnl7 A woman standing by a window", + "prompt", + "single", + "concise", + "preserve", + True, + ) + _expect_text("node_formatter.krea_prompt", krea_output[0], 20) + _expect("sxcppnl7" in krea_output[0], "Krea2 Formatter did not preserve trigger when enabled") + _expect(krea_output[6].startswith("text("), "Krea2 Formatter method changed unexpectedly") + + sdxl_output = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"]().build( + "A woman standing by a window", + "prompt", + "single", + "flat_vector_pony", + "pony_high", + "mythp0rt", + True, + False, + 1.29, + ) + _expect_text("node_formatter.sdxl_prompt", sdxl_output[0], 40) + _expect_trigger_once("node_formatter.sdxl_prompt", sdxl_output[0], "mythp0rt") + _expect_text("node_formatter.sdxl_negative", sdxl_output[1], 20) + _expect(sdxl_output[6].startswith("text("), "SDXL Formatter method changed unexpectedly") + + def smoke_node_profile_filter_registration() -> None: required_nodes = [ "SxCPGenerationProfile", @@ -2168,6 +2224,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [ ("node_route_config_registration", smoke_node_route_config_registration), ("node_character_registration", smoke_node_character_registration), ("node_hardcore_position_registration", smoke_node_hardcore_position_registration), + ("node_formatter_registration", smoke_node_formatter_registration), ("node_profile_filter_registration", smoke_node_profile_filter_registration), ]