Spaces:
Sleeping
Sleeping
| """ | |
| Image Generation Utility - DALL-E 3 Integration for Character Portraits | |
| """ | |
| import os | |
| import base64 | |
| from typing import Optional, Tuple, Literal | |
| from pathlib import Path | |
| import requests | |
| from io import BytesIO | |
| import openai | |
| from openai import OpenAI | |
| from src.config import config | |
| from src.models.character import Character, DnDRace, DnDClass | |
| ImageProvider = Literal["openai", "huggingface", "auto"] | |
| # Skin tone options by race (3-5 options each) | |
| RACE_SKIN_TONES = { | |
| DnDRace.HUMAN: [ | |
| "Fair/Pale", | |
| "Light", | |
| "Medium/Olive", | |
| "Tan/Brown", | |
| "Deep/Dark" | |
| ], | |
| DnDRace.ELF: [ | |
| "Pale/Moonlight", | |
| "Porcelain", | |
| "Honey/Golden", | |
| "Bronze", | |
| "Dark Copper" | |
| ], | |
| DnDRace.DWARF: [ | |
| "Ruddy/Tan", | |
| "Bronze", | |
| "Deep Brown", | |
| "Stone Gray" | |
| ], | |
| DnDRace.HALFLING: [ | |
| "Fair/Rosy", | |
| "Light Tan", | |
| "Medium Brown", | |
| "Dark Brown" | |
| ], | |
| DnDRace.DRAGONBORN: [ | |
| "Brass (Golden)", | |
| "Copper (Reddish)", | |
| "Bronze (Brown)", | |
| "Silver (Silvery)", | |
| "Red (Crimson)", | |
| "Blue (Sapphire)", | |
| "Green (Emerald)", | |
| "Black (Obsidian)", | |
| "White (Pearl)" | |
| ], | |
| DnDRace.GNOME: [ | |
| "Fair/Pink", | |
| "Tan/Sandy", | |
| "Light Brown", | |
| "Deep Brown" | |
| ], | |
| DnDRace.HALF_ELF: [ | |
| "Fair", | |
| "Light Olive", | |
| "Medium Tan", | |
| "Brown", | |
| "Dark" | |
| ], | |
| DnDRace.HALF_ORC: [ | |
| "Gray-Green", | |
| "Olive-Green", | |
| "Deep Green", | |
| "Gray-Brown" | |
| ], | |
| DnDRace.TIEFLING: [ | |
| "Red", | |
| "Purple", | |
| "Blue-Gray", | |
| "Dark Gray", | |
| "Pink-Red" | |
| ], | |
| DnDRace.DROW: [ | |
| "Dark Gray", | |
| "Purple-Black", | |
| "Blue-Black", | |
| "Obsidian Black" | |
| ] | |
| } | |
| class ImageGenerator: | |
| """Generate character portraits using DALL-E 3 or HuggingFace Inference""" | |
| def __init__(self): | |
| """Initialize image generator with available providers""" | |
| # Read API keys directly from environment | |
| self.openai_api_key = os.getenv("OPENAI_API_KEY") | |
| self.hf_api_key = os.getenv("HUGGINGFACE_API_KEY") or os.getenv("HF_TOKEN") | |
| self.output_dir = Path("data/portraits") | |
| self.output_dir.mkdir(parents=True, exist_ok=True) | |
| # HuggingFace model for fallback (Stable Diffusion XL) | |
| self.hf_model = "stabilityai/stable-diffusion-xl-base-1.0" | |
| # Check available providers | |
| self.has_openai = bool(self.openai_api_key) | |
| self.has_hf = bool(self.hf_api_key) | |
| if self.openai_api_key: | |
| print(f"β OpenAI key found for image generation (key: {self.openai_api_key[:10]}...)") | |
| if self.hf_api_key: | |
| print(f"β HuggingFace key found for image generation (key: {self.hf_api_key[:10]}...)") | |
| if not self.has_openai and not self.has_hf: | |
| print("β οΈ Warning: No image generation API keys found") | |
| print(" Image generation will not be available") | |
| print(" Add OPENAI_API_KEY or HUGGINGFACE_API_KEY to HF Spaces Secrets") | |
| def generate_character_portrait( | |
| self, | |
| character: Character, | |
| style: str = "fantasy art", | |
| quality: str = "standard", | |
| size: str = "1024x1024", | |
| provider: ImageProvider = "auto" | |
| ) -> Tuple[Optional[str], Optional[bytes]]: | |
| """ | |
| Generate a character portrait using DALL-E 3 or HuggingFace | |
| Args: | |
| character: Character object with full details | |
| style: Art style (e.g., "fantasy art", "digital painting", "anime") | |
| quality: Image quality ("standard" or "hd") - OpenAI only | |
| size: Image size ("1024x1024", "1024x1792", "1792x1024") | |
| provider: "openai", "huggingface", or "auto" (tries OpenAI first, falls back to HF) | |
| Returns: | |
| Tuple of (file_path, image_bytes) | |
| """ | |
| # Build detailed prompt from character | |
| prompt = self._build_character_prompt(character, style) | |
| # Determine which provider to use | |
| if provider == "auto": | |
| # Try OpenAI first, fallback to HuggingFace | |
| if self.has_openai: | |
| try: | |
| image_bytes = self._generate_image_openai(prompt, quality, size) | |
| except Exception as openai_error: | |
| print(f"OpenAI failed: {openai_error}") | |
| if self.has_hf: | |
| print("Falling back to HuggingFace Inference...") | |
| image_bytes = self._generate_image_huggingface(prompt) | |
| else: | |
| raise # Re-raise OpenAI error if no fallback | |
| elif self.has_hf: | |
| image_bytes = self._generate_image_huggingface(prompt) | |
| else: | |
| raise ValueError("No image generation provider available") | |
| elif provider == "openai": | |
| if not self.has_openai: | |
| raise ValueError("OpenAI API key not configured") | |
| image_bytes = self._generate_image_openai(prompt, quality, size) | |
| elif provider == "huggingface": | |
| if not self.has_hf: | |
| raise ValueError("HuggingFace API key not configured") | |
| image_bytes = self._generate_image_huggingface(prompt) | |
| else: | |
| raise ValueError(f"Unknown provider: {provider}") | |
| if image_bytes: | |
| # Save to file | |
| filename = f"{character.id}_portrait.png" | |
| file_path = self.output_dir / filename | |
| with open(file_path, 'wb') as f: | |
| f.write(image_bytes) | |
| return str(file_path), image_bytes | |
| return None, None | |
| def generate_custom_portrait( | |
| self, | |
| prompt: str, | |
| character_id: Optional[str] = None, | |
| quality: str = "standard", | |
| size: str = "1024x1024" | |
| ) -> Tuple[Optional[str], Optional[bytes]]: | |
| """ | |
| Generate a portrait from custom prompt | |
| Args: | |
| prompt: Custom DALL-E prompt | |
| character_id: Optional character ID for filename | |
| quality: Image quality | |
| size: Image size | |
| Returns: | |
| Tuple of (file_path, image_bytes) | |
| """ | |
| try: | |
| image_bytes = self._generate_image(prompt, quality, size) | |
| if image_bytes: | |
| # Save to file | |
| if character_id: | |
| filename = f"{character_id}_custom_portrait.png" | |
| else: | |
| import uuid | |
| filename = f"portrait_{uuid.uuid4().hex[:8]}.png" | |
| file_path = self.output_dir / filename | |
| with open(file_path, 'wb') as f: | |
| f.write(image_bytes) | |
| return str(file_path), image_bytes | |
| return None, None | |
| except Exception as e: | |
| print(f"Error generating custom image: {e}") | |
| return None, None | |
| def _build_character_prompt(self, character: Character, style: str) -> str: | |
| """ | |
| Build detailed DALL-E prompt from character data | |
| Includes race, class, gender, alignment, skin tone, physical description | |
| """ | |
| # Base description | |
| parts = [] | |
| # Rich style prefix | |
| parts.append(f"Highly detailed {style} character portrait,") | |
| # Determine gender for trait customization | |
| character_gender = None | |
| if hasattr(character, 'gender') and character.gender and character.gender != "Not specified": | |
| character_gender = character.gender.lower() | |
| # Build VERY strong gender emphasis | |
| gender_prefix = "" | |
| gender_features = "" | |
| if character_gender == "female": | |
| gender_prefix = "BEAUTIFUL FEMININE FEMALE WOMAN" | |
| gender_features = "distinctly feminine facial features, soft feminine jawline, delicate bone structure, graceful feminine appearance" | |
| elif character_gender == "male": | |
| gender_prefix = "HANDSOME MASCULINE MALE MAN" | |
| gender_features = "distinctly masculine facial features, strong masculine jawline, defined bone structure, rugged masculine appearance" | |
| elif character_gender: | |
| gender_prefix = character_gender.upper() | |
| gender_features = "unique facial features" | |
| else: | |
| gender_prefix = "PERSON" | |
| gender_features = "expressive facial features" | |
| # Get skin tone with STRONG emphasis | |
| skin_emphasis = "" | |
| if hasattr(character, 'skin_tone') and character.skin_tone: | |
| # Make skin tone VERY prominent | |
| skin_emphasis = f"WITH {character.skin_tone.upper()} COLORED SKIN," | |
| # Build the core character description | |
| parts.append(f"{gender_prefix} {character.race.value.upper()} {character.character_class.value.upper()} {skin_emphasis}") | |
| # Add gender-specific features and race traits (passing gender for customization) | |
| race_traits = self._get_race_traits(character.race, character_gender) | |
| parts.append(f"{gender_features}, {race_traits},") | |
| # Rich class-specific elements | |
| class_elements = self._get_class_elements(character.character_class) | |
| parts.append(f"{class_elements},") | |
| # Alignment influence | |
| alignment_mood = self._get_alignment_mood(character.alignment.value) | |
| if alignment_mood: | |
| parts.append(f"{alignment_mood},") | |
| # Rich artistic details with gender reinforcement | |
| parts.append("epic fantasy character portrait, professional illustration,") | |
| if character_gender == "female": | |
| parts.append("CLEARLY FEMALE CHARACTER, feminine beauty,") | |
| elif character_gender == "male": | |
| parts.append("CLEARLY MALE CHARACTER, masculine presence,") | |
| parts.append("highly detailed face with intricate features, expressive eyes,") | |
| parts.append("dramatic cinematic lighting with rim light, volumetric atmosphere,") | |
| parts.append("sharp focus on facial features and expression,") | |
| parts.append(f"masterpiece quality {style} artwork, trending on artstation, 8k resolution") | |
| prompt = " ".join(parts) | |
| # Ensure prompt isn't too long (DALL-E 3 has 4000 char limit) | |
| if len(prompt) > 3000: | |
| print(f"β οΈ Warning: Prompt truncated from {len(prompt)} to 3000 characters") | |
| prompt = prompt[:3000] | |
| return prompt | |
| def _get_race_traits(self, race: DnDRace, gender: Optional[str] = None) -> str: | |
| """Get ultra-detailed physical traits for race with gender-appropriate descriptions""" | |
| if gender == "female": | |
| traits = { | |
| DnDRace.HUMAN: "FEMININE WOMAN with expressive almond or round eyes showing depth and intelligence, natural human facial proportions with soft cheekbones, graceful jawline, delicate yet strong features, smooth skin with subtle weathering from adventure, diverse beauty with character lines from experience, warm approachable mouth with natural lips, well-defined brow expressing emotion, elegant neck, hair framing face naturally (flowing, braided, or practical), subtle beauty marks or freckles adding character, human versatility and adaptability shown in features, CLEARLY FEMALE facial structure", | |
| DnDRace.ELF: "FEMININE ELVEN WOMAN with long gracefully pointed ears swept back, sharp elegant angular features showing timeless beauty, almond-shaped luminous eyes with otherworldly gaze (colors like silver, gold, violet, or emerald), impossibly high sculpted cheekbones, delicate pointed chin, slender refined nose, soft bow-shaped lips, smooth porcelain-like skin with ageless quality, flowing silken hair (often silver, gold, auburn, or raven) cascading past shoulders or in elaborate braids, ethereal feminine grace, elegant arched eyebrows, slender graceful neck, mystical fey beauty radiating ancient wisdom, subtle glow to skin, CLEARLY FEMALE ELF with otherworldly elegance", | |
| DnDRace.DWARF: "FEMININE DWARVEN WOMAN with strong beautiful features, thick lustrous hair in elaborate braids adorned with metal rings and gemstones, fierce intelligent eyes showing determination and warmth, pronounced cheekbones with weathered sun-kissed complexion, broad but feminine jawline, strong nose with character, full lips often set in confident expression, weathered skin from forge work and mountain living showing texture and history, ornate beard jewelry or traditional face tattoos (cultural choice), powerful neck and shoulders showing strength, earthen beauty combining power and femininity, hair in rich colors (copper, bronze, black, auburn), skin with ruddy or bronze undertones, CLEARLY FEMALE DWARF with warrior-artisan beauty", | |
| DnDRace.HALFLING: "FEMININE HALFLING WOMAN with round friendly face showing youthful charm, large bright cheerful eyes (often hazel, brown, or green) sparkling with warmth and curiosity, naturally rosy cheeks with healthy glow, button nose, sweet gentle smile with dimples, soft rounded chin, curly or wavy tousled hair in warm colors (brown, auburn, golden, chestnut), smooth skin with youthful texture, delicate pointed ears (smaller than elves), laugh lines from joyful life, slightly plump cherubic features, cozy homely beauty radiating kindness, hair often adorned with flowers or ribbons, CLEARLY FEMALE HALFLING with welcoming maternal energy", | |
| DnDRace.DRAGONBORN: "FEMININE DRAGONBORN WOMAN with elegant draconic head featuring graceful reptilian features, sleek scaled skin covering face and neck with metallic or chromatic sheen matching dragon ancestry, fierce yet intelligent eyes with slit pupils glowing with inner fire, refined snout with noble profile, sharp teeth visible when speaking, pronounced facial ridges and horns sweeping back elegantly, scales with iridescent quality catching light beautifully, no hair but decorative horn jewelry or scale paint, expressive eye ridges conveying emotion, strong graceful neck with visible scale texture, regal bearing combining dragon majesty with feminine grace, face scales in beautiful patterns, CLEARLY FEMALE DRAGONBORN with powerful elegant beauty", | |
| DnDRace.GNOME: "FEMININE GNOME WOMAN with large expressive eyes full of endless curiosity and mischief (often in unusual colors like amber, turquoise, or violet), delicate upturned pointed nose, wild energetic hair in vibrant colors (pink, green, blue, purple, or natural tones) styled chaotically or with practical clips, mischievous playful grin showing cleverness, soft cheeks with youthful appearance, pointed chin, arched expressive eyebrows always moving with thought, smooth skin often smudged with grease or colored powder, tiny adorable ears, laugh lines around eyes from constant amusement, face showing brilliant intellect and creative energy, often wearing magnifying goggles or spectacles pushed up, CLEARLY FEMALE GNOME with inventive spirited beauty", | |
| DnDRace.HALF_ELF: "FEMININE HALF-ELF WOMAN with subtly pointed ears (shorter than full elves), beautiful blend of human warmth and elven ethereal grace, refined yet approachable facial features, high but not extreme cheekbones, expressive eyes with hint of otherworldly luminescence (various colors with subtle glow), elegant straight nose between human and elven, soft lips with gentle smile, smooth skin with light natural glow, flowing hair in diverse colors showing mixed heritage, graceful neck and shoulders, features combining best of both ancestries, slightly elongated facial proportions, eyebrows with elegant arch, skin texture between human and elven smoothness, versatile beauty adaptable to any culture, CLEARLY FEMALE HALF-ELF with hybrid enchanting beauty", | |
| DnDRace.HALF_ORC: "BEAUTIFUL FEMININE HALF-ORC WOMAN, CLEARLY FEMALE WARRIOR WOMAN, delicate small lower canines (petite feminine tusks barely visible), SOFT FEMININE FACIAL FEATURES with graceful cheekbones, FEMININE JAWLINE strong but elegant, fierce intelligent eyes showing both strength and compassion (colors like amber, green, brown), slightly prominent brow ridge adding character not masculinity, FEMININE NOSE with strong bridge, full lips with natural beauty, SMOOTH SKIN with greenish or grayish undertones (matching selected skin tone), LONG FLOWING HAIR in warrior braids or wild mane (black, brown, auburn, or dark green), graceful neck showing both power and femininity, war paint or tribal markings enhancing feminine beauty, ATHLETIC FEMININE PHYSIQUE radiating strength and grace, DISTINCTLY FEMALE ORC-BLOODED BEAUTY, warrior goddess appearance combining fierceness with unmistakable femininity, scarring or battle marks adding to beauty not diminishing it, WOMAN HALF-ORC FEMALE", | |
| DnDRace.TIEFLING: "FEMININE TIEFLING WOMAN with elegant curved horns sweeping back from forehead or temples (ram-like, gazelle-like, or demonic style) adorned with jewelry, long graceful pointed tail often visible, exotic glowing eyes without pupils (colors like molten gold, burning silver, fiery red, violet, or emerald), sharp elegant cheekbones, delicate pointed chin, soft lips with hint of sharp canine teeth visible when smiling, EXOTIC SKIN in vibrant colors (deep crimson, wine red, purple, blue-gray, pink-red, or obsidian) matching heritage, high arched eyebrows, hair in unusual colors (black, white, red, purple, blue) flowing or styled dramatically, smooth skin with slight infernal texture or patterns, elegant pointed ears or human-like ears, graceful neck, facial features combining demonic heritage with seductive beauty, CLEARLY FEMALE TIEFLING with dangerous alluring charm, infernal markings or tattoos enhancing features, OTHERWORLDLY FEMININE BEAUTY with edge of darkness", | |
| DnDRace.DROW: "FEMININE DROW ELF WOMAN with striking white or silver luminous hair (flowing, braided in elaborate warrior styles, or styled in matriarchal fashion), MIDNIGHT OBSIDIAN-BLACK SKIN or deep purple-black skin with smooth flawless texture, long gracefully pointed elven ears, GLOWING EYES in crimson red or violet purple with otherworldly luminescence piercing through darkness, razor-sharp elegant cheekbones, delicate pointed chin, straight refined nose, soft dark lips (deep purple or black), ageless ethereal features combining elven beauty with dark elegance, high arched eyebrows, slender graceful neck, hair-skin contrast creating striking visual, skin with subtle undertone (blue-black, purple-black, or pure obsidian), CLEARLY FEMALE DROW with dangerous seductive beauty, spider web jewelry or dark metal accessories, face radiating lethal grace and underground nobility, DARK ELF WOMAN with predatory elegance", | |
| } | |
| elif gender == "male": | |
| traits = { | |
| DnDRace.HUMAN: "MASCULINE MAN with strong expressive eyes showing determination and wisdom, natural human facial proportions with defined cheekbones, STRONG MASCULINE JAWLINE with character, broader features showing strength, skin with weathering from adventure and sun exposure, diverse rugged handsomeness with battle scars or life experience, firm mouth with natural lips, PROMINENT BROW showing resolve, POWERFUL NECK and shoulders, facial hair options (beard, stubble, or clean-shaven), hair in practical warrior styles or flowing heroic length, skin texture showing outdoor life, human adaptability in features, CLEARLY MALE facial structure with masculine presence", | |
| DnDRace.ELF: "MASCULINE ELVEN MAN with long gracefully pointed ears swept back, sharp noble angular features showing ancient bloodline, almond-shaped piercing eyes with otherworldly gaze (colors like silver, gold, violet, or emerald), high prominent cheekbones, STRONG ELEGANT JAWLINE, refined straight nose, firm lips, smooth ageless skin with ethereal quality, flowing silken hair (often silver, gold, auburn, or raven) worn long or in warrior braids, CLEARLY MALE ELF with masculine grace, high arched brows, LEAN POWERFUL NECK, ethereal masculine beauty combining strength with timeless elegance, skin with subtle glow, facial features showing both warrior prowess and ancient wisdom, no facial hair typical of elves, aristocratic bearing", | |
| DnDRace.DWARF: "MASCULINE DWARVEN MAN with MAGNIFICENT THICK BEARD in elaborate braids adorned with metal rings and gemstones (beard is point of pride), thick lustrous hair in warrior braids or bound styles, fierce determined eyes showing strength and honor, VERY PRONOUNCED STRONG CHEEKBONES, BROAD POWERFUL JAWLINE partially hidden by beard, STRONG NOSE with character, weathered ruddy complexion from forge work and mountain living showing texture and history, BROAD POWERFUL NECK and shoulders radiating strength, skin in ruddy or bronze tones, traditional clan tattoos or ritual scars, hair in rich colors (copper, bronze, black, auburn, iron-gray), CLEARLY MALE DWARF with warrior-craftsman presence, rugged masculine beauty, features carved by mountain stone and forge fire", | |
| DnDRace.HALFLING: "MASCULINE HALFLING MAN with round friendly face showing cheerful courage, large bright eyes (often hazel, brown, or green) sparkling with warmth and mischief, naturally rosy cheeks with healthy glow, button nose, wide genuine smile with laugh lines, STRONG ROUNDED CHIN, curly or wavy tousled hair in warm colors (brown, auburn, golden, chestnut), youthful skin texture showing outdoor life, delicate pointed ears (smaller than elves), crow's feet from constant smiling, jovial features radiating hospitality, possible sideburns or chin beard, hair slightly wild from adventure, CLEARLY MALE HALFLING with welcoming paternal energy and hidden bravery", | |
| DnDRace.DRAGONBORN: "MASCULINE DRAGONBORN MAN with powerful draconic head featuring STRONG reptilian features, THICK scaled skin covering face and neck with metallic or chromatic sheen matching dragon ancestry, fierce commanding eyes with slit pupils burning with inner fire, PRONOUNCED STRONG SNOUT with noble warrior profile, sharp dangerous teeth visible when speaking, PROMINENT FACIAL RIDGES and LARGE HORNS sweeping back powerfully, scales with armored quality showing battle-worn texture, no hair but war trophies or horn decorations, STRONG BROW RIDGE conveying intensity, POWERFUL THICK NECK with pronounced scale texture, commanding presence radiating dragon majesty and masculine power, face scales in intimidating patterns, CLEARLY MALE DRAGONBORN with fierce noble strength", | |
| DnDRace.GNOME: "MASCULINE GNOME MAN with large expressive eyes full of endless curiosity and clever mischief (often in unusual colors like amber, turquoise, or violet), pointed upturned nose, wild energetic hair or beard in vibrant colors (pink, green, blue, purple, or natural tones) styled chaotically, mischievous clever grin showing wit and intelligence, STRONG POINTED CHIN, bushy expressive eyebrows always moving with thought, weathered skin often smudged with grease or colored powder from experiments, tiny ears, laugh lines and wrinkles from constant invention, face showing brilliant intellect and manic creativity, often wearing magnifying goggles or spectacles, possible elaborate mustache or beard, CLEARLY MALE GNOME with inventive eccentric charm", | |
| DnDRace.HALF_ELF: "MASCULINE HALF-ELF MAN with subtly pointed ears (shorter than full elves), handsome blend of human strength and elven grace, refined yet approachable facial features, STRONG DEFINED CHEEKBONES, FIRM MASCULINE JAWLINE showing mixed heritage, piercing eyes with hint of otherworldly luminescence (various colors with subtle glow), straight nose between human and elven proportions, firm lips with confident expression, skin with light natural glow, hair in diverse colors showing mixed heritage (worn in various styles), STRONG NECK and shoulders, features combining human vitality with elven elegance, slightly elongated facial proportions, strong brow, skin texture between human ruggedness and elven smoothness, adaptable handsome features, CLEARLY MALE HALF-ELF with hybrid noble bearing", | |
| DnDRace.HALF_ORC: "MASCULINE HALF-ORC MAN, CLEARLY MALE WARRIOR, LARGE PROMINENT LOWER CANINES (impressive tusks) protruding from mouth, VERY STRONG PROMINENT BROW RIDGE, BROAD POWERFUL MASCULINE FEATURES, SQUARE MASSIVE JAWLINE showing orcish strength, fierce intense eyes showing battle fury and determination (colors like amber, red, green, brown), WIDE FLAT NOSE with strong bridge, scarred weathered skin showing countless battles, THICK SKIN with greenish or grayish undertones (matching selected skin tone), hair in warrior styles (topknot, war braids, or shaved patterns) in dark colors (black, brown, dark green), MASSIVE POWERFUL NECK and shoulders radiating raw strength, war paint or tribal scars marking face, battle-scarred features adding to intimidating presence, CLEARLY MALE HALF-ORC with fearsome warrior appearance, rugged brutal handsomeness, MAN HALF-ORC MALE", | |
| DnDRace.TIEFLING: "MASCULINE TIEFLING MAN with prominent curved horns sweeping from forehead or temples (ram-like, demonic, or dragon-like style) possibly adorned with rings, long pointed tail often visible, exotic glowing eyes without pupils (colors like molten gold, burning silver, hellfire red, violet, or emerald), STRONG SHARP CHEEKBONES, DEFINED MASCULINE JAWLINE, firm lips with visible sharp canine teeth when grinning, EXOTIC SKIN in vibrant infernal colors (deep crimson, wine red, purple, blue-gray, pink-red, or obsidian) matching heritage, STRONG BROW with commanding presence, hair in unusual colors (black, white, red, purple, blue) worn in dramatic styles, skin with slight infernal texture or glowing patterns, pointed ears or human-like ears, POWERFUL NECK, facial features combining demonic heritage with dark handsomeness, CLEARLY MALE TIEFLING with dangerous charismatic presence, infernal markings or ritual scars, OTHERWORLDLY MASCULINE BEAUTY with menacing edge", | |
| DnDRace.DROW: "MASCULINE DROW ELF MAN with striking white or silver luminous hair (long warrior style, braided in military fashion, or swept back dramatically), MIDNIGHT OBSIDIAN-BLACK SKIN or deep purple-black skin with smooth flawless texture, long gracefully pointed elven ears, GLOWING EYES in crimson red or violet purple with otherworldly intensity piercing darkness, RAZOR-SHARP CHEEKBONES, STRONG MASCULINE JAWLINE, straight refined nose, firm dark lips (deep purple or black), ageless features combining elven beauty with dark masculine power, STRONG ARCHED BROWS, LEAN POWERFUL NECK, hair-skin contrast creating striking warrior appearance, skin with subtle undertone (blue-black, purple-black, or pure obsidian), CLEARLY MALE DROW with dangerous lethal handsomeness, spider web scars or dark metal piercings, face radiating predatory grace and underground warrior nobility, DARK ELF MAN with deadly elegant presence, no facial hair typical of elves", | |
| } | |
| else: | |
| # Androgynous/non-binary descriptions | |
| traits = { | |
| DnDRace.HUMAN: "ANDROGYNOUS HUMAN with expressive eyes showing depth and soulful intelligence, balanced facial proportions combining strength and grace, elegantly defined cheekbones neither overly sharp nor soft, refined jawline with harmonious structure, captivating features transcending gender norms, smooth skin with subtle weathering from adventure showing life experience, balanced mouth with expressive lips, thoughtfully arched brow, graceful neck, hair styled in ways celebrating fluidity (undercut, flowing, asymmetric, or artistic), natural beauty radiating confidence and self-expression, features showing human diversity and non-conformity, face with androgynous allure and mysterious charm", | |
| DnDRace.ELF: "ANDROGYNOUS ELF with long gracefully pointed ears swept back elegantly, sharp perfectly balanced angular features transcending gender, almond-shaped luminous eyes with captivating otherworldly gaze (colors like silver, gold, violet, or emerald), impossibly high sculpted cheekbones creating divine symmetry, refined jawline neither distinctly masculine nor feminine, delicate straight nose, soft balanced lips, smooth porcelain-like ageless skin with ethereal quality, flowing silken hair in mystical colors (silver, gold, auburn, raven, or pastel tones) styled in fluid artistic ways, graceful slender neck, features radiating fey beauty beyond mortal gender concepts, face showing ancient wisdom and timeless allure, subtle luminescence to skin, androgynous elven perfection with magnetic presence", | |
| DnDRace.DWARF: "ANDROGYNOUS DWARF with thick lustrous hair in artistic braids adorned with unique gemstones and personal metalwork, fierce intelligent eyes showing determination and creative spirit, pronounced balanced cheekbones, weathered complexion from forge work and mountain living with beautiful texture, strong yet refined jawline, noble nose with character, expressive lips, neck and shoulders showing both strength and grace, skin in warm tones (ruddy, bronze, or deep brown), traditional face tattoos or unique piercings celebrating identity, hair in unconventional colors or natural tones, features combining dwarven strength with fluid beauty, face showing artisan soul and warrior heart, androgynous dwarven presence breaking traditional molds", | |
| DnDRace.HALFLING: "ANDROGYNOUS HALFLING with round friendly face radiating warmth and acceptance, large bright expressive eyes (often hazel, brown, or green) sparkling with joy and wisdom, naturally rosy cheeks with healthy glow, button nose, gentle welcoming smile with dimples, soft balanced chin, curly or wavy hair in warm or creative colors styled in personal expression, smooth youthful skin, delicate pointed ears (smaller than elves), features combining childlike wonder with ageless understanding, face showing both nurturing spirit and adventurous soul, hair adorned with meaningful tokens or natural elements, androgynous halfling beauty radiating community and individuality", | |
| DnDRace.DRAGONBORN: "ANDROGYNOUS DRAGONBORN with elegant draconic head featuring balanced reptilian features, sleek scaled skin covering face and neck with beautiful metallic or chromatic sheen, fierce intelligent eyes with slit pupils showing wisdom and power, refined snout with noble profile, sharp teeth visible when speaking, facial ridges and horns swept back in artistic arrangement, scales with mesmerizing iridescent patterns, no hair but decorative horn jewelry or ritual scale painting, expressive eye ridges conveying deep emotion, graceful powerful neck with scale texture, presence combining dragon majesty with fluid grace, face scales in unique beautiful patterns, androgynous dragonborn radiating ancient power beyond gender", | |
| DnDRace.GNOME: "ANDROGYNOUS GNOME with large expressive eyes full of boundless curiosity and creative wonder (often in unique colors like amber, turquoise, or violet), delicate pointed nose, wild artistic hair in vibrant unconventional colors (rainbow, pastel gradients, or natural tones) styled in expressive ways, clever mischievous smile showing brilliant wit, balanced chin, constantly moving expressive eyebrows, skin with inventor's marks (oil smudges, colorful stains, creative tattoos), small ears with multiple piercings or decorations, face radiating innovation and free spirit, often wearing unique goggles or handcrafted accessories, features showing genius that defies categorization, androgynous gnome charm combining intellect with whimsy", | |
| DnDRace.HALF_ELF: "ANDROGYNOUS HALF-ELF with subtly pointed ears (shorter than full elves), captivating blend of human warmth and elven ethereal beauty, refined yet approachable features transcending binary presentation, balanced elegant cheekbones, graceful jawline neither distinctly masculine nor feminine, mesmerizing eyes with hint of otherworldly luminescence (various colors with gentle glow), straight nose combining human and elven characteristics, expressive lips with enigmatic smile, smooth skin with subtle natural glow, flowing hair in diverse colors showing mixed heritage (styled in personal artistic expression), elegant neck, features perfectly balancing both ancestries, slightly elongated proportions with harmonious beauty, skin texture between human character and elven smoothness, versatile beauty that belongs to all worlds and none, androgynous half-elf with magnetic hybrid allure", | |
| DnDRace.HALF_ORC: "ANDROGYNOUS HALF-ORC with moderate lower canines (tusks of medium prominence), powerful yet graceful build transcending traditional gender, balanced prominent brow ridge adding character without overpowering, strong features combining orcish heritage with fluid beauty, fierce intelligent eyes showing both warrior spirit and deep wisdom (colors like amber, green, hazel), balanced nose with strong bridge, expressive lips, SKIN with greenish or grayish undertones (matching selected skin tone) with beautiful texture, hair in warrior styles (braids, shaved patterns, or flowing) in unconventional colors, graceful powerful neck, war paint or artistic tattoos celebrating identity and clan, features showing both battle prowess and spiritual depth, scars telling stories of survival and growth, androgynous half-orc beauty breaking warrior stereotypes, presence radiating strength without conforming to binary expectations, face combining orcish power with transcendent grace", | |
| DnDRace.TIEFLING: "ANDROGYNOUS TIEFLING with elegant artistic horns sweeping from forehead (ram-like, gazelle-like, or unique style) adorned with meaningful jewelry, long expressive pointed tail often visible, exotic glowing eyes without pupils (colors like molten gold, burning silver, mystic violet, or ethereal teal), balanced sharp cheekbones creating striking symmetry, refined jawline neither distinctly masculine nor feminine, expressive lips with hint of sharp canine teeth, EXOTIC SKIN in vibrant mystical colors (deep crimson, royal purple, midnight blue, pink-red, or silver-gray) matching infernal heritage, elegantly arched brows, hair in supernatural colors (starlight white, shadow black, flame red, void purple, or gradient tones) styled in personal artistic expression, smooth skin with subtle infernal patterns or glowing sigils, pointed ears or human-like ears, graceful neck, features combining demonic heritage with transcendent beauty beyond mortal categories, androgynous tiefling radiating otherworldly allure and dangerous mystique, infernal markings celebrating unique identity, face showing both darkness and light in perfect balance", | |
| DnDRace.DROW: "ANDROGYNOUS DROW ELF with striking luminous white or silver hair (styled in unique artistic ways, warrior braids, or flowing freely), MIDNIGHT OBSIDIAN-BLACK SKIN or deep purple-black skin with flawless smooth texture, long gracefully pointed elven ears, MESMERIZING GLOWING EYES in crimson red or violet purple with captivating otherworldly luminescence, razor-sharp perfectly balanced cheekbones, refined jawline transcending gender, straight elegant nose, expressive dark lips (deep purple or black), ageless features combining elven beauty with dark androgynous allure, gracefully arched brows, elegant neck, hair-skin contrast creating stunning visual impact, skin with subtle undertone (blue-black, purple-black, or pure obsidian), androgynous drow radiating lethal grace and underground nobility beyond binary concepts, spider web jewelry or unique dark metal adornments celebrating identity, face showing predatory elegance and wisdom of the Underdark, dark elf presence with transcendent dangerous beauty, features commanding respect through power not gender", | |
| } | |
| return traits.get(race, "distinctive fantasy features") | |
| def _get_class_elements(self, character_class: DnDClass) -> str: | |
| """Get rich equipment/clothing descriptions for class""" | |
| elements = { | |
| DnDClass.FIGHTER: "wearing ornate heavy plate armor with battle scars, wielding a masterwork longsword, shield with heraldic emblem, warrior's stance, battle-ready posture", | |
| DnDClass.WIZARD: "wearing flowing arcane robes covered in mystical runes and symbols, holding an ancient gnarled staff with glowing crystal, spellbook at belt, magical energy crackling around hands, scholarly yet powerful presence", | |
| DnDClass.ROGUE: "wearing form-fitting leather armor in dark colors, multiple daggers and lockpicks visible, hooded cloak, agile stance, tools of the trade on belt, mysterious air, shadows clinging to figure", | |
| DnDClass.CLERIC: "wearing holy vestments and blessed armor adorned with sacred symbols, divine light emanating softly, holy symbol prominently displayed, mace or shield with deity's iconography, righteous bearing", | |
| DnDClass.RANGER: "wearing weathered leather armor decorated with natural elements, longbow and quiver of arrows, hunting knives, camouflage cloak, survival gear, at one with nature, tracker's keen gaze", | |
| DnDClass.PALADIN: "wearing radiant plate armor gleaming with holy light, sacred sword with blessed runes, shield bearing oath symbol, divine aura surrounding them, righteous and noble bearing, armor reflecting inner conviction", | |
| DnDClass.BARD: "wearing flamboyant colorful clothes with artistic flair, ornate musical instrument (lute or flute), decorative rapier, charismatic pose, theatrical presence, jewelry and fine accessories", | |
| DnDClass.BARBARIAN: "wearing minimal tribal armor showing powerful muscles, massive greataxe or battle weapon, war paint or tribal tattoos, furs and bone decorations, primal fierce energy, untamed strength visible", | |
| DnDClass.DRUID: "wearing natural robes made from leaves and vines, wooden staff carved with nature symbols, animal companion nearby or nature motifs, flowers or moss in hair, earth-toned garments, wild natural aesthetic", | |
| DnDClass.MONK: "wearing simple martial arts robes or wraps, disciplined stance showing combat training, meditation beads or prayer rope, barefoot or simple sandals, focused ki energy visible, balanced pose, inner peace and outer strength", | |
| DnDClass.SORCERER: "wearing elegant robes with innate magical patterns, raw arcane energy visibly crackling and swirling around body, no spellbook (power from within), draconic or wild magic aesthetic, chaotic magical aura", | |
| DnDClass.WARLOCK: "wearing dark mysterious robes with eldritch patterns, otherworldly patron's influence visible in design, arcane focus or pact weapon, eerie magical glow, forbidden knowledge in eyes, shadows and mysterious energies swirling", | |
| } | |
| return elements.get(character_class, "adventurer's gear and equipment, ready for questing") | |
| def _get_alignment_mood(self, alignment: str) -> str: | |
| """Get rich mood/expression and visual atmosphere based on alignment""" | |
| moods = { | |
| "Lawful Good": "noble righteous expression radiating honor and justice, heroic stance, warm golden lighting highlighting features, aura of divine protection, inspiring presence, defender of the weak demeanor, lawful symbols glowing softly", | |
| "Neutral Good": "kind compassionate expression with gentle warm smile, caring eyes full of empathy, soft natural lighting, helping hand gesture, approachable and trustworthy aura, peaceful yet determined energy", | |
| "Chaotic Good": "rebellious freedom-fighter expression with fierce determination, defiant heroic gaze, dynamic wild hair, passionate energy, breaking chains imagery, fighting for justice with unconventional methods, vibrant chaotic-good energy swirling", | |
| "Lawful Neutral": "disciplined stoic expression showing unwavering resolve, stern neutral features, cold calculated gaze, orderly balanced lighting, perfect symmetry, following code without emotion, judge-like presence, scales of balance nearby", | |
| "True Neutral": "perfectly balanced contemplative expression, serene eyes seeing all sides, neutral gray and earth-tone lighting, harmonious natural pose, druid-like connection to balance, neither light nor dark dominating, peaceful equilibrium", | |
| "Chaotic Neutral": "unpredictable wild expression with mischievous glint, chaotic energy in eyes, asymmetrical dynamic pose, swirling random elements, free spirit aesthetic, untamed unpredictable aura, living for freedom and self-interest", | |
| "Lawful Evil": "coldly calculating tyrant expression, cruel methodical eyes, harsh angular shadows, oppressive dark lighting from above, commanding authoritative pose, iron-fisted control visible, merciless lawful darkness, chains and order motifs", | |
| "Neutral Evil": "cruel cunning expression with sinister smirk, selfish malevolent eyes, shadowy ominous lighting, betrayer's calculating gaze, pure self-interest and cruelty, dark subtle shadows, dangerous predatory presence", | |
| "Chaotic Evil": "menacing destructive expression with maniacal energy, eyes burning with malevolent chaos, violent chaotic shadows swirling, destructive aura crackling, reveling in cruelty and disorder, demonic wild energy, nightmare-inducing presence, burning destruction in background", | |
| } | |
| return moods.get(alignment, "determined focused expression") | |
| def _generate_image_openai( | |
| self, | |
| prompt: str, | |
| quality: str = "standard", | |
| size: str = "1024x1024" | |
| ) -> Optional[bytes]: | |
| """ | |
| Call DALL-E 3 API to generate image | |
| Args: | |
| prompt: Image generation prompt | |
| quality: "standard" or "hd" | |
| size: Image dimensions | |
| Returns: | |
| Image bytes or None | |
| """ | |
| client = OpenAI(api_key=self.openai_api_key) | |
| try: | |
| response = client.images.generate( | |
| model="dall-e-3", | |
| prompt=prompt, | |
| size=size, | |
| quality=quality, | |
| n=1, | |
| ) | |
| # Get image URL | |
| image_url = response.data[0].url | |
| # Download image | |
| image_response = requests.get(image_url) | |
| if image_response.status_code == 200: | |
| return image_response.content | |
| return None | |
| except openai.BadRequestError as e: | |
| error_str = str(e) | |
| print(f"DALL-E API BadRequest error: {error_str}") | |
| # Check for billing issues | |
| if "billing_hard_limit" in error_str or "billing hard limit" in error_str.lower(): | |
| raise ValueError( | |
| "π³ Billing limit reached: Your OpenAI account has reached its billing limit.\n\n" | |
| "To fix this:\n" | |
| "1. Go to https://platform.openai.com/account/billing\n" | |
| "2. Add credits or update your payment method\n" | |
| "3. Check/increase your usage limits\n\n" | |
| "Note: DALL-E 3 costs $0.040 per standard image or $0.080 per HD image." | |
| ) | |
| elif "content_policy" in error_str.lower() or "safety" in error_str.lower(): | |
| raise ValueError( | |
| "Content policy violation: The generated prompt was flagged by OpenAI's safety system. " | |
| "Try using a different art style or character description." | |
| ) | |
| else: | |
| raise ValueError(f"DALL-E API Error: {error_str}") | |
| except openai.AuthenticationError as e: | |
| print(f"DALL-E Authentication error: {e}") | |
| raise ValueError( | |
| "π Authentication error: Your OpenAI API key is invalid or expired.\n" | |
| "Please check that OPENAI_API_KEY in your .env file is correct." | |
| ) | |
| except openai.RateLimitError as e: | |
| print(f"DALL-E Rate limit error: {e}") | |
| raise ValueError( | |
| "β° Rate limit exceeded: Too many requests to OpenAI API.\n" | |
| "Please wait a moment and try again." | |
| ) | |
| except Exception as e: | |
| error_str = str(e) | |
| print(f"DALL-E API error: {error_str}") | |
| # Catch-all with helpful hint | |
| if "billing" in error_str.lower() or "quota" in error_str.lower(): | |
| raise ValueError( | |
| "π³ Billing/Quota error: " + error_str + "\n\n" | |
| "Check your OpenAI account at https://platform.openai.com/account/billing" | |
| ) | |
| else: | |
| raise ValueError(f"DALL-E API Error: {error_str}") | |
| def _generate_image_huggingface(self, prompt: str) -> Optional[bytes]: | |
| """ | |
| Call HuggingFace Inference API to generate image using Stable Diffusion | |
| Args: | |
| prompt: Image generation prompt | |
| Returns: | |
| Image bytes or None | |
| """ | |
| from huggingface_hub import InferenceClient | |
| client = InferenceClient(token=self.hf_api_key) | |
| try: | |
| # Generate image using Stable Diffusion XL | |
| image = client.text_to_image( | |
| prompt=prompt, | |
| model=self.hf_model | |
| ) | |
| # Convert PIL Image to bytes | |
| from PIL import Image | |
| import io | |
| if isinstance(image, Image.Image): | |
| img_byte_arr = io.BytesIO() | |
| image.save(img_byte_arr, format='PNG') | |
| img_byte_arr.seek(0) | |
| return img_byte_arr.read() | |
| return None | |
| except Exception as e: | |
| error_str = str(e) | |
| print(f"HuggingFace API error: {error_str}") | |
| # Provide helpful error messages | |
| if "401" in error_str or "authentication" in error_str.lower(): | |
| raise ValueError( | |
| "π HuggingFace Authentication error: Your HF API key is invalid.\n" | |
| "Get a free key at https://huggingface.co/settings/tokens" | |
| ) | |
| elif "503" in error_str or "model" in error_str.lower() and "loading" in error_str.lower(): | |
| raise ValueError( | |
| "β° Model loading: The HuggingFace model is starting up.\n" | |
| "Please wait 30-60 seconds and try again." | |
| ) | |
| elif "rate" in error_str.lower() or "quota" in error_str.lower(): | |
| raise ValueError( | |
| "β° Rate limit: HuggingFace API rate limit reached.\n" | |
| "Free tier: ~100 requests/day. Upgrade at https://huggingface.co/pricing" | |
| ) | |
| else: | |
| raise ValueError(f"HuggingFace API Error: {error_str}") | |
| def get_portrait_path(self, character_id: str) -> Optional[str]: | |
| """Get saved portrait path for character""" | |
| filename = f"{character_id}_portrait.png" | |
| file_path = self.output_dir / filename | |
| if file_path.exists(): | |
| return str(file_path) | |
| # Check for custom portrait | |
| filename = f"{character_id}_custom_portrait.png" | |
| file_path = self.output_dir / filename | |
| if file_path.exists(): | |
| return str(file_path) | |
| return None | |
| # Global instance | |
| _image_generator: Optional[ImageGenerator] = None | |
| def get_image_generator() -> ImageGenerator: | |
| """Get or create global image generator instance""" | |
| global _image_generator | |
| if _image_generator is None: | |
| _image_generator = ImageGenerator() | |
| return _image_generator | |