Extract SDXL format dispatch route

This commit is contained in:
2026-06-27 12:26:00 +02:00
parent 837299be6c
commit 1ee0b6e91a
5 changed files with 338 additions and 82 deletions
@@ -415,6 +415,10 @@ Keep here:
Already isolated:
- `sdxl_format_route.py` owns top-level SDXL dispatch, including formatter
profile application, target and nude-weight normalization, metadata-vs-text
input selection, single-vs-pair branching, final prompt/negative output
shape, and fallback routing; `sdxl_formatter.py` keeps the public wrapper.
- `sdxl_tag_routes.py` owns normal metadata row tags and Insta/OF pair soft/hard
tag extraction behind `SDXLRowTagRequest`, `SDXLPairTagRequest`,
`SDXLTagRouteDependencies`, and `SDXLTagRoute`; `sdxl_formatter.py` keeps
+2 -1
View File
@@ -64,7 +64,7 @@ call the same core generation functions.
| `SxCP Prompt Builder From Configs` | `build_prompt_from_configs` -> `builder_config_route.py` -> `build_prompt` -> `builder_prompt_route.py` | Same generator, but inputs come from category/cast/profile/filter helper nodes. |
| `SxCP Insta/OF Prompt Pair` | `build_insta_of_pair` | Builds a softcore row and hardcore row with shared cast/continuity options. |
| `SxCP Krea2 Formatter` | `format_krea2_prompt` -> `krea_format_route.py` | Converts metadata rows or pair metadata into Krea2-friendly prose. |
| `SxCP SDXL Formatter` | `format_sdxl_prompt` | Converts metadata rows or pair metadata into SDXL/tag style prompts. |
| `SxCP SDXL Formatter` | `format_sdxl_prompt` -> `sdxl_format_route.py` | Converts metadata rows or pair metadata into SDXL/tag style prompts. |
| `SxCP Caption Naturalizer` | `naturalize_caption` | Converts rows into more natural sentence captions. |
Core helper ownership:
@@ -130,6 +130,7 @@ Core helper ownership:
| `node_tooltips.py` | Node input tooltip inventory, node-specific overrides, dynamic-input fallback rules, and tooltip injection installer used by `__init__.py`. |
| `server_routes.py` | Pure payload handlers for profile-save and accumulator server endpoints, used by ComfyUI routes and smoke tests without importing ComfyUI. |
| `sdxl_presets.py` | SDXL formatter profiles, style presets, quality presets, default negative prompt, and metadata-family tag hints used by the SDXL formatter and node choice lists. |
| `sdxl_format_route.py` | Top-level SDXL dispatch, formatter profile application, target and nude-weight normalization, metadata-vs-text input selection, single-vs-pair branching, final prompt/negative output shape, and fallback routing. |
| `sdxl_tag_policy.py` | SDXL tag splitting, tag-key dedupe, count inference, character descriptor tags, metadata-family/camera/explicit helper tags, and route dependency assembly used by `sdxl_formatter.py` and `sdxl_tag_routes.py`. |
| `caption_policy.py` | Caption naturalizer policy data and helpers: caption profiles, style tails, item labels, metadata-family caption labels, detail/style-policy normalization, clothing cleanup, and composition cleanup. |
| `caption_text_policy.py` | Caption sentence helpers, trigger wrapping, formatter-hint append, row-value fallback wrappers, cast text wrappers, single-caption front parsing, and metadata-route dependency assembly used by `caption_naturalizer.py` and `caption_metadata_routes.py`. |
+168
View File
@@ -0,0 +1,168 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Callable
@dataclass(frozen=True)
class SDXLFormatRequest:
source_text: str
metadata_json: str = ""
negative_prompt: str = ""
input_hint: str = "auto"
target: str = "auto"
style_preset: str = "flat_vector_pony"
quality_preset: str = "pony_high"
trigger: str = "mythp0rt"
prepend_trigger: bool = True
preserve_trigger: bool = False
nude_weight: float = 1.29
custom_style: str = ""
custom_quality: str = ""
extra_positive: str = ""
extra_negative: str = ""
formatter_profile: str = "manual_controls"
@dataclass(frozen=True)
class SDXLFormatRoute:
output: dict[str, str]
branch: str
method: str
target: str
style_preset: str
quality_preset: str
nude_weight: float
@dataclass(frozen=True)
class SDXLFormatDependencies:
default_negative: str
apply_formatter_profile: Callable[[str, str, str], tuple[str, str]]
clean: Callable[[Any], str]
row_from_inputs: Callable[[str, str, str], tuple[dict[str, Any] | None, str]]
row_core_tags: Callable[[dict[str, Any], float], list[str]]
soft_tags: Callable[[dict[str, Any], dict[str, Any], float], str]
hard_tags: Callable[[dict[str, Any], dict[str, Any], float], str]
fallback_text_to_sdxl: Callable[[str, bool, float], tuple[str, str, str]]
assemble_prompt: Callable[[str, str, str, str, bool, str, str, str], str]
combine_negative: Callable[..., str]
sanitize_negative_text: Callable[[str], str]
def format_sdxl_prompt_result(request: SDXLFormatRequest, deps: SDXLFormatDependencies) -> SDXLFormatRoute:
style_preset, quality_preset = deps.apply_formatter_profile(
request.formatter_profile,
request.style_preset,
request.quality_preset,
)
target = request.target if request.target in ("auto", "single", "softcore", "hardcore") else "auto"
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)
if row and row.get("mode") == "Insta/OF":
soft_row = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {}
hard_row = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {}
soft_body = deps.soft_tags(soft_row, row, nude_weight)
hard_body = deps.hard_tags(hard_row, row, nude_weight)
soft_prompt = deps.assemble_prompt(
soft_body,
style_preset,
quality_preset,
request.trigger,
request.prepend_trigger,
request.custom_style,
request.custom_quality,
request.extra_positive,
)
hard_prompt = deps.assemble_prompt(
hard_body,
style_preset,
quality_preset,
request.trigger,
request.prepend_trigger,
request.custom_style,
request.custom_quality,
request.extra_positive,
)
selected = hard_prompt if target == "hardcore" else soft_prompt
selected_negative = (
row.get("hardcore_negative_prompt") if target == "hardcore" else row.get("softcore_negative_prompt")
)
output = {
"sdxl_prompt": selected,
"negative_prompt": deps.sanitize_negative_text(
deps.combine_negative(
deps.default_negative,
selected_negative,
request.negative_prompt,
request.extra_negative,
)
),
"sdxl_softcore_prompt": soft_prompt,
"sdxl_hardcore_prompt": hard_prompt,
"softcore_negative_prompt": deps.sanitize_negative_text(
deps.combine_negative(deps.default_negative, row.get("softcore_negative_prompt"), request.extra_negative)
),
"hardcore_negative_prompt": deps.sanitize_negative_text(
deps.combine_negative(deps.default_negative, row.get("hardcore_negative_prompt"), request.extra_negative)
),
"method": f"{method}:sdxl(insta_of_pair)",
}
return SDXLFormatRoute(
output=output,
branch="insta_of_pair",
method=output["method"],
target=target,
style_preset=style_preset,
quality_preset=quality_preset,
nude_weight=nude_weight,
)
if row:
body = ", ".join(deps.row_core_tags(row, nude_weight))
extracted_negative = deps.clean(row.get("negative_prompt"))
method = f"{method}:sdxl(metadata)"
branch = "metadata"
else:
body, extracted_negative, method = deps.fallback_text_to_sdxl(
request.source_text,
request.preserve_trigger,
nude_weight,
)
branch = "fallback"
prompt = deps.assemble_prompt(
body,
style_preset,
quality_preset,
request.trigger,
request.prepend_trigger,
request.custom_style,
request.custom_quality,
request.extra_positive,
)
output = {
"sdxl_prompt": prompt,
"negative_prompt": deps.sanitize_negative_text(
deps.combine_negative(deps.default_negative, extracted_negative, request.negative_prompt, request.extra_negative)
),
"sdxl_softcore_prompt": "",
"sdxl_hardcore_prompt": "",
"softcore_negative_prompt": "",
"hardcore_negative_prompt": "",
"method": method,
}
return SDXLFormatRoute(
output=output,
branch=branch,
method=method,
target=target,
style_preset=style_preset,
quality_preset=quality_preset,
nude_weight=nude_weight,
)
def format_sdxl_prompt(request: SDXLFormatRequest, deps: SDXLFormatDependencies) -> dict[str, str]:
return format_sdxl_prompt_result(request, deps).output
+39 -78
View File
@@ -4,12 +4,14 @@ from typing import Any
try:
from . import formatter_input as input_policy
from . import sdxl_format_route
from . import sdxl_tag_policy
from . import sdxl_tag_routes
from . import sdxl_presets as sdxl_policy
from .prompt_hygiene import sanitize_negative_text, sanitize_tag_prompt
except ImportError: # Allows local smoke tests with `python -c`.
import formatter_input as input_policy
import sdxl_format_route
import sdxl_tag_policy
import sdxl_tag_routes
import sdxl_presets as sdxl_policy
@@ -203,6 +205,26 @@ def _fallback_text_to_sdxl(
return tags, negative, "text(fallback)"
def _sdxl_format_dependencies() -> sdxl_format_route.SDXLFormatDependencies:
return sdxl_format_route.SDXLFormatDependencies(
default_negative=SDXL_DEFAULT_NEGATIVE,
apply_formatter_profile=lambda profile, style, quality: sdxl_policy.apply_formatter_profile(
profile,
style_preset=style,
quality_preset=quality,
),
clean=_clean,
row_from_inputs=_row_from_inputs,
row_core_tags=_row_core_tags,
soft_tags=_soft_tags,
hard_tags=_hard_tags,
fallback_text_to_sdxl=_fallback_text_to_sdxl,
assemble_prompt=_assemble_prompt,
combine_negative=_combine_negative,
sanitize_negative_text=sanitize_negative_text,
)
def format_sdxl_prompt(
source_text: str,
metadata_json: str = "",
@@ -221,85 +243,24 @@ def format_sdxl_prompt(
extra_negative: str = "",
formatter_profile: str = "manual_controls",
) -> dict[str, str]:
style_preset, quality_preset = sdxl_policy.apply_formatter_profile(
formatter_profile,
return sdxl_format_route.format_sdxl_prompt(
sdxl_format_route.SDXLFormatRequest(
source_text=source_text,
metadata_json=metadata_json,
negative_prompt=negative_prompt,
input_hint=input_hint,
target=target,
style_preset=style_preset,
quality_preset=quality_preset,
)
target = target if target in ("auto", "single", "softcore", "hardcore") else "auto"
nude_weight = max(0.1, min(3.0, float(nude_weight)))
row, method = _row_from_inputs(source_text, metadata_json, input_hint)
if row and row.get("mode") == "Insta/OF":
soft_row = row.get("softcore_row") if isinstance(row.get("softcore_row"), dict) else {}
hard_row = row.get("hardcore_row") if isinstance(row.get("hardcore_row"), dict) else {}
soft_body = _soft_tags(soft_row, row, nude_weight)
hard_body = _hard_tags(hard_row, row, nude_weight)
soft_prompt = _assemble_prompt(
soft_body,
style_preset,
quality_preset,
trigger,
prepend_trigger,
custom_style,
custom_quality,
extra_positive,
)
hard_prompt = _assemble_prompt(
hard_body,
style_preset,
quality_preset,
trigger,
prepend_trigger,
custom_style,
custom_quality,
extra_positive,
)
selected = hard_prompt if target == "hardcore" else soft_prompt
selected_negative = (
row.get("hardcore_negative_prompt") if target == "hardcore" else row.get("softcore_negative_prompt")
)
return {
"sdxl_prompt": selected,
"negative_prompt": sanitize_negative_text(
_combine_negative(SDXL_DEFAULT_NEGATIVE, selected_negative, negative_prompt, extra_negative)
trigger=trigger,
prepend_trigger=prepend_trigger,
preserve_trigger=preserve_trigger,
nude_weight=nude_weight,
custom_style=custom_style,
custom_quality=custom_quality,
extra_positive=extra_positive,
extra_negative=extra_negative,
formatter_profile=formatter_profile,
),
"sdxl_softcore_prompt": soft_prompt,
"sdxl_hardcore_prompt": hard_prompt,
"softcore_negative_prompt": sanitize_negative_text(
_combine_negative(SDXL_DEFAULT_NEGATIVE, row.get("softcore_negative_prompt"), extra_negative)
),
"hardcore_negative_prompt": sanitize_negative_text(
_combine_negative(SDXL_DEFAULT_NEGATIVE, row.get("hardcore_negative_prompt"), extra_negative)
),
"method": f"{method}:sdxl(insta_of_pair)",
}
if row:
body = ", ".join(_row_core_tags(row, nude_weight))
extracted_negative = _clean(row.get("negative_prompt"))
method = f"{method}:sdxl(metadata)"
else:
body, extracted_negative, method = _fallback_text_to_sdxl(source_text, preserve_trigger, nude_weight)
prompt = _assemble_prompt(
body,
style_preset,
quality_preset,
trigger,
prepend_trigger,
custom_style,
custom_quality,
extra_positive,
_sdxl_format_dependencies(),
)
return {
"sdxl_prompt": prompt,
"negative_prompt": sanitize_negative_text(
_combine_negative(SDXL_DEFAULT_NEGATIVE, extracted_negative, negative_prompt, extra_negative)
),
"sdxl_softcore_prompt": "",
"sdxl_hardcore_prompt": "",
"softcore_negative_prompt": "",
"hardcore_negative_prompt": "",
"method": method,
}
+122
View File
@@ -79,6 +79,7 @@ import row_route_metadata # noqa: E402
import row_subject_route # noqa: E402
import server_routes # noqa: E402
import sdxl_formatter # noqa: E402
import sdxl_format_route # noqa: E402
import sdxl_presets # noqa: E402
import sdxl_tag_policy # noqa: E402
import sdxl_tag_routes # noqa: E402
@@ -2763,6 +2764,126 @@ def smoke_sdxl_presets_policy() -> None:
_expect("score_9" not in profiled_prompt, "SDXL photo profile should switch away from Pony score quality tail")
def smoke_sdxl_format_route_policy() -> None:
row = _prompt_row(
name="sdxl_format_route_single",
category="woman",
subcategory="random",
seed=3701,
men_count=0,
camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
)
single_request = sdxl_format_route.SDXLFormatRequest(
source_text="",
metadata_json=_json(row),
target="single",
style_preset="flat_vector_pony",
quality_preset="pony_high",
trigger=SdxlTrigger,
prepend_trigger=True,
nude_weight=9.0,
extra_positive="sdxl route marker",
extra_negative="sdxl route negative",
formatter_profile="sdxl_photo",
)
typed_single = sdxl_format_route.format_sdxl_prompt_result(
single_request,
sdxl_formatter._sdxl_format_dependencies(),
)
public_single = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=single_request.metadata_json,
target=single_request.target,
style_preset=single_request.style_preset,
quality_preset=single_request.quality_preset,
trigger=single_request.trigger,
prepend_trigger=single_request.prepend_trigger,
nude_weight=single_request.nude_weight,
extra_positive=single_request.extra_positive,
extra_negative=single_request.extra_negative,
formatter_profile=single_request.formatter_profile,
)
_expect(typed_single.output == public_single, "Typed SDXL format route should match public single formatter output")
_expect(typed_single.branch == "metadata", "Typed SDXL format route changed single branch")
_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")
_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")
pair = pb.build_insta_of_pair(
row_number=1,
start_index=1,
seed=3702,
ethnicity="any",
figure="random",
no_plus_women=False,
no_black=False,
trigger=Trigger,
prepend_trigger_to_prompt=True,
options_json=_insta_options(),
character_cast=_character_cast(),
hardcore_position_config=_action_filter("penetration_only"),
)
pair_request = sdxl_format_route.SDXLFormatRequest(
source_text="",
metadata_json=_json(pair),
target="hardcore",
trigger=SdxlTrigger,
prepend_trigger=True,
extra_positive="pair sdxl route marker",
extra_negative="pair sdxl route negative",
)
typed_pair = sdxl_format_route.format_sdxl_prompt_result(
pair_request,
sdxl_formatter._sdxl_format_dependencies(),
)
public_pair = sdxl_formatter.format_sdxl_prompt(
"",
metadata_json=pair_request.metadata_json,
target=pair_request.target,
trigger=pair_request.trigger,
prepend_trigger=pair_request.prepend_trigger,
extra_positive=pair_request.extra_positive,
extra_negative=pair_request.extra_negative,
)
_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")
_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")
fallback_request = sdxl_format_route.SDXLFormatRequest(
source_text="Characters: woman. Erotic outfit: sheer dress. Camera: side view. Avoid: blur",
input_hint="prompt",
target="weird",
trigger=SdxlTrigger,
prepend_trigger=False,
nude_weight=0.01,
style_preset="none",
quality_preset="none",
)
typed_fallback = sdxl_format_route.format_sdxl_prompt_result(
fallback_request,
sdxl_formatter._sdxl_format_dependencies(),
)
public_fallback = sdxl_formatter.format_sdxl_prompt(
fallback_request.source_text,
input_hint=fallback_request.input_hint,
target=fallback_request.target,
trigger=fallback_request.trigger,
prepend_trigger=fallback_request.prepend_trigger,
nude_weight=fallback_request.nude_weight,
style_preset=fallback_request.style_preset,
quality_preset=fallback_request.quality_preset,
)
_expect(typed_fallback.output == public_fallback, "Typed SDXL format route should match public fallback output")
_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")
_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")
def smoke_sdxl_tag_policy() -> None:
row = _fixture_hardcore_row(
action_family="oral",
@@ -5628,6 +5749,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
("caption_text_policy", smoke_caption_text_policy),
("caption_metadata_routes", smoke_caption_metadata_routes),
("sdxl_presets_policy", smoke_sdxl_presets_policy),
("sdxl_format_route_policy", smoke_sdxl_format_route_policy),
("sdxl_tag_policy", smoke_sdxl_tag_policy),
("sdxl_tag_routes", smoke_sdxl_tag_routes),
("hardcore_position_config_policy", smoke_hardcore_position_config_policy),