""" Application Settings and Configuration ======================================= Centralized configuration management for Nano Banana Streamlit. All environment variables, paths, and constants defined here. """ import os from pathlib import Path from typing import Optional class Settings: """ Application-wide settings and configuration. This class uses class methods and properties to provide a simple interface for accessing configuration values. """ # ========================================================================= # PROJECT PATHS # ========================================================================= # Root directory of the project PROJECT_ROOT = Path(__file__).parent.parent # Output directory for generated images OUTPUT_DIR = PROJECT_ROOT / "outputs" # Ensure output directory exists OUTPUT_DIR.mkdir(exist_ok=True) # Subdirectories for different generation types CHARACTER_SHEETS_DIR = OUTPUT_DIR / "character_sheets" WARDROBE_CHANGES_DIR = OUTPUT_DIR / "wardrobe_changes" COMPOSITIONS_DIR = OUTPUT_DIR / "compositions" STANDARD_DIR = OUTPUT_DIR / "standard" # Create all subdirectories for directory in [CHARACTER_SHEETS_DIR, WARDROBE_CHANGES_DIR, COMPOSITIONS_DIR, STANDARD_DIR]: directory.mkdir(exist_ok=True) # Log file LOG_FILE = OUTPUT_DIR / "generation.log" # ========================================================================= # API KEYS AND CREDENTIALS # ========================================================================= @classmethod def get_gemini_api_key(cls) -> Optional[str]: """ Get Gemini API key from environment variable. Returns: API key string if set, None otherwise """ return os.environ.get("GEMINI_API_KEY") # ========================================================================= # BACKEND CONFIGURATION # ========================================================================= # OmniGen2 server URL OMNIGEN2_BASE_URL = "http://127.0.0.1:9002" # ComfyUI server URL COMFYUI_BASE_URL = "http://127.0.0.1:8188" # Backend timeout (seconds) # Set to None for local backends (ComfyUI) - no timeout needed, monitor logs instead # For network/API backends (Gemini), keep a reasonable timeout BACKEND_TIMEOUT = None # No timeout for local models (was 600s / 10 min) # ========================================================================= # GENERATION PARAMETERS # ========================================================================= # Available aspect ratios ASPECT_RATIOS = { "1:1 (1024x1024)": "1:1", "16:9 (1344x768)": "16:9", "9:16 (768x1344)": "9:16", "3:2 (1248x832)": "3:2", "2:3 (832x1248)": "2:3", "3:4 (864x1184)": "3:4", # Character portraits (Gemini actual output) "4:3 (1344x1008)": "4:3", "4:5 (1024x1280)": "4:5", "5:4 (1280x1024)": "5:4", "21:9 (1536x640)": "21:9", } # Default generation parameters DEFAULT_ASPECT_RATIO = "16:9 (1344x768)" DEFAULT_TEMPERATURE = 0.4 MIN_TEMPERATURE = 0.0 MAX_TEMPERATURE = 1.0 TEMPERATURE_STEP = 0.05 # ========================================================================= # CHARACTER FORGE SETTINGS # ========================================================================= # Aspect ratios for character sheet views PORTRAIT_ASPECT_RATIO = "3:4" # For face portraits (864x1184) BODY_ASPECT_RATIO = "9:16" # For full body shots (768x1344) # Generation temperatures for each stage PORTRAIT_TEMPERATURE = 0.35 # Lower for consistency BODY_TEMPERATURE = 0.5 # Slightly higher for variety # Default negative prompts for ComfyUI qwen workflow # These help steer generation away from common errors DEFAULT_NEGATIVE_PROMPTS = { "stage_0a": "blurry, low quality, distorted, deformed, disfigured, bad anatomy, extra limbs, missing limbs, multiple people", "stage_0b": "different person, wrong face, altered features, different hair color, different eye color, low quality, blurry", "stage_1": "side view, profile, back view, different person, different face, altered facial features, different clothing, wrong outfit, blurry, low quality", "stage_2": "front view, facing camera, back view, three-quarter view, different person, different face, altered features, different clothing, wrong outfit, blurry, low quality", "stage_3": "front view, facing camera, back view, rear view, different person, different face, different body, altered proportions, different clothing, costume change, nude, undressed, blurry, low quality, cut off, cropped, incomplete body", "stage_4": "front view, facing camera, side view, profile view, face visible, different person, different body, different clothing, costume change, nude, undressed, blurry, low quality, cut off, cropped, incomplete body" } # Composition settings CHARACTER_SHEET_SPACING = 20 # Pixels between rows CHARACTER_SHEET_BACKGROUND = "#2C2C2C" # Dark gray # Retry logic MAX_RETRIES = 3 RETRY_BASE_DELAY = 2 # Seconds (exponential backoff) RATE_LIMIT_DELAY_MIN = 2.0 # Seconds RATE_LIMIT_DELAY_MAX = 3.0 # Seconds # ========================================================================= # LOGGING CONFIGURATION # ========================================================================= LOG_LEVEL = "INFO" LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" # Rotating file handler settings LOG_MAX_BYTES = 10 * 1024 * 1024 # 10 MB LOG_BACKUP_COUNT = 5 # Keep 5 backup files # ========================================================================= # UI CONFIGURATION # ========================================================================= # Maximum image upload size (MB) MAX_IMAGE_UPLOAD_SIZE = 20 # MB # Image display size PREVIEW_IMAGE_WIDTH = 512 # Pixels # History display MAX_HISTORY_ITEMS = 20 # ========================================================================= # COMPOSITION ASSISTANT SETTINGS # ========================================================================= # Image type options IMAGE_TYPES = [ "Subject/Character", "Background/Environment", "Style Reference", "Product", "Texture", "Not Used" ] # Shot type options SHOT_TYPES = [ "close-up shot", "medium shot", "full body shot", "wide shot", "extreme close-up", "establishing shot" ] # Camera angle options CAMERA_ANGLES = [ "eye-level perspective", "low-angle perspective", "high-angle perspective", "bird's-eye view", "Dutch angle (tilted)", "over-the-shoulder" ] # Lighting options LIGHTING_OPTIONS = [ "Auto (match images)", "natural daylight", "soft studio lighting", "dramatic side lighting", "golden hour", "blue hour", "moody low-key", "high-key bright", "rim lighting" ] # ========================================================================= # BACKEND TYPE ENUMERATION # ========================================================================= BACKEND_GEMINI = "Gemini API (Cloud)" BACKEND_OMNIGEN2 = "OmniGen2 (Local)" BACKEND_COMFYUI = "ComfyUI (Local)" BACKEND_FLUX_KREA = "FLUX Krea (Local)" # For initial portrait generation BACKEND_FLUX_KONTEXT = "FLUX Kontext (Local)" # For perspective transformations AVAILABLE_BACKENDS = [ BACKEND_GEMINI, BACKEND_OMNIGEN2, BACKEND_COMFYUI, BACKEND_FLUX_KREA, BACKEND_FLUX_KONTEXT ] # ========================================================================= # HELPER METHODS # ========================================================================= @classmethod def get_aspect_ratio_value(cls, display_name: str) -> str: """ Convert display name to aspect ratio value. Args: display_name: Display name like "16:9 (1344x768)" Returns: Aspect ratio value like "16:9" """ return cls.ASPECT_RATIOS.get(display_name, "1:1") @classmethod def is_gemini_configured(cls) -> bool: """Check if Gemini API is configured (API key set).""" return cls.get_gemini_api_key() is not None @classmethod def validate_temperature(cls, temperature: float) -> float: """ Validate and clamp temperature to valid range. Args: temperature: Temperature value to validate Returns: Validated temperature within [MIN_TEMPERATURE, MAX_TEMPERATURE] """ return max(cls.MIN_TEMPERATURE, min(cls.MAX_TEMPERATURE, temperature)) # Make settings instance available for import settings = Settings()