Files
Home_assistant/backend/app/homelab/anima_prompt.py
T
2026-06-15 03:15:08 +00:00

121 lines
3.6 KiB
Python

"""Сборка Anima-промптов: appearance из карточки + action/outfit из контекста."""
from __future__ import annotations
from dataclasses import dataclass
ANIMA_QUALITY = "masterpiece, best quality, score_7, anime"
ANIMA_NEGATIVE = "worst quality, low quality, score_1, score_2, score_3, blurry, jpeg artifacts, sepia"
_INVALID_TAGS = frozenset({
"pumped_up", "pumped", "looking_at_each_other", "couple",
"2girls", "2boys", "multiple_girls", "multiple_boys",
})
_JUNK_STANDALONE_TAGS = frozenset({
"white", "black", "skin", "ear", "ears", "girl", "boy", "fox", "wolf", "cat",
"short", "tall", "golden", "silver", "red", "blue", "green", "purple",
"pink", "brown", "eye", "eyes", "hair",
})
@dataclass(frozen=True)
class AnimaPromptBundle:
positive: str
negative: str
def _sanitize_tags(tag_str: str) -> str:
if not tag_str:
return ""
out: list[str] = []
seen: set[str] = set()
for raw in tag_str.split(","):
t = raw.strip()
if not t:
continue
key = t.lower().replace(" ", "_")
if key in seen or len(key) <= 2:
continue
if key in _INVALID_TAGS:
continue
if "_" not in key and key in _JUNK_STANDALONE_TAGS:
continue
seen.add(key)
out.append(t if "_" in t else key)
return ", ".join(out)
def _append_lora(parts: list[str], lora_name: str, lora_weight: float) -> None:
lora = (lora_name or "").strip()
if not lora:
return
weight = lora_weight if lora_weight > 0 else 0.8
parts.append(f"<lora:{lora}:{weight}>")
def build_character_image_prompt(
appearance_tags: str,
*,
action_tags: str = "",
outfit_tags: str = "",
environment_tags: str = "",
lora_name: str = "",
lora_weight: float = 0.8,
) -> AnimaPromptBundle:
"""Appearance (карточка) + action/outfit/env (контекст), только теги."""
appearance = _sanitize_tags(appearance_tags)
outfit = _sanitize_tags(outfit_tags)
action = _sanitize_tags(action_tags) or "looking_at_viewer, smile"
environment = _sanitize_tags(environment_tags) or "simple_background, soft_lighting"
parts = [ANIMA_QUALITY]
if appearance:
parts.append(appearance)
if outfit:
parts.append(outfit)
parts.append(action)
parts.append(environment)
_append_lora(parts, lora_name, lora_weight)
positive = ", ".join(p.strip() for p in parts if p.strip())
return AnimaPromptBundle(positive=positive, negative=ANIMA_NEGATIVE)
def build_draw_self_prompt(
appearance_tags: str,
*,
action_tags: str = "",
outfit_tags: str = "",
environment_tags: str = "",
lora_name: str = "",
lora_weight: float = 0.8,
) -> AnimaPromptBundle:
return build_character_image_prompt(
appearance_tags,
action_tags=action_tags,
outfit_tags=outfit_tags,
environment_tags=environment_tags,
lora_name=lora_name,
lora_weight=lora_weight,
)
def build_scene_tags_prompt(
scene_tags: str,
appearance_tags: str,
*,
lora_name: str = "",
lora_weight: float = 0.8,
) -> AnimaPromptBundle:
"""Готовые booru-теги сцены + appearance."""
scene = _sanitize_tags(scene_tags)
return build_character_image_prompt(
appearance_tags,
action_tags=scene,
outfit_tags="",
environment_tags="simple_background, soft_lighting",
lora_name=lora_name,
lora_weight=lora_weight,
)