"""Сборка 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"") 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, )