Files
ComfyUI-Ethanfel-Prompt-Bui…/krea_normal_formatter.py
T

172 lines
5.8 KiB
Python

from __future__ import annotations
import re
from dataclasses import dataclass
from typing import Any, Callable
@dataclass(frozen=True)
class KreaNormalRowRequest:
row: dict[str, Any]
detail_level: str
style_mode: str
subject_type: str
primary: str
item: str
scene: str
pose: str
expression: str
composition: str
camera: str
camera_scene: str
style: str
@dataclass(frozen=True)
class KreaNormalRowPrompt:
prompt: str
method: str
def as_tuple(self) -> tuple[str, str]:
return self.prompt, self.method
@dataclass(frozen=True)
class KreaNormalRowDependencies:
clean: Callable[[Any], str]
row_value: Callable[[dict[str, Any], str, tuple[str, ...]], str]
age_subject: Callable[[dict[str, Any], str], str]
age_detail_phrase: Callable[[Any], str]
appearance_phrase: Callable[[dict[str, Any]], str]
with_indefinite_article: Callable[[str], str]
paragraph: Callable[[list[str]], str]
def _couple_clothing_phrase(item: str, clean: Callable[[Any], str]) -> str:
item = clean(item)
lower = item.lower()
partner_text = re.sub(r"\bPartner ([AB]) wears\b", r"Partner \1 wearing", item)
partner_text = re.sub(r"\bPartner ([AB]) has\b", r"Partner \1 with", partner_text)
if lower.startswith("partner a "):
return f"The outfits show {partner_text}"
if lower.startswith(("two ", "paired ", "coordinated ")):
return f"The outfits are {partner_text}"
return f"The couple wears {item}"
def _cap_first(text: str) -> str:
text = str(text or "").strip()
return f"{text[:1].upper()}{text[1:]}" if text else ""
def _couple_subject_phrase(subject: str, ages: str) -> str:
subject = _cap_first(subject or "adult couple")
ages = str(ages or "").strip()
if ages:
return f"{subject}, {ages}"
return subject
def _framed_composition_phrase(composition: str, prefix: str = "framed as") -> str:
composition = re.sub(r"\s+composition$", "", str(composition or "").strip(), flags=re.IGNORECASE)
composition = re.sub(
r"\bcomposition\b",
"frame",
composition,
flags=re.IGNORECASE,
).strip(" ,")
if not composition:
return ""
return f"{prefix} {composition}"
def _appearance_with_phrase(appearance: str, with_indefinite_article: Callable[[str], str]) -> str:
appearance = str(appearance or "").strip()
if not appearance:
return ""
first_clause = appearance.split(",", 1)[0].lower()
if re.search(r"\b(?:body|build|figure|frame|physique|silhouette)\b", first_clause):
appearance = with_indefinite_article(appearance)
return f"with {appearance}"
def format_normal_row_result(
request: KreaNormalRowRequest,
deps: KreaNormalRowDependencies,
) -> KreaNormalRowPrompt:
row = request.row
subject_type = request.subject_type
primary = request.primary
item = request.item
scene = request.scene
pose = request.pose
expression = request.expression
composition = request.composition
camera = request.camera
camera_scene = request.camera_scene
style = request.style
detail_level = request.detail_level
if primary in ("woman", "man") or subject_type in ("woman", "man", "single_any"):
subject = deps.age_subject(row, "adult woman")
appearance = deps.appearance_phrase(row)
subject_phrase = deps.with_indefinite_article(subject)
appearance_phrase = _appearance_with_phrase(appearance, deps.with_indefinite_article)
if appearance_phrase:
subject_phrase = f"{subject_phrase} {appearance_phrase}"
parts = [
subject_phrase,
f"wearing {item}" if item else "",
f"{pose}" if pose else "",
f"with {expression}" if expression else "",
f"in {scene}" if scene else "",
camera_scene,
_framed_composition_phrase(composition),
camera,
style if detail_level != "concise" else "",
]
return KreaNormalRowPrompt(
deps.paragraph([", ".join(part for part in parts[:6] if part), *parts[6:]]),
"metadata(single)",
)
if subject_type == "couple" or primary in ("two women", "two men", "a woman and a man"):
subject = deps.clean(row.get("subject_phrase") or primary or "adult couple")
if subject == "woman and man":
subject = "a woman and a man"
ages = deps.age_detail_phrase(deps.row_value(row, "age", ("Ages",)) or row.get("age_band"))
body = deps.row_value(row, "body", ("Body types",)) or deps.clean(row.get("body_type"))
parts = [
_couple_subject_phrase(subject, ages),
f"Body types: {body}" if body else "",
_couple_clothing_phrase(item, deps.clean) if item else "",
f"The pose is {pose}" if pose else "",
f"The setting is {scene}" if scene else "",
camera_scene,
f"Facial expressions are {expression}" if expression else "",
_framed_composition_phrase(composition, "The image is framed as"),
camera,
style if detail_level != "concise" else "",
]
return KreaNormalRowPrompt(deps.paragraph(parts), "metadata(couple)")
subject = deps.age_subject(row, primary or "adult scene")
parts = [
f"{subject}",
f"featuring {item}" if item else "",
f"in {scene}" if scene else "",
camera_scene,
f"with {expression}" if expression else "",
_framed_composition_phrase(composition),
camera,
style if detail_level != "concise" else "",
]
return KreaNormalRowPrompt(deps.paragraph(parts), "metadata(generic)")
def format_normal_row(
request: KreaNormalRowRequest,
deps: KreaNormalRowDependencies,
) -> tuple[str, str]:
return format_normal_row_result(request, deps).as_tuple()