Add SDXL formatter profiles

This commit is contained in:
2026-06-27 01:49:39 +02:00
parent 21da2949c6
commit 5ab2433ca7
8 changed files with 130 additions and 8 deletions
+16
View File
@@ -421,6 +421,22 @@ partitions, counters, or office rows are kept visible. In male-POV setups this
becomes a first-person spatial description and the external camera sentence is becomes a first-person spatial description and the external camera sentence is
suppressed. suppressed.
`SxCP SDXL Formatter` rewrites prompt builder output or `metadata_json` into
comma-tag SDXL/Pony-style prompts. Connect `metadata_json` when possible so
character, camera, outfit, and action metadata stay available to the tag route.
SDXL formatter controls:
- `formatter_profile`: `manual_controls` keeps `style_preset` and
`quality_preset` authoritative. `pony_flat_vector`, `sdxl_photo`, and
`flat_vector` apply coherent formatter defaults.
- `style_preset`: positive style anchor such as `flat_vector_pony`,
`flat_vector`, or `photographic`.
- `quality_preset`: quality/score tail such as `pony_high` or `sdxl_high`.
- `trigger` and `prepend_trigger_to_prompt`: explicit model/LoRA trigger
placement for SDXL-style workflows.
- `custom_style` and `custom_quality`: override the selected preset text.
`SxCP Caption Naturalizer` rewrites tag-like captions or labeled prompts into `SxCP Caption Naturalizer` rewrites tag-like captions or labeled prompts into
more natural language. Connect the prompt builder's `metadata_json` output to more natural language. Connect the prompt builder's `metadata_json` output to
`source_text` for the cleanest result. You can also connect `caption` or `source_text` for the cleanest result. You can also connect `caption` or
+1
View File
@@ -301,6 +301,7 @@ NODE_INPUT_TOOLTIPS = {
}, },
"SxCPSDXLFormatter": { "SxCPSDXLFormatter": {
"metadata_json": "Best input for SDXL tag formatting because it preserves cast, camera, outfit, and explicit action metadata.", "metadata_json": "Best input for SDXL tag formatting because it preserves cast, camera, outfit, and explicit action metadata.",
"formatter_profile": "High-level formatter defaults. manual_controls keeps style_preset and quality_preset authoritative.",
"style_preset": "Positive style anchor preset. flat_vector_pony matches the old SDXL tag style.", "style_preset": "Positive style anchor preset. flat_vector_pony matches the old SDXL tag style.",
"quality_preset": "Quality/score tag tail for SDXL or Pony-style checkpoints.", "quality_preset": "Quality/score tag tail for SDXL or Pony-style checkpoints.",
"custom_style": "Optional replacement for the style preset. Leave empty to use style_preset.", "custom_style": "Optional replacement for the style preset. Leave empty to use style_preset.",
+3 -1
View File
@@ -288,10 +288,12 @@ Keep here:
- shared formatter input parsing from `formatter_input.py`. - shared formatter input parsing from `formatter_input.py`.
- style presets, quality presets, default negative prompt, and action/position - style presets, quality presets, default negative prompt, and action/position
family tag hints from `sdxl_presets.py`. family tag hints from `sdxl_presets.py`.
- formatter profiles for manual controls, Pony flat-vector, SDXL photo, and
plain flat-vector styles live in `sdxl_presets.py` and are exposed by
`SxCP SDXL Formatter`.
Improve later: Improve later:
- add formatter profiles for Pony, SDXL photo, and flat vector;
- make fallback cleanup use the shared field-label inventory. - make fallback cleanup use the shared field-label inventory.
### Naturalizer Path ### Naturalizer Path
+3 -3
View File
@@ -96,7 +96,7 @@ Core helper ownership:
| `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup. | | `prompt_hygiene.py` | Generic prompt, caption, and negative-prompt cleanup. |
| `row_normalization.py` | Final prompt-row and pair metadata normalization: trigger prepending, extra-positive append, negative merge/dedupe, caption-part joining, and embedded soft/hard row sanitation. | | `row_normalization.py` | Final prompt-row and pair metadata normalization: trigger prepending, extra-positive append, negative merge/dedupe, caption-part joining, and embedded soft/hard row sanitation. |
| `formatter_input.py` | Shared formatter input parsing: text cleanup, metadata/source JSON detection, trigger-prefix stripping, shared prompt field-label inventory, `Avoid:` splitting, prompt-field extraction, and metadata row-value fallback. | | `formatter_input.py` | Shared formatter input parsing: text cleanup, metadata/source JSON detection, trigger-prefix stripping, shared prompt field-label inventory, `Avoid:` splitting, prompt-field extraction, and metadata row-value fallback. |
| `sdxl_presets.py` | SDXL style presets, quality presets, default negative prompt, and metadata-family tag hints used by the SDXL formatter and node choice lists. | | `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. |
| `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_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. |
## Node IO Map ## Node IO Map
@@ -651,8 +651,8 @@ not parse metadata. That is a wiring/input-hint issue, not a prompt pool issue.
- Normal metadata row: `_row_core_tags`. - Normal metadata row: `_row_core_tags`.
- Plain text fallback: `_fallback_text_to_sdxl`. - Plain text fallback: `_fallback_text_to_sdxl`.
Use this route for style triggers, weighted tag style, nude weighting, and Pony / Use this route for style triggers, weighted tag style, nude weighting, formatter
SDXL quality/style presets. profiles, and Pony / SDXL quality/style presets.
SDXL field consumption: SDXL field consumption:
+15 -2
View File
@@ -4,12 +4,22 @@ try:
from .caption_naturalizer import naturalize_caption from .caption_naturalizer import naturalize_caption
from .caption_policy import caption_profile_choices from .caption_policy import caption_profile_choices
from .krea_formatter import format_krea2_prompt from .krea_formatter import format_krea2_prompt
from .sdxl_formatter import format_sdxl_prompt, sdxl_quality_preset_choices, sdxl_style_preset_choices from .sdxl_formatter import (
format_sdxl_prompt,
sdxl_formatter_profile_choices,
sdxl_quality_preset_choices,
sdxl_style_preset_choices,
)
except ImportError: # Allows local smoke tests from the repository root. except ImportError: # Allows local smoke tests from the repository root.
from caption_naturalizer import naturalize_caption from caption_naturalizer import naturalize_caption
from caption_policy import caption_profile_choices from caption_policy import caption_profile_choices
from krea_formatter import format_krea2_prompt from krea_formatter import format_krea2_prompt
from sdxl_formatter import format_sdxl_prompt, sdxl_quality_preset_choices, sdxl_style_preset_choices from sdxl_formatter import (
format_sdxl_prompt,
sdxl_formatter_profile_choices,
sdxl_quality_preset_choices,
sdxl_style_preset_choices,
)
class SxCPCaptionNaturalizer: class SxCPCaptionNaturalizer:
@@ -138,6 +148,7 @@ class SxCPSDXLFormatter:
"source_text": ("STRING", {"default": "", "multiline": True}), "source_text": ("STRING", {"default": "", "multiline": True}),
"input_hint": (["auto", "metadata_json", "prompt"], {"default": "auto"}), "input_hint": (["auto", "metadata_json", "prompt"], {"default": "auto"}),
"target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}), "target": (["auto", "single", "softcore", "hardcore"], {"default": "auto"}),
"formatter_profile": (sdxl_formatter_profile_choices(), {"default": "manual_controls"}),
"style_preset": (sdxl_style_preset_choices(), {"default": "flat_vector_pony"}), "style_preset": (sdxl_style_preset_choices(), {"default": "flat_vector_pony"}),
"quality_preset": (sdxl_quality_preset_choices(), {"default": "pony_high"}), "quality_preset": (sdxl_quality_preset_choices(), {"default": "pony_high"}),
"trigger": ("STRING", {"default": "mythp0rt", "multiline": False}), "trigger": ("STRING", {"default": "mythp0rt", "multiline": False}),
@@ -174,6 +185,7 @@ class SxCPSDXLFormatter:
source_text, source_text,
input_hint, input_hint,
target, target,
formatter_profile,
style_preset, style_preset,
quality_preset, quality_preset,
trigger, trigger,
@@ -195,6 +207,7 @@ class SxCPSDXLFormatter:
negative_prompt=negative_prompt or "", negative_prompt=negative_prompt or "",
input_hint=input_hint, input_hint=input_hint,
target=target, target=target,
formatter_profile=formatter_profile,
style_preset=style_preset, style_preset=style_preset,
quality_preset=quality_preset, quality_preset=quality_preset,
trigger=trigger, trigger=trigger,
+11 -2
View File
@@ -23,6 +23,7 @@ TRIGGER_CANDIDATES = (
SDXL_STYLE_PRESETS = sdxl_policy.SDXL_STYLE_PRESETS SDXL_STYLE_PRESETS = sdxl_policy.SDXL_STYLE_PRESETS
SDXL_QUALITY_PRESETS = sdxl_policy.SDXL_QUALITY_PRESETS SDXL_QUALITY_PRESETS = sdxl_policy.SDXL_QUALITY_PRESETS
SDXL_FORMATTER_PROFILES = sdxl_policy.SDXL_FORMATTER_PROFILES
SDXL_DEFAULT_NEGATIVE = sdxl_policy.SDXL_DEFAULT_NEGATIVE SDXL_DEFAULT_NEGATIVE = sdxl_policy.SDXL_DEFAULT_NEGATIVE
SDXL_ACTION_FAMILY_TAGS = sdxl_policy.SDXL_ACTION_FAMILY_TAGS SDXL_ACTION_FAMILY_TAGS = sdxl_policy.SDXL_ACTION_FAMILY_TAGS
SDXL_POSITION_FAMILY_TAGS = sdxl_policy.SDXL_POSITION_FAMILY_TAGS SDXL_POSITION_FAMILY_TAGS = sdxl_policy.SDXL_POSITION_FAMILY_TAGS
@@ -38,6 +39,10 @@ def sdxl_quality_preset_choices() -> list[str]:
return sdxl_policy.sdxl_quality_preset_choices() return sdxl_policy.sdxl_quality_preset_choices()
def sdxl_formatter_profile_choices() -> list[str]:
return sdxl_policy.sdxl_formatter_profile_choices()
def _clean(value: Any) -> str: def _clean(value: Any) -> str:
return input_policy.clean_text(value) return input_policy.clean_text(value)
@@ -436,9 +441,13 @@ def format_sdxl_prompt(
custom_quality: str = "", custom_quality: str = "",
extra_positive: str = "", extra_positive: str = "",
extra_negative: str = "", extra_negative: str = "",
formatter_profile: str = "manual_controls",
) -> dict[str, str]: ) -> dict[str, str]:
style_preset = sdxl_policy.normalize_style_preset(style_preset) style_preset, quality_preset = sdxl_policy.apply_formatter_profile(
quality_preset = sdxl_policy.normalize_quality_preset(quality_preset) formatter_profile,
style_preset=style_preset,
quality_preset=quality_preset,
)
target = target if target in ("auto", "single", "softcore", "hardcore") else "auto" target = target if target in ("auto", "single", "softcore", "hardcore") else "auto"
nude_weight = max(0.1, min(3.0, float(nude_weight))) nude_weight = max(0.1, min(3.0, float(nude_weight)))
row, method = _row_from_inputs(source_text, metadata_json, input_hint) row, method = _row_from_inputs(source_text, metadata_json, input_hint)
+38
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
DEFAULT_STYLE_PRESET = "flat_vector_pony" DEFAULT_STYLE_PRESET = "flat_vector_pony"
DEFAULT_QUALITY_PRESET = "pony_high" DEFAULT_QUALITY_PRESET = "pony_high"
DEFAULT_FORMATTER_PROFILE = "manual_controls"
SDXL_STYLE_PRESETS = { SDXL_STYLE_PRESETS = {
"flat_vector_pony": "(skindentation:1.25), (flat color:2.0), no lineart, no outline, Flat vector", "flat_vector_pony": "(skindentation:1.25), (flat color:2.0), no lineart, no outline, Flat vector",
@@ -21,6 +22,22 @@ SDXL_QUALITY_PRESETS = {
"none": "", "none": "",
} }
SDXL_FORMATTER_PROFILES = {
"manual_controls": {},
"pony_flat_vector": {
"style_preset": "flat_vector_pony",
"quality_preset": "pony_high",
},
"sdxl_photo": {
"style_preset": "photographic",
"quality_preset": "sdxl_high",
},
"flat_vector": {
"style_preset": "flat_vector",
"quality_preset": "sdxl_high",
},
}
SDXL_DEFAULT_NEGATIVE = ( SDXL_DEFAULT_NEGATIVE = (
"worst quality, low quality, normal quality, lowres, bad anatomy, bad hands, " "worst quality, low quality, normal quality, lowres, bad anatomy, bad hands, "
"extra fingers, missing fingers, fused fingers, deformed, disfigured, malformed body, " "extra fingers, missing fingers, fused fingers, deformed, disfigured, malformed body, "
@@ -58,9 +75,30 @@ def sdxl_quality_preset_choices() -> list[str]:
return list(SDXL_QUALITY_PRESETS) return list(SDXL_QUALITY_PRESETS)
def sdxl_formatter_profile_choices() -> list[str]:
return list(SDXL_FORMATTER_PROFILES)
def normalize_style_preset(value: str) -> str: def normalize_style_preset(value: str) -> str:
return value if value in SDXL_STYLE_PRESETS else DEFAULT_STYLE_PRESET return value if value in SDXL_STYLE_PRESETS else DEFAULT_STYLE_PRESET
def normalize_quality_preset(value: str) -> str: def normalize_quality_preset(value: str) -> str:
return value if value in SDXL_QUALITY_PRESETS else DEFAULT_QUALITY_PRESET return value if value in SDXL_QUALITY_PRESETS else DEFAULT_QUALITY_PRESET
def normalize_formatter_profile(value: str) -> str:
return value if value in SDXL_FORMATTER_PROFILES else DEFAULT_FORMATTER_PROFILE
def apply_formatter_profile(
formatter_profile: str,
*,
style_preset: str,
quality_preset: str,
) -> tuple[str, str]:
profile = SDXL_FORMATTER_PROFILES[normalize_formatter_profile(formatter_profile)]
return (
normalize_style_preset(profile.get("style_preset", style_preset)),
normalize_quality_preset(profile.get("quality_preset", quality_preset)),
)
+43
View File
@@ -1034,14 +1034,41 @@ def smoke_sdxl_presets_policy() -> None:
sdxl_formatter.SDXL_QUALITY_PRESETS is sdxl_presets.SDXL_QUALITY_PRESETS, sdxl_formatter.SDXL_QUALITY_PRESETS is sdxl_presets.SDXL_QUALITY_PRESETS,
"SDXL formatter quality presets should delegate to sdxl_presets", "SDXL formatter quality presets should delegate to sdxl_presets",
) )
_expect(
sdxl_formatter.SDXL_FORMATTER_PROFILES is sdxl_presets.SDXL_FORMATTER_PROFILES,
"SDXL formatter profiles should delegate to sdxl_presets",
)
_expect( _expect(
sdxl_formatter.SDXL_ACTION_FAMILY_TAGS is sdxl_presets.SDXL_ACTION_FAMILY_TAGS, sdxl_formatter.SDXL_ACTION_FAMILY_TAGS is sdxl_presets.SDXL_ACTION_FAMILY_TAGS,
"SDXL formatter action-family tags should delegate to sdxl_presets", "SDXL formatter action-family tags should delegate to sdxl_presets",
) )
_expect("sdxl_photo" in sdxl_presets.sdxl_formatter_profile_choices(), "SDXL profile choices lost sdxl_photo")
_expect("flat_vector_pony" in sdxl_presets.sdxl_style_preset_choices(), "SDXL style preset choices lost default") _expect("flat_vector_pony" in sdxl_presets.sdxl_style_preset_choices(), "SDXL style preset choices lost default")
_expect("pony_high" in sdxl_presets.sdxl_quality_preset_choices(), "SDXL quality preset choices lost default") _expect("pony_high" in sdxl_presets.sdxl_quality_preset_choices(), "SDXL quality preset choices lost default")
_expect(
sdxl_presets.normalize_formatter_profile("bad") == sdxl_presets.DEFAULT_FORMATTER_PROFILE,
"SDXL invalid profile fallback changed",
)
_expect(sdxl_presets.normalize_style_preset("bad") == sdxl_presets.DEFAULT_STYLE_PRESET, "SDXL invalid style fallback changed") _expect(sdxl_presets.normalize_style_preset("bad") == sdxl_presets.DEFAULT_STYLE_PRESET, "SDXL invalid style fallback changed")
_expect(sdxl_presets.normalize_quality_preset("bad") == sdxl_presets.DEFAULT_QUALITY_PRESET, "SDXL invalid quality fallback changed") _expect(sdxl_presets.normalize_quality_preset("bad") == sdxl_presets.DEFAULT_QUALITY_PRESET, "SDXL invalid quality fallback changed")
_expect(
sdxl_presets.apply_formatter_profile(
"sdxl_photo",
style_preset="flat_vector_pony",
quality_preset="pony_high",
)
== ("photographic", "sdxl_high"),
"SDXL photo profile overrides changed",
)
_expect(
sdxl_presets.apply_formatter_profile(
"manual_controls",
style_preset="flat_vector",
quality_preset="none",
)
== ("flat_vector", "none"),
"SDXL manual profile should preserve explicit controls",
)
row = _fixture_hardcore_row( row = _fixture_hardcore_row(
action_family="oral", action_family="oral",
@@ -1063,6 +1090,18 @@ def smoke_sdxl_presets_policy() -> None:
_expect_trigger_once("sdxl_presets.formatted_prompt", formatted.get("sdxl_prompt"), SdxlTrigger) _expect_trigger_once("sdxl_presets.formatted_prompt", formatted.get("sdxl_prompt"), SdxlTrigger)
_expect("Flat vector" in formatted.get("sdxl_prompt", ""), "SDXL invalid style did not fall back to default preset") _expect("Flat vector" in formatted.get("sdxl_prompt", ""), "SDXL invalid style did not fall back to default preset")
_expect("score_9" in formatted.get("sdxl_prompt", ""), "SDXL invalid quality did not fall back to default preset") _expect("score_9" in formatted.get("sdxl_prompt", ""), "SDXL invalid quality did not fall back to default preset")
profiled = sdxl_formatter.format_sdxl_prompt(
_json(row),
input_hint="auto",
formatter_profile="sdxl_photo",
style_preset="flat_vector_pony",
quality_preset="pony_high",
trigger=SdxlTrigger,
prepend_trigger=True,
)
profiled_prompt = profiled.get("sdxl_prompt", "")
_expect("realistic photo" in profiled_prompt, "SDXL photo profile did not apply photographic style")
_expect("score_9" not in profiled_prompt, "SDXL photo profile should switch away from Pony score quality tail")
def smoke_hardcore_position_config_policy() -> None: def smoke_hardcore_position_config_policy() -> None:
@@ -2824,6 +2863,7 @@ def smoke_node_formatter_registration() -> None:
"A woman standing by a window", "A woman standing by a window",
"prompt", "prompt",
"single", "single",
"manual_controls",
"flat_vector_pony", "flat_vector_pony",
"pony_high", "pony_high",
"mythp0rt", "mythp0rt",
@@ -2835,6 +2875,9 @@ def smoke_node_formatter_registration() -> None:
_expect_trigger_once("node_formatter.sdxl_prompt", sdxl_output[0], "mythp0rt") _expect_trigger_once("node_formatter.sdxl_prompt", sdxl_output[0], "mythp0rt")
_expect_text("node_formatter.sdxl_negative", sdxl_output[1], 20) _expect_text("node_formatter.sdxl_negative", sdxl_output[1], 20)
_expect(sdxl_output[6].startswith("text("), "SDXL Formatter method changed unexpectedly") _expect(sdxl_output[6].startswith("text("), "SDXL Formatter method changed unexpectedly")
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("tooltip" in sdxl_inputs["formatter_profile"][1], "SDXL formatter_profile tooltip injection missing")
def smoke_node_insta_registration() -> None: def smoke_node_insta_registration() -> None: