From a128b2dc9a9ed1aa35b6da7c9488de38f11a0273 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sat, 27 Jun 2026 01:25:39 +0200 Subject: [PATCH] Extract SDXL preset policy --- docs/prompt-architecture-improvement-plan.md | 4 +- docs/prompt-pool-routing-map.md | 1 + sdxl_formatter.py | 69 ++++++-------------- sdxl_presets.py | 66 +++++++++++++++++++ tools/prompt_smoke.py | 42 ++++++++++++ 5 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 sdxl_presets.py diff --git a/docs/prompt-architecture-improvement-plan.md b/docs/prompt-architecture-improvement-plan.md index dbeb977..70c3c64 100644 --- a/docs/prompt-architecture-improvement-plan.md +++ b/docs/prompt-architecture-improvement-plan.md @@ -283,11 +283,11 @@ Keep here: - metadata-family tag hints from `action_family`, `position_family`, and `position_keys`. - shared formatter input parsing from `formatter_input.py`. +- style presets, quality presets, default negative prompt, and action/position + family tag hints from `sdxl_presets.py`. Improve later: -- move presets into data dictionaries or JSON so adding styles does not require - editing formatter logic; - add formatter profiles for Pony, SDXL photo, and flat vector; - make fallback cleanup use the shared field-label inventory. diff --git a/docs/prompt-pool-routing-map.md b/docs/prompt-pool-routing-map.md index 6081102..4fee002 100644 --- a/docs/prompt-pool-routing-map.md +++ b/docs/prompt-pool-routing-map.md @@ -95,6 +95,7 @@ Core helper ownership: | `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. | | `formatter_input.py` | Shared formatter input parsing: text cleanup, metadata/source JSON detection, trigger-prefix stripping, `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. | ## Node IO Map diff --git a/sdxl_formatter.py b/sdxl_formatter.py index 99f5d40..5ec75f0 100644 --- a/sdxl_formatter.py +++ b/sdxl_formatter.py @@ -5,10 +5,12 @@ from typing import Any try: from . import formatter_input as input_policy + from . import sdxl_presets as sdxl_policy from .hardcore_action_metadata import normalize_hardcore_action_family 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_presets as sdxl_policy from hardcore_action_metadata import normalize_hardcore_action_family from prompt_hygiene import sanitize_negative_text, sanitize_tag_prompt @@ -19,50 +21,11 @@ TRIGGER_CANDIDATES = ( "mythp0rt", ) -SDXL_STYLE_PRESETS = { - "flat_vector_pony": "(skindentation:1.25), (flat color:2.0), no lineart, no outline, Flat vector", - "flat_vector": "(flat color:2.0), no lineart, no outline, Flat vector", - "photographic": "realistic photo, detailed skin texture, depth of field", - "none": "", -} - -SDXL_QUALITY_PRESETS = { - "pony_high": ( - "amazing quality, ultra detailed, 8k, very detailed, high detailed texture, " - "highly detailed anatomy, best quality, newest, very aesthetic, (score_9:1.1), " - "(score_8_up:1.1), (score_7_up:1.1), masterpiece, absurdres, highres" - ), - "sdxl_high": "masterpiece, best quality, amazing quality, ultra detailed, 8k, absurdres, highres", - "none": "", -} - -SDXL_DEFAULT_NEGATIVE = ( - "worst quality, low quality, normal quality, lowres, bad anatomy, bad hands, " - "extra fingers, missing fingers, fused fingers, deformed, disfigured, malformed body, " - "watermark, signature, text, logo, blurry, jpeg artifacts, censored, mosaic censor" -) - -SDXL_ACTION_FAMILY_TAGS = { - "foreplay": ("foreplay", "body contact"), - "outercourse": ("outercourse", "non-penetrative sex"), - "oral": ("oral sex",), - "penetration": ("penetrative sex", "penetration"), - "toy_double": ("double penetration", "toy-assisted sex"), - "climax": ("climax", "semen"), -} - -SDXL_POSITION_FAMILY_TAGS = { - "penetrative": ("penetrative sex",), - "foreplay": ("foreplay",), - "interaction": ("interaction",), - "manual": ("manual stimulation",), - "oral": ("oral sex",), - "outercourse": ("outercourse",), - "anal": ("anal sex",), - "climax": ("climax",), - "threesome": ("threesome",), - "group": ("group sex",), -} +SDXL_STYLE_PRESETS = sdxl_policy.SDXL_STYLE_PRESETS +SDXL_QUALITY_PRESETS = sdxl_policy.SDXL_QUALITY_PRESETS +SDXL_DEFAULT_NEGATIVE = sdxl_policy.SDXL_DEFAULT_NEGATIVE +SDXL_ACTION_FAMILY_TAGS = sdxl_policy.SDXL_ACTION_FAMILY_TAGS +SDXL_POSITION_FAMILY_TAGS = sdxl_policy.SDXL_POSITION_FAMILY_TAGS PROMPT_FIELD_LABELS = ( "Ages", @@ -88,11 +51,11 @@ PROMPT_FIELD_LABELS = ( def sdxl_style_preset_choices() -> list[str]: - return list(SDXL_STYLE_PRESETS) + return sdxl_policy.sdxl_style_preset_choices() def sdxl_quality_preset_choices() -> list[str]: - return list(SDXL_QUALITY_PRESETS) + return sdxl_policy.sdxl_quality_preset_choices() def _clean(value: Any) -> str: @@ -362,7 +325,10 @@ def _row_core_tags(row: dict[str, Any], nude_weight: float) -> list[str]: def _style_prefix(style_preset: str, trigger: str, prepend_trigger: bool, custom_style: str) -> str: - style = custom_style if _clean(custom_style) else SDXL_STYLE_PRESETS.get(style_preset, SDXL_STYLE_PRESETS["flat_vector_pony"]) + style = custom_style if _clean(custom_style) else SDXL_STYLE_PRESETS.get( + style_preset, + SDXL_STYLE_PRESETS[sdxl_policy.DEFAULT_STYLE_PRESET], + ) trigger = _clean(trigger) if prepend_trigger and trigger: return _combine_tags(style, trigger) @@ -370,7 +336,10 @@ def _style_prefix(style_preset: str, trigger: str, prepend_trigger: bool, custom def _quality_tail(quality_preset: str, custom_quality: str) -> str: - return _clean(custom_quality) or SDXL_QUALITY_PRESETS.get(quality_preset, SDXL_QUALITY_PRESETS["pony_high"]) + return _clean(custom_quality) or SDXL_QUALITY_PRESETS.get( + quality_preset, + SDXL_QUALITY_PRESETS[sdxl_policy.DEFAULT_QUALITY_PRESET], + ) def _soft_tags(row: dict[str, Any], root: dict[str, Any], nude_weight: float) -> str: @@ -488,8 +457,8 @@ def format_sdxl_prompt( extra_positive: str = "", extra_negative: str = "", ) -> dict[str, str]: - style_preset = style_preset if style_preset in SDXL_STYLE_PRESETS else "flat_vector_pony" - quality_preset = quality_preset if quality_preset in SDXL_QUALITY_PRESETS else "pony_high" + style_preset = sdxl_policy.normalize_style_preset(style_preset) + quality_preset = sdxl_policy.normalize_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) diff --git a/sdxl_presets.py b/sdxl_presets.py new file mode 100644 index 0000000..344a67c --- /dev/null +++ b/sdxl_presets.py @@ -0,0 +1,66 @@ +from __future__ import annotations + + +DEFAULT_STYLE_PRESET = "flat_vector_pony" +DEFAULT_QUALITY_PRESET = "pony_high" + +SDXL_STYLE_PRESETS = { + "flat_vector_pony": "(skindentation:1.25), (flat color:2.0), no lineart, no outline, Flat vector", + "flat_vector": "(flat color:2.0), no lineart, no outline, Flat vector", + "photographic": "realistic photo, detailed skin texture, depth of field", + "none": "", +} + +SDXL_QUALITY_PRESETS = { + "pony_high": ( + "amazing quality, ultra detailed, 8k, very detailed, high detailed texture, " + "highly detailed anatomy, best quality, newest, very aesthetic, (score_9:1.1), " + "(score_8_up:1.1), (score_7_up:1.1), masterpiece, absurdres, highres" + ), + "sdxl_high": "masterpiece, best quality, amazing quality, ultra detailed, 8k, absurdres, highres", + "none": "", +} + +SDXL_DEFAULT_NEGATIVE = ( + "worst quality, low quality, normal quality, lowres, bad anatomy, bad hands, " + "extra fingers, missing fingers, fused fingers, deformed, disfigured, malformed body, " + "watermark, signature, text, logo, blurry, jpeg artifacts, censored, mosaic censor" +) + +SDXL_ACTION_FAMILY_TAGS = { + "foreplay": ("foreplay", "body contact"), + "outercourse": ("outercourse", "non-penetrative sex"), + "oral": ("oral sex",), + "penetration": ("penetrative sex", "penetration"), + "toy_double": ("double penetration", "toy-assisted sex"), + "climax": ("climax", "semen"), +} + +SDXL_POSITION_FAMILY_TAGS = { + "penetrative": ("penetrative sex",), + "foreplay": ("foreplay",), + "interaction": ("interaction",), + "manual": ("manual stimulation",), + "oral": ("oral sex",), + "outercourse": ("outercourse",), + "anal": ("anal sex",), + "climax": ("climax",), + "threesome": ("threesome",), + "group": ("group sex",), +} + + +def sdxl_style_preset_choices() -> list[str]: + return list(SDXL_STYLE_PRESETS) + + +def sdxl_quality_preset_choices() -> list[str]: + return list(SDXL_QUALITY_PRESETS) + + +def normalize_style_preset(value: str) -> str: + return value if value in SDXL_STYLE_PRESETS else DEFAULT_STYLE_PRESET + + +def normalize_quality_preset(value: str) -> str: + return value if value in SDXL_QUALITY_PRESETS else DEFAULT_QUALITY_PRESET diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index e60c792..b529d6f 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -38,6 +38,7 @@ import location_config # noqa: E402 import prompt_builder as pb # noqa: E402 import row_normalization # noqa: E402 import sdxl_formatter # noqa: E402 +import sdxl_presets # noqa: E402 import seed_config # noqa: E402 @@ -899,6 +900,46 @@ def smoke_formatter_input_policy() -> None: _expect_text("formatter_input.caption", caption, 20) +def smoke_sdxl_presets_policy() -> None: + _expect( + sdxl_formatter.SDXL_STYLE_PRESETS is sdxl_presets.SDXL_STYLE_PRESETS, + "SDXL formatter style presets should delegate to sdxl_presets", + ) + _expect( + sdxl_formatter.SDXL_QUALITY_PRESETS is sdxl_presets.SDXL_QUALITY_PRESETS, + "SDXL formatter quality presets should delegate to sdxl_presets", + ) + _expect( + sdxl_formatter.SDXL_ACTION_FAMILY_TAGS is sdxl_presets.SDXL_ACTION_FAMILY_TAGS, + "SDXL formatter action-family tags should delegate to sdxl_presets", + ) + _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(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") + + row = _fixture_hardcore_row( + action_family="oral", + position_family="oral", + position_key="kneeling_oral", + position_keys=["kneeling_oral"], + ) + tags = sdxl_formatter._metadata_family_tags(row) + _expect("oral sex" in tags, "SDXL metadata family tags lost oral family tag") + _expect("kneeling oral" in tags, "SDXL metadata family tags lost position key tag") + formatted = sdxl_formatter.format_sdxl_prompt( + _json(row), + input_hint="auto", + style_preset="bad", + quality_preset="bad", + trigger=SdxlTrigger, + prepend_trigger=True, + ) + _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("score_9" in formatted.get("sdxl_prompt", ""), "SDXL invalid quality did not fall back to default preset") + + def smoke_hardcore_position_config_policy() -> None: _expect( pb.HARDCORE_POSITION_FAMILY_CHOICES is hardcore_position_config.HARDCORE_POSITION_FAMILY_CHOICES, @@ -2871,6 +2912,7 @@ SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [ ("character_profile_policy", smoke_character_profile_policy), ("row_normalization_policy", smoke_row_normalization_policy), ("formatter_input_policy", smoke_formatter_input_policy), + ("sdxl_presets_policy", smoke_sdxl_presets_policy), ("hardcore_position_config_policy", smoke_hardcore_position_config_policy), ("category_library_route", smoke_category_library_route), ("hardcore_category_routes", smoke_hardcore_category_routes),