Files
ComfyUI-Ethanfel-Prompt-Bui…/sdxl_formatter.py
T
2026-06-27 11:48:54 +02:00

306 lines
9.8 KiB
Python

from __future__ import annotations
from typing import Any
try:
from . import formatter_input as input_policy
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_tag_policy
import sdxl_tag_routes
import sdxl_presets as sdxl_policy
from prompt_hygiene import sanitize_negative_text, sanitize_tag_prompt
TRIGGER_CANDIDATES = (
"sxcpinup_coloredpencil",
"sxcppnl7",
"mythp0rt",
)
SDXL_STYLE_PRESETS = sdxl_policy.SDXL_STYLE_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_ACTION_FAMILY_TAGS = sdxl_policy.SDXL_ACTION_FAMILY_TAGS
SDXL_POSITION_FAMILY_TAGS = sdxl_policy.SDXL_POSITION_FAMILY_TAGS
PROMPT_FIELD_LABELS = input_policy.prompt_field_labels()
def sdxl_style_preset_choices() -> list[str]:
return sdxl_policy.sdxl_style_preset_choices()
def sdxl_quality_preset_choices() -> list[str]:
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:
return input_policy.clean_text(value)
def _maybe_json(text: str) -> dict[str, Any] | None:
return input_policy.maybe_json(text)
def _row_from_inputs(source_text: str, metadata_json: str, input_hint: str) -> tuple[dict[str, Any] | None, str]:
return input_policy.row_from_inputs(source_text, metadata_json, input_hint)
def _strip_trigger(text: str, preserve_trigger: bool) -> str:
return input_policy.strip_trigger_prefix(text, TRIGGER_CANDIDATES, preserve_trigger=preserve_trigger)
def _split_avoid(text: str) -> tuple[str, str]:
return input_policy.split_avoid(text)
def _strip_prompt_field_labels(text: str) -> str:
return input_policy.strip_prompt_field_labels(text, field_labels=PROMPT_FIELD_LABELS)
def _split_tag_text(text: Any) -> list[str]:
return sdxl_tag_policy.split_tag_text(text)
def _tag_key(tag: str) -> str:
return sdxl_tag_policy.tag_key(tag)
def _add(tags: list[str], seen: set[str], value: Any) -> None:
sdxl_tag_policy.add(tags, seen, value)
def _add_one(tags: list[str], seen: set[str], tag: str) -> None:
sdxl_tag_policy.add_one(tags, seen, tag)
def _metadata_family_tags(row: dict[str, Any]) -> list[str]:
return sdxl_tag_policy.metadata_family_tags(row)
def _formatter_hint_tags(*rows: dict[str, Any]) -> list[str]:
return sdxl_tag_policy.formatter_hint_tags(*rows)
def _combine_tags(*parts: Any) -> str:
return sdxl_tag_policy.combine_tags(*parts)
def _combine_negative(*parts: Any) -> str:
return sdxl_tag_policy.combine_negative(*parts)
def _count_tag(women_count: int = 0, men_count: int = 0) -> list[str]:
return sdxl_tag_policy.count_tag(women_count, men_count)
def _infer_counts(row: dict[str, Any]) -> tuple[int, int]:
return sdxl_tag_policy.infer_counts(row)
def _character_tags_from_descriptor(descriptor: Any) -> list[str]:
return sdxl_tag_policy.character_tags_from_descriptor(descriptor)
def _normal_character_tags(row: dict[str, Any]) -> list[str]:
return sdxl_tag_policy.normal_character_tags(row)
def _camera_tags_from_config(config: Any) -> list[str]:
return sdxl_tag_policy.camera_tags_from_config(config)
def _camera_tags(row: dict[str, Any], directive: Any = "", config: Any = None) -> list[str]:
return sdxl_tag_policy.camera_tags(row, directive, config)
def _explicit_tags(text: str, nude_weight: float) -> list[str]:
return sdxl_tag_policy.explicit_tags(text, nude_weight)
def _sdxl_tag_route_dependencies() -> sdxl_tag_routes.SDXLTagRouteDependencies:
return sdxl_tag_policy.tag_route_dependencies()
def _row_core_tags(row: dict[str, Any], nude_weight: float) -> list[str]:
return sdxl_tag_routes.row_core_tags(
sdxl_tag_routes.SDXLRowTagRequest(row, nude_weight),
_sdxl_tag_route_dependencies(),
)
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[sdxl_policy.DEFAULT_STYLE_PRESET],
)
trigger = _clean(trigger)
if prepend_trigger and trigger:
return _combine_tags(style, trigger)
return style
def _quality_tail(quality_preset: str, custom_quality: str) -> str:
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:
return sdxl_tag_routes.soft_tags(
sdxl_tag_routes.SDXLPairTagRequest(row, root, nude_weight),
_sdxl_tag_route_dependencies(),
)
def _hard_tags(row: dict[str, Any], root: dict[str, Any], nude_weight: float) -> str:
return sdxl_tag_routes.hard_tags(
sdxl_tag_routes.SDXLPairTagRequest(row, root, nude_weight),
_sdxl_tag_route_dependencies(),
)
def _assemble_prompt(
body_tags: str,
style_preset: str,
quality_preset: str,
trigger: str,
prepend_trigger: bool,
custom_style: str,
custom_quality: str,
extra_positive: str,
) -> str:
return sanitize_tag_prompt(
_combine_tags(
_style_prefix(style_preset, trigger, prepend_trigger, custom_style),
body_tags,
_quality_tail(quality_preset, custom_quality),
extra_positive,
),
triggers=(trigger,),
)
def _fallback_text_to_sdxl(
source_text: str,
preserve_trigger: bool,
nude_weight: float,
) -> tuple[str, str, str]:
positive, negative = _split_avoid(_strip_trigger(source_text, preserve_trigger))
positive = _strip_prompt_field_labels(positive)
tags = _combine_tags(positive, ", ".join(_explicit_tags(positive, nude_weight)))
return tags, negative, "text(fallback)"
def format_sdxl_prompt(
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",
) -> dict[str, str]:
style_preset, quality_preset = sdxl_policy.apply_formatter_profile(
formatter_profile,
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)
),
"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,
)
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,
}