diff --git a/media-pipeline/core/character_engine/generation/__init__.py b/media-pipeline/core/character_engine/generation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/media-pipeline/core/character_engine/generation/prompt_builder.py b/media-pipeline/core/character_engine/generation/prompt_builder.py new file mode 100644 index 0000000..c80c095 --- /dev/null +++ b/media-pipeline/core/character_engine/generation/prompt_builder.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Optional + +from ..schema.character import Character, Outfit + + +@dataclass +class PromptConfig: + quality_prefix: str = "masterpiece, best quality, highly detailed" + negative: str = "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, deformed" + + +class PromptBuilder: + + def __init__(self, config: Optional[PromptConfig] = None): + self.config = config or PromptConfig() + + def build( + self, + character: Character, + outfit_name: str = "default", + scene: str = "", + extra_tags: Optional[list[str]] = None, + ) -> dict[str, str]: + segments = [] + + segments.append(self.config.quality_prefix) + + segments.append(self._appearance_segment(character)) + + outfit = self._find_outfit(character, outfit_name) + if outfit: + segments.append(outfit.description) + + if character.style_tags: + segments.append(", ".join(character.style_tags)) + + if scene: + segments.append(scene) + + if extra_tags: + segments.append(", ".join(extra_tags)) + + positive = ", ".join(seg for seg in segments if seg) + + return { + "positive": positive, + "negative": self.config.negative, + } + + def _appearance_segment(self, character: Character) -> str: + parts = [] + app = character.appearance + + gender_map = {"female": "1girl", "male": "1boy", "other": "1person"} + parts.append(gender_map.get(character.gender.value, "1person")) + + if app.hair: + parts.append(app.hair) + if app.eyes: + parts.append(app.eyes) + if app.skin: + parts.append(f"{app.skin} skin") + if app.build: + parts.append(f"{app.build} build") + for feat in app.features: + parts.append(feat) + + return ", ".join(parts) + + def _find_outfit(self, character: Character, name: str) -> Optional[Outfit]: + for outfit in character.outfits: + if outfit.name == name: + return outfit + if character.outfits: + return character.outfits[0] + return None