"""Сборка Anima-промптов без LLM (теги, без POV/hybrid).""" from __future__ import annotations import re 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" _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 "_" 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_draw_self_prompt( appearance_tags: str, *, lora_name: str = "", lora_weight: float = 0.8, ) -> AnimaPromptBundle: """Портрет «нарисуй себя» — только booru-теги, без POV и prose.""" appearance = _sanitize_tags(appearance_tags) action = "looking_at_viewer, smile, upper_body, portrait" environment = "simple_background, soft_lighting" parts = [ANIMA_QUALITY] if appearance: parts.append(appearance) 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_scene_tags_prompt( scene_tags: str, appearance_tags: str, *, lora_name: str = "", lora_weight: float = 0.8, ) -> AnimaPromptBundle: """Прямая сцена из booru-тегов (без LLM).""" appearance = _sanitize_tags(appearance_tags) scene = _sanitize_tags(scene_tags) parts = [ANIMA_QUALITY] if appearance: parts.append(appearance) if scene: parts.append(scene) _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 looks_like_booru_tags(text: str) -> bool: """Грубая эвристика: строка похожа на теги, а не на прозу.""" raw = (text or "").strip() if not raw or len(raw) > 400: return False if raw.count(",") >= 2: return True return bool(re.search(r"\b\d+(girl|boy)s?\b", raw, re.I))