File size: 9,236 Bytes
5b6e956
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
"""
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()