diff --git a/caption_format_route.py b/caption_format_route.py index c98a4b5..a27d9e9 100644 --- a/caption_format_route.py +++ b/caption_format_route.py @@ -5,9 +5,11 @@ from typing import Any, Callable try: from . import formatter_input as input_policy + from . import formatter_route_trace as trace_policy from . import formatter_target as target_policy except ImportError: # pragma: no cover - plain-script smoke tests import formatter_input as input_policy + import formatter_route_trace as trace_policy import formatter_target as target_policy @@ -35,10 +37,14 @@ class CaptionFormatRoute: style_policy: str include_trigger: bool keep_style: bool + route_trace_json: str = "" def as_tuple(self) -> tuple[str, str]: return self.caption, self.method + def as_trace_tuple(self) -> tuple[str, str, str]: + return self.caption, self.method, self.route_trace_json + @dataclass(frozen=True) class CaptionFormatDependencies: @@ -72,6 +78,17 @@ def naturalize_caption_result( triggers=(request.trigger,), ) full_method = f"{row_method}:{method}" + route_trace = trace_policy.route_trace_json( + formatter="caption", + branch="metadata", + method=full_method, + input_hint=input_hint, + target=target, + detail_level=detail_level, + style_policy=style_policy, + include_trigger=include_trigger, + keep_style=keep_style, + ) return CaptionFormatRoute( caption=caption, method=full_method, @@ -82,6 +99,7 @@ def naturalize_caption_result( style_policy=style_policy, include_trigger=include_trigger, keep_style=keep_style, + route_trace_json=route_trace, ) prose, method = deps.text_to_prose(request.source_text, detail_level, keep_style) @@ -89,6 +107,17 @@ def naturalize_caption_result( deps.with_trigger(prose, request.trigger, include_trigger), triggers=(request.trigger,), ) + route_trace = trace_policy.route_trace_json( + formatter="caption", + branch="text", + method=method, + input_hint=input_hint, + target=target, + detail_level=detail_level, + style_policy=style_policy, + include_trigger=include_trigger, + keep_style=keep_style, + ) return CaptionFormatRoute( caption=caption, method=method, @@ -99,6 +128,7 @@ def naturalize_caption_result( style_policy=style_policy, include_trigger=include_trigger, keep_style=keep_style, + route_trace_json=route_trace, ) diff --git a/caption_naturalizer.py b/caption_naturalizer.py index 6bc8e6e..af0876b 100644 --- a/caption_naturalizer.py +++ b/caption_naturalizer.py @@ -395,3 +395,32 @@ def naturalize_caption( ), _caption_format_dependencies(), ) + + +def naturalize_caption_with_trace( + source_text: str, + metadata_json: str = "", + input_hint: str = "auto", + target: str = "auto", + trigger: str = DEFAULT_TRIGGER, + include_trigger: bool = True, + detail_level: str = "balanced", + style_policy: str = "drop_style_tail", + caption_profile: str = caption_policy.CAPTION_PROFILE_DEFAULT, +) -> tuple[str, str, str]: + """Rewrite text like naturalize_caption and include formatter route trace JSON.""" + result = caption_format_route.naturalize_caption_result( + caption_format_route.CaptionFormatRequest( + source_text=source_text, + metadata_json=metadata_json, + input_hint=input_hint, + target=target, + trigger=trigger, + include_trigger=include_trigger, + detail_level=detail_level, + style_policy=style_policy, + caption_profile=caption_profile, + ), + _caption_format_dependencies(), + ) + return result.as_trace_tuple() diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md index 82e7ecd..9190978 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -150,9 +150,9 @@ recoverable. | `SxCP Prompt Builder` | category, subcategory, seed, optional config nodes | `prompt`, `negative_prompt`, `caption`, `metadata_json`, `category`, `subcategory` | | `SxCP Prompt Builder From Configs` | category/cast/profile/filter/config node outputs | Same as `SxCP Prompt Builder` | | `SxCP Insta/OF Prompt Pair` | options, seed_config, character_cast, location/composition/camera, hardcore_position_config | `softcore_prompt`, `hardcore_prompt`, both negatives, both captions, `shared_descriptor`, `metadata_json` | -| `SxCP Krea2 Formatter` | `source_text`, connectable `metadata_json`, target | `krea_prompt`, both pair prompts if pair metadata exists, negative outputs, method | -| `SxCP SDXL Formatter` | `source_text`, connectable `metadata_json`, target, style/quality preset | `sdxl_prompt`, both pair prompts if pair metadata exists, negative outputs, method | -| `SxCP Caption Naturalizer` | `source_text`, connectable `metadata_json`, target | `natural_caption`, method | +| `SxCP Krea2 Formatter` | `source_text`, connectable `metadata_json`, target | `krea_prompt`, both pair prompts if pair metadata exists, negative outputs, method, `route_trace_json` | +| `SxCP SDXL Formatter` | `source_text`, connectable `metadata_json`, target, style/quality preset | `sdxl_prompt`, both pair prompts if pair metadata exists, negative outputs, method, `route_trace_json` | +| `SxCP Caption Naturalizer` | `source_text`, connectable `metadata_json`, target | `natural_caption`, method, `route_trace_json` | ## Practical Recipes @@ -703,7 +703,9 @@ structural: metadata with both a softcore side and a hardcore side (`softcore_row`/`hardcore_row` or root soft/hard prompt/caption fields) is treated as pair metadata even if the UI `mode` label is absent. Krea2, SDXL, and caption routes share this detection to avoid hidden drift between formatter -paths. +paths. Formatter nodes expose `route_trace_json` with the formatter name, +branch, method, normalized target/input hint, selected pair side when relevant, +and route-specific controls. ### Krea2 diff --git a/formatter_route_trace.py b/formatter_route_trace.py new file mode 100644 index 0000000..fdc3411 --- /dev/null +++ b/formatter_route_trace.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +import json +from typing import Any + + +def route_trace_json(**values: Any) -> str: + trace: dict[str, Any] = {} + for key, value in values.items(): + if value is None: + continue + if isinstance(value, str): + value = value.strip() + if not value: + continue + trace[key] = value + return json.dumps(trace, ensure_ascii=True, sort_keys=True) diff --git a/krea_format_route.py b/krea_format_route.py index fbc591b..968d6fa 100644 --- a/krea_format_route.py +++ b/krea_format_route.py @@ -6,10 +6,12 @@ from typing import Any, Callable try: from . import formatter_detail as detail_policy from . import formatter_input as input_policy + from . import formatter_route_trace as trace_policy from . import formatter_target as target_policy except ImportError: # pragma: no cover - plain-script smoke tests import formatter_detail as detail_policy import formatter_input as input_policy + import formatter_route_trace as trace_policy import formatter_target as target_policy @@ -68,6 +70,7 @@ def format_krea2_prompt_result(request: KreaFormatRequest, deps: KreaFormatDepen detail_level = detail_policy.normalize_detail_level(request.detail_level) style_mode = normalize_style_mode(request.style_mode) target = target_policy.normalize_target(request.target) + input_hint = input_policy.normalize_input_hint(request.input_hint, text_hint=input_policy.INPUT_HINT_PROMPT) row, method = deps.row_from_inputs(request.source_text, request.metadata_json, request.input_hint) if row and input_policy.is_pair_metadata(row): @@ -104,6 +107,16 @@ def format_krea2_prompt_result(request: KreaFormatRequest, deps: KreaFormatDepen ), "method": f"{method}:krea2(insta_of_pair)", } + output["route_trace_json"] = trace_policy.route_trace_json( + formatter="krea2", + branch="insta_of_pair", + method=output["method"], + input_hint=input_hint, + target=target, + selected_side=pair_target.selected_side, + detail_level=detail_level, + style_mode=style_mode, + ) return KreaFormatRoute( output=output, branch="insta_of_pair", @@ -143,6 +156,15 @@ def format_krea2_prompt_result(request: KreaFormatRequest, deps: KreaFormatDepen "hardcore_negative_prompt": "", "method": method, } + output["route_trace_json"] = trace_policy.route_trace_json( + formatter="krea2", + branch=branch, + method=method, + input_hint=input_hint, + target=target, + detail_level=detail_level, + style_mode=style_mode, + ) return KreaFormatRoute( output=output, branch=branch, diff --git a/node_formatter.py b/node_formatter.py index de955c9..5c7667f 100644 --- a/node_formatter.py +++ b/node_formatter.py @@ -1,7 +1,7 @@ from __future__ import annotations try: - from .caption_naturalizer import naturalize_caption + from .caption_naturalizer import naturalize_caption_with_trace from .caption_policy import caption_profile_choices, style_policy_choices from .formatter_detail import detail_level_choices from .formatter_input import INPUT_HINT_CAPTION_OR_PROMPT, INPUT_HINT_PROMPT, input_hint_choices @@ -15,7 +15,7 @@ try: sdxl_style_preset_choices, ) except ImportError: # Allows local smoke tests from the repository root. - from caption_naturalizer import naturalize_caption + from caption_naturalizer import naturalize_caption_with_trace from caption_policy import caption_profile_choices, style_policy_choices from formatter_detail import detail_level_choices from formatter_input import INPUT_HINT_CAPTION_OR_PROMPT, INPUT_HINT_PROMPT, input_hint_choices @@ -50,8 +50,8 @@ class SxCPCaptionNaturalizer: }, } - RETURN_TYPES = ("STRING", "STRING") - RETURN_NAMES = ("natural_caption", "method") + RETURN_TYPES = ("STRING", "STRING", "STRING") + RETURN_NAMES = ("natural_caption", "method", "route_trace_json") FUNCTION = "build" CATEGORY = "prompt_builder" @@ -69,7 +69,7 @@ class SxCPCaptionNaturalizer: metadata_json="", ): active_source_text = source_text_input or source_text or "" - return naturalize_caption( + return naturalize_caption_with_trace( source_text=active_source_text, metadata_json=metadata_json or "", input_hint=input_hint, @@ -103,7 +103,7 @@ class SxCPKrea2Formatter: }, } - RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") RETURN_NAMES = ( "krea_prompt", "negative_prompt", @@ -112,6 +112,7 @@ class SxCPKrea2Formatter: "softcore_negative_prompt", "hardcore_negative_prompt", "method", + "route_trace_json", ) FUNCTION = "build" CATEGORY = "prompt_builder" @@ -151,6 +152,7 @@ class SxCPKrea2Formatter: row["softcore_negative_prompt"], row["hardcore_negative_prompt"], row["method"], + row["route_trace_json"], ) @@ -181,7 +183,7 @@ class SxCPSDXLFormatter: }, } - RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") + RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING") RETURN_NAMES = ( "sdxl_prompt", "negative_prompt", @@ -190,6 +192,7 @@ class SxCPSDXLFormatter: "softcore_negative_prompt", "hardcore_negative_prompt", "method", + "route_trace_json", ) FUNCTION = "build" CATEGORY = "prompt_builder" @@ -241,6 +244,7 @@ class SxCPSDXLFormatter: row["softcore_negative_prompt"], row["hardcore_negative_prompt"], row["method"], + row["route_trace_json"], ) diff --git a/sdxl_format_route.py b/sdxl_format_route.py index a281ac6..721a3e1 100644 --- a/sdxl_format_route.py +++ b/sdxl_format_route.py @@ -5,9 +5,11 @@ from typing import Any, Callable try: from . import formatter_input as input_policy + from . import formatter_route_trace as trace_policy from . import formatter_target as target_policy except ImportError: # pragma: no cover - plain-script smoke tests import formatter_input as input_policy + import formatter_route_trace as trace_policy import formatter_target as target_policy @@ -64,6 +66,7 @@ def format_sdxl_prompt_result(request: SDXLFormatRequest, deps: SDXLFormatDepend request.quality_preset, ) target = target_policy.normalize_target(request.target) + input_hint = input_policy.normalize_input_hint(request.input_hint, text_hint=input_policy.INPUT_HINT_PROMPT) nude_weight = max(0.1, min(3.0, float(request.nude_weight))) row, method = deps.row_from_inputs(request.source_text, request.metadata_json, request.input_hint) @@ -119,6 +122,17 @@ def format_sdxl_prompt_result(request: SDXLFormatRequest, deps: SDXLFormatDepend ), "method": f"{method}:sdxl(insta_of_pair)", } + output["route_trace_json"] = trace_policy.route_trace_json( + formatter="sdxl", + branch="insta_of_pair", + method=output["method"], + input_hint=input_hint, + target=target, + selected_side=pair_target.selected_side, + style_preset=style_preset, + quality_preset=quality_preset, + nude_weight=nude_weight, + ) return SDXLFormatRoute( output=output, branch="insta_of_pair", @@ -163,6 +177,16 @@ def format_sdxl_prompt_result(request: SDXLFormatRequest, deps: SDXLFormatDepend "hardcore_negative_prompt": "", "method": method, } + output["route_trace_json"] = trace_policy.route_trace_json( + formatter="sdxl", + branch=branch, + method=method, + input_hint=input_hint, + target=target, + style_preset=style_preset, + quality_preset=quality_preset, + nude_weight=nude_weight, + ) return SDXLFormatRoute( output=output, branch=branch, diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index b78e7b2..2cba16d 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -3150,6 +3150,10 @@ def smoke_krea_format_route_policy() -> None: _expect(typed_single.output == public_single, "Typed Krea format route should match public single formatter output") _expect(typed_single.branch == "metadata(single)", "Typed Krea format route changed single branch") _expect(typed_single.target == "single", "Typed Krea format route lost target normalization") + single_trace = json.loads(_expect_text("krea_format_route_policy.single_trace", typed_single.output.get("route_trace_json"), 20)) + _expect(single_trace.get("formatter") == "krea2", "Typed Krea single trace lost formatter") + _expect(single_trace.get("branch") == "metadata(single)", "Typed Krea single trace lost branch") + _expect(single_trace.get("target") == "single", "Typed Krea single trace lost target") _expect("krea route marker" in typed_single.output.get("krea_prompt", ""), "Typed Krea route lost extra positive") _expect("krea route negative" in typed_single.output.get("negative_prompt", ""), "Typed Krea route lost extra negative") @@ -3191,6 +3195,9 @@ def smoke_krea_format_route_policy() -> None: ) _expect(typed_pair.output == public_pair, "Typed Krea format route should match public pair formatter output") _expect(typed_pair.branch == "insta_of_pair", "Typed Krea format route changed pair branch") + pair_trace = json.loads(_expect_text("krea_format_route_policy.pair_trace", typed_pair.output.get("route_trace_json"), 20)) + _expect(pair_trace.get("branch") == "insta_of_pair", "Typed Krea pair trace lost branch") + _expect(pair_trace.get("selected_side") == "hardcore", "Typed Krea pair trace lost selected side") _expect_text("krea_format_route_policy.hard_prompt", typed_pair.output.get("krea_hardcore_prompt"), 40) _expect("pair route marker" in typed_pair.output.get("krea_prompt", ""), "Typed Krea pair route lost extra positive") @@ -3217,6 +3224,9 @@ def smoke_krea_format_route_policy() -> None: _expect(typed_fallback.output == public_fallback, "Typed Krea format route should match public fallback output") _expect(typed_fallback.branch == "fallback", "Typed Krea format route changed fallback branch") _expect(typed_fallback.target == "auto", "Typed Krea format route should normalize invalid target") + fallback_trace = json.loads(_expect_text("krea_format_route_policy.fallback_trace", typed_fallback.output.get("route_trace_json"), 20)) + _expect(fallback_trace.get("branch") == "fallback", "Typed Krea fallback trace lost branch") + _expect(fallback_trace.get("input_hint") == "prompt", "Typed Krea fallback trace lost input hint") _expect(typed_fallback.detail_level == "balanced", "Typed Krea format route should normalize invalid detail level") _expect(typed_fallback.style_mode == "preserve", "Typed Krea format route should normalize invalid style mode") _expect(krea_format_route.style_mode_choices() == ["preserve", "photographic", "minimal"], "Krea style mode choices changed") @@ -3378,6 +3388,22 @@ def smoke_caption_format_route_policy() -> None: _expect(typed_metadata.detail_level == "dense", "Typed caption route lost training_dense detail override") _expect(typed_metadata.style_policy == "drop_style_tail", "Typed caption route lost training_dense style override") _expect(typed_metadata.include_trigger is True, "Typed caption route lost training_dense trigger override") + metadata_trace = json.loads(_expect_text("caption_format_route_policy.metadata_trace", typed_metadata.route_trace_json, 20)) + _expect(metadata_trace.get("formatter") == "caption", "Typed caption metadata trace lost formatter") + _expect(metadata_trace.get("branch") == "metadata", "Typed caption metadata trace lost branch") + _expect(metadata_trace.get("target") == "single", "Typed caption metadata trace lost target") + traced_public_metadata = caption_naturalizer.naturalize_caption_with_trace( + "", + metadata_json=metadata_request.metadata_json, + input_hint=metadata_request.input_hint, + target=metadata_request.target, + trigger=metadata_request.trigger, + include_trigger=metadata_request.include_trigger, + detail_level=metadata_request.detail_level, + style_policy=metadata_request.style_policy, + caption_profile=metadata_request.caption_profile, + ) + _expect(traced_public_metadata == typed_metadata.as_trace_tuple(), "Caption trace wrapper drifted from typed route") _expect(typed_metadata.caption.startswith(Trigger), "Typed caption metadata route should prepend training trigger") fallback_request = caption_format_route.CaptionFormatRequest( @@ -3409,6 +3435,9 @@ def smoke_caption_format_route_policy() -> None: _expect(typed_fallback.input_hint == "auto", "Typed caption route should normalize invalid input hint") _expect(typed_fallback.target == "auto", "Typed caption route should normalize invalid target") _expect(typed_fallback.include_trigger is False, "Typed caption browsing profile should disable trigger") + fallback_trace = json.loads(_expect_text("caption_format_route_policy.fallback_trace", typed_fallback.route_trace_json, 20)) + _expect(fallback_trace.get("branch") == "text", "Typed caption fallback trace lost branch") + _expect(fallback_trace.get("input_hint") == "auto", "Typed caption fallback trace lost input hint") _expect(typed_fallback.keep_style is True, "Typed caption browsing profile should keep style terms") _expect(not typed_fallback.caption.startswith(Trigger), "Typed caption fallback route should not prepend browsing trigger") _expect(typed_fallback.method == "text(fallback)", "Typed caption fallback method changed") @@ -3721,6 +3750,11 @@ def smoke_sdxl_format_route_policy() -> None: _expect(typed_single.target == "single", "Typed SDXL format route lost target normalization") _expect(typed_single.nude_weight == 3.0, "Typed SDXL format route should clamp high nude weight") _expect(typed_single.style_preset == "photographic", "Typed SDXL format route lost profile style override") + single_trace = json.loads(_expect_text("sdxl_format_route_policy.single_trace", typed_single.output.get("route_trace_json"), 20)) + _expect(single_trace.get("formatter") == "sdxl", "Typed SDXL single trace lost formatter") + _expect(single_trace.get("branch") == "metadata", "Typed SDXL single trace lost branch") + _expect(single_trace.get("target") == "single", "Typed SDXL single trace lost target") + _expect(single_trace.get("nude_weight") == 3.0, "Typed SDXL single trace lost clamped nude weight") _expect("sdxl route marker" in typed_single.output.get("sdxl_prompt", ""), "Typed SDXL route lost extra positive") _expect("sdxl route negative" in typed_single.output.get("negative_prompt", ""), "Typed SDXL route lost extra negative") @@ -3762,6 +3796,9 @@ def smoke_sdxl_format_route_policy() -> None: ) _expect(typed_pair.output == public_pair, "Typed SDXL format route should match public pair formatter output") _expect(typed_pair.branch == "insta_of_pair", "Typed SDXL format route changed pair branch") + pair_trace = json.loads(_expect_text("sdxl_format_route_policy.pair_trace", typed_pair.output.get("route_trace_json"), 20)) + _expect(pair_trace.get("branch") == "insta_of_pair", "Typed SDXL pair trace lost branch") + _expect(pair_trace.get("selected_side") == "hardcore", "Typed SDXL pair trace lost selected side") _expect_text("sdxl_format_route_policy.hard_prompt", typed_pair.output.get("sdxl_hardcore_prompt"), 40) _expect("pair sdxl route marker" in typed_pair.output.get("sdxl_prompt", ""), "Typed SDXL pair route lost extra positive") @@ -3793,6 +3830,9 @@ def smoke_sdxl_format_route_policy() -> None: _expect(typed_fallback.branch == "fallback", "Typed SDXL format route changed fallback branch") _expect(typed_fallback.target == "auto", "Typed SDXL format route should normalize invalid target") _expect(typed_fallback.nude_weight == 0.1, "Typed SDXL format route should clamp low nude weight") + fallback_trace = json.loads(_expect_text("sdxl_format_route_policy.fallback_trace", typed_fallback.output.get("route_trace_json"), 20)) + _expect(fallback_trace.get("branch") == "fallback", "Typed SDXL fallback trace lost branch") + _expect(fallback_trace.get("input_hint") == "prompt", "Typed SDXL fallback trace lost input hint") _expect("Characters:" not in typed_fallback.output.get("sdxl_prompt", ""), "Typed SDXL fallback leaked Characters label") _expect("blur" in typed_fallback.output.get("negative_prompt", ""), "Typed SDXL fallback route lost Avoid negative") @@ -7005,7 +7045,7 @@ def smoke_node_formatter_registration() -> None: _expect(krea_optional["metadata_json"][1].get("forceInput") is True, "Krea2 Formatter metadata_json should be connectable") _expect(krea_optional["negative_prompt"][1].get("forceInput") is True, "Krea2 Formatter negative_prompt should be connectable") - caption, caption_method = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"]().build( + caption, caption_method, caption_trace_json = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"]().build( "A woman standing by a window, best quality", "caption_or_prompt", "manual_controls", @@ -7017,6 +7057,9 @@ def smoke_node_formatter_registration() -> None: _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") + caption_trace = json.loads(_expect_text("node_formatter.caption_trace", caption_trace_json, 20)) + _expect(caption_trace.get("formatter") == "caption", "Caption Naturalizer trace lost formatter") + _expect(caption_trace.get("branch") == "text", "Caption Naturalizer trace lost text branch") caption_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"].INPUT_TYPES().get("required") or {} _expect("caption_profile" in caption_inputs, "Caption Naturalizer lost caption_profile input") _expect("target" in caption_inputs, "Caption Naturalizer lost target input") @@ -7039,6 +7082,9 @@ def smoke_node_formatter_registration() -> None: _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") + krea_trace = json.loads(_expect_text("node_formatter.krea_trace", krea_output[7], 20)) + _expect(krea_trace.get("formatter") == "krea2", "Krea2 Formatter trace lost formatter") + _expect(krea_trace.get("branch") == "fallback", "Krea2 Formatter trace lost fallback branch") sdxl_output = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"]().build( "A woman standing by a window", @@ -7056,6 +7102,9 @@ def smoke_node_formatter_registration() -> None: _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") + sdxl_trace = json.loads(_expect_text("node_formatter.sdxl_trace", sdxl_output[7], 20)) + _expect(sdxl_trace.get("formatter") == "sdxl", "SDXL Formatter trace lost formatter") + _expect(sdxl_trace.get("branch") == "fallback", "SDXL Formatter trace lost fallback branch") sdxl_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"].INPUT_TYPES().get("required") or {} _expect("formatter_profile" in sdxl_inputs, "SDXL Formatter lost formatter_profile input") _expect(sdxl_inputs["target"][0] == formatter_target.target_choices(), "SDXL Formatter target choices drifted") @@ -7108,6 +7157,7 @@ def smoke_node_formatter_registration() -> None: expected_krea_pair["softcore_negative_prompt"], expected_krea_pair["hardcore_negative_prompt"], expected_krea_pair["method"], + expected_krea_pair["route_trace_json"], ), "Krea2 Formatter node output drifted from public metadata formatter", ) @@ -7152,11 +7202,12 @@ def smoke_node_formatter_registration() -> None: expected_sdxl_pair["softcore_negative_prompt"], expected_sdxl_pair["hardcore_negative_prompt"], expected_sdxl_pair["method"], + expected_sdxl_pair["route_trace_json"], ), "SDXL Formatter node output drifted from public metadata formatter", ) - expected_caption_pair = caption_naturalizer.naturalize_caption( + expected_caption_pair = caption_naturalizer.naturalize_caption_with_trace( "", metadata_json=pair_metadata, input_hint="metadata_json",