lulavc commited on
Commit
973676f
·
verified ·
1 Parent(s): b6ec4f0

FIX: Import spaces before torch to prevent CUDA initialization error

Browse files
Files changed (1) hide show
  1. app.py +695 -1035
app.py CHANGED
@@ -14,1217 +14,877 @@ Author: AI Agent Framework Specialist
14
  Version: 2.0.0 Production
15
  """
16
 
17
- from spaces import GPU
 
 
 
 
 
 
 
18
  import os
19
  import sys
20
  import time
21
  import asyncio
22
- import hashlib
23
  import logging
24
- import traceback
25
- import warnings
26
- from typing import Optional, Tuple, Dict, Any, Union, List
27
- from contextlib import asynccontextmanager
 
 
28
  from dataclasses import dataclass
29
- from enum import Enum
 
30
 
31
- # Third-party imports
32
- import gradio as gr
33
  import torch
34
  import numpy as np
35
  from PIL import Image
36
- import psutil
37
- from functools import lru_cache
38
- from datetime import datetime, timedelta
39
-
40
- # Diffusers and model imports
41
- from diffusers import DiffusionPipeline, StableDiffusionImg2ImgPipeline
42
- from diffusers.utils import logging as diffusers_logging
43
- from spaces import GPU
44
 
45
- # Suppress noisy warnings
46
- warnings.filterwarnings("ignore", category=UserWarning)
47
- diffusers_logging.set_verbosity_error()
 
 
 
 
 
48
 
49
- # Configure logging early
50
  logging.basicConfig(
51
  level=logging.INFO,
52
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
53
  handlers=[
54
- logging.StreamHandler(sys.stdout),
55
- logging.FileHandler('z_image_turbo.log', mode='a')
56
  ]
57
  )
58
  logger = logging.getLogger(__name__)
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- class ErrorCode(Enum):
62
- """Enumeration of application error codes"""
63
- SUCCESS = 0
64
- MODEL_LOAD_ERROR = 1001
65
- GENERATION_ERROR = 1002
66
- TRANSFORM_ERROR = 1003
67
- INVALID_INPUT = 2001
68
- RESOURCE_ERROR = 3001
69
- NETWORK_ERROR = 4001
70
- CACHE_ERROR = 5001
71
- UNKNOWN_ERROR = 9999
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  @dataclass
75
  class GenerationResult:
76
- """Data class for generation results"""
77
  success: bool
78
  image: Optional[Image.Image] = None
79
- seed: int = 0
80
- message: str = ""
81
- error_code: ErrorCode = ErrorCode.SUCCESS
82
- generation_time: float = 0.0
83
- metadata: Optional[Dict[str, Any]] = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  class SystemMonitor:
87
- """Monitor system resources and performance"""
88
 
89
  def __init__(self):
90
- self.start_time = time.time()
91
  self.generation_count = 0
 
92
  self.error_count = 0
93
- self.cache_hits = 0
94
- self.cache_misses = 0
 
 
 
 
 
 
 
 
 
 
95
 
96
- def get_system_info(self) -> Dict[str, Any]:
97
- """Get current system resource information"""
98
- try:
99
- memory = psutil.virtual_memory()
100
- gpu_memory = self._get_gpu_memory()
 
101
 
102
- return {
103
- "uptime_seconds": time.time() - self.start_time,
104
- "memory_used_gb": memory.used / (1024**3),
105
- "memory_percent": memory.percent,
106
- "gpu_memory_used_gb": gpu_memory,
107
  "cpu_percent": psutil.cpu_percent(interval=0.1),
108
- "active_generations": self.generation_count,
 
 
 
 
 
 
 
 
 
 
 
109
  "error_count": self.error_count,
110
- "cache_hit_rate": self.get_cache_hit_rate()
 
 
 
 
111
  }
112
- except Exception as e:
113
- logger.error(f"Error getting system info: {e}")
114
- return {}
115
 
116
- def _get_gpu_memory(self) -> float:
117
- """Get GPU memory usage in GB"""
118
- try:
119
  if torch.cuda.is_available():
120
- return torch.cuda.memory_allocated() / (1024**3)
121
- except:
122
- pass
123
- return 0.0
124
-
125
- def get_cache_hit_rate(self) -> float:
126
- """Calculate cache hit rate percentage"""
127
- total = self.cache_hits + self.cache_misses
128
- return (self.cache_hits / total * 100) if total > 0 else 0.0
129
-
130
- def record_generation(self, success: bool):
131
- """Record a generation attempt"""
132
- self.generation_count += 1
133
- if not success:
134
- self.error_count += 1
135
-
136
- def record_cache_hit(self):
137
- """Record a cache hit"""
138
- self.cache_hits += 1
139
-
140
- def record_cache_miss(self):
141
- """Record a cache miss"""
142
- self.cache_misses += 1
143
 
 
144
 
145
  class ModelManager:
146
- """Manages model loading, optimization, and resource handling"""
147
 
148
  def __init__(self):
149
- self.model_name = "Tongyi-MAI/Z-Image-Turbo"
150
- self.pipe_t2i = None
151
- self.pipe_i2i = None
152
- self.is_loaded = False
153
  self.optimizations_applied = []
154
- self._load_lock = asyncio.Lock()
155
-
156
- async def load_models(self) -> bool:
157
- """Load models with proper error handling and fallbacks"""
158
- async with self._load_lock:
159
- if self.is_loaded:
160
- return True
161
-
162
- try:
163
- logger.info(f"Loading model: {self.model_name}")
164
- start_time = time.time()
165
-
166
- # Determine optimal dtype based on hardware
167
- dtype = self._get_optimal_dtype()
168
-
169
- # Load base pipeline
170
- self.pipe_t2i = DiffusionPipeline.from_pretrained(
171
- self.model_name,
172
- torch_dtype=dtype,
173
- use_safetensors=True,
174
- variant=self._get_variant(dtype),
175
- low_cpu_mem_usage=True
176
- )
177
-
178
- # Create img2img pipeline
179
- self.pipe_i2i = StableDiffusionImg2ImgPipeline(
180
- vae=self.pipe_t2i.vae,
181
- text_encoder=self.pipe_t2i.text_encoder,
182
- tokenizer=self.pipe_t2i.tokenizer,
183
- unet=self.pipe_t2i.unet,
184
- scheduler=self.pipe_t2i.scheduler,
185
- safety_checker=None,
186
- feature_extractor=None,
187
- requires_safety_checker=False
188
- )
189
-
190
- # Apply optimizations
191
- await self._apply_optimizations()
192
-
193
- load_time = time.time() - start_time
194
- logger.info(f"Models loaded successfully in {load_time:.2f}s")
195
- logger.info(f"Applied optimizations: {', '.join(self.optimizations_applied)}")
196
-
197
- self.is_loaded = True
198
- return True
199
-
200
- except Exception as e:
201
- logger.error(f"Failed to load models: {e}")
202
- logger.error(traceback.format_exc())
203
- return False
204
 
205
  def _get_optimal_dtype(self) -> torch.dtype:
206
- """Determine optimal data type based on hardware"""
207
- try:
208
- # Check for bfloat16 support (better for newer GPUs)
209
- if torch.cuda.is_available() and torch.cuda.is_bf16_supported():
210
- logger.info("Using bfloat16 for optimal performance")
211
  return torch.bfloat16
212
- # Fall back to float16 for compatibility
213
- elif torch.cuda.is_available():
214
- logger.info("Using float16 for CUDA")
215
- return torch.float16
216
- # CPU fallback
217
  else:
218
- logger.info("Using float32 for CPU")
219
- return torch.float32
220
- except:
221
- logger.warning("Could not detect optimal dtype, using float32")
222
- return torch.float32
223
-
224
- def _get_variant(self, dtype: torch.dtype) -> Optional[str]:
225
- """Get model variant based on dtype"""
226
- return "fp16" if dtype == torch.float16 else None
227
-
228
- async def _apply_optimizations(self):
229
- """Apply performance optimizations with proper fallbacks"""
230
-
231
- # 1. Try xformers optimization
232
- if self._try_enable_xformers():
233
- self.optimizations_applied.append("xformers")
234
-
235
- # 2. Try model CPU offloading for memory efficiency
236
- if self._try_enable_cpu_offload():
237
- self.optimizations_applied.append("cpu_offload")
238
-
239
- # 3. Try PyTorch 2.0+ compilation
240
- if self._try_enable_torch_compile():
241
- self.optimizations_applied.append("torch_compile")
242
-
243
- # 4. Enable VAE slicing for memory efficiency
244
- self._enable_vae_slicing()
245
 
246
- # 5. Clear CUDA cache
247
- if torch.cuda.is_available():
248
- torch.cuda.empty_cache()
 
 
 
 
 
 
 
 
 
249
 
250
  def _try_enable_xformers(self) -> bool:
251
- """Try to enable xformers with fallback"""
252
  try:
 
253
  import xformers.ops
254
- self.pipe_t2i.enable_xformers_memory_efficient_attention()
255
- self.pipe_i2i.enable_xformers_memory_efficient_attention()
256
- logger.info("✓ Enabled xformers memory efficient attention")
257
  return True
258
  except ImportError:
259
- logger.info("xformers not available, using default attention")
260
  return False
261
  except Exception as e:
262
- logger.warning(f"Could not enable xformers: {e}")
263
  return False
264
 
265
- def _try_enable_cpu_offload(self) -> bool:
266
- """Try to enable CPU offloading with fallback"""
267
- try:
268
- # Only enable if we have limited GPU memory
269
- if torch.cuda.is_available():
270
- gpu_memory = torch.cuda.get_device_properties(0).total_memory
271
- if gpu_memory < 8 * 1024**3: # Less than 8GB
272
- self.pipe_t2i.enable_sequential_cpu_offload()
273
- self.pipe_i2i.enable_sequential_cpu_offload()
274
- logger.info("✓ Enabled sequential CPU offloading")
275
- return True
276
- except Exception as e:
277
- logger.warning(f"⚠ Could not enable CPU offload: {e}")
278
- return False
279
-
280
- def _try_enable_torch_compile(self) -> bool:
281
- """Try to enable torch.compile with version check and fallback"""
282
- try:
283
- # Check PyTorch version
284
- torch_version = torch.__version__.split('+')[0]
285
- major, minor = map(int, torch_version.split('.')[:2])
286
-
287
- if major >= 2:
288
- logger.info("PyTorch 2.0+ detected, attempting compilation...")
289
- self.pipe_t2i.unet = torch.compile(
290
- self.pipe_t2i.unet,
291
- mode="reduce-overhead",
292
- fullgraph=False # More compatible
293
- )
294
- self.pipe_i2i.unet = torch.compile(
295
- self.pipe_i2i.unet,
296
- mode="reduce-overhead",
297
- fullgraph=False
298
- )
299
- logger.info("✓ Successfully compiled UNet with torch.compile")
300
- return True
301
- else:
302
- logger.info(f"⚠ PyTorch {torch_version} < 2.0, compilation not available")
303
- except Exception as e:
304
- logger.warning(f"⚠ Could not compile UNet: {e}")
305
- return False
306
 
307
- def _enable_vae_slicing(self):
308
- """Enable VAE slicing for memory efficiency"""
309
  try:
310
- self.pipe_t2i.vae.enable_slicing()
311
- self.pipe_i2i.vae.enable_slicing()
312
- logger.info("✓ Enabled VAE slicing")
313
- except Exception as e:
314
- logger.warning(f"⚠ Could not enable VAE slicing: {e}")
315
-
316
-
317
- class CacheManager:
318
- """Manages caching for generated images and analyses"""
319
-
320
- def __init__(self, max_size: int = 100):
321
- self.max_size = max_size
322
- self.image_cache: Dict[str, Tuple[Image.Image, datetime]] = {}
323
- self.analysis_cache: Dict[str, Tuple[str, datetime]] = {}
324
- self.cache_ttl = timedelta(hours=24)
325
-
326
- def get_cache_key(self, *args) -> str:
327
- """Generate consistent cache key"""
328
- key_str = "|".join(str(arg) for arg in args)
329
- return hashlib.sha256(key_str.encode()).hexdigest()[:16]
330
-
331
- def get_cached_image(self, cache_key: str) -> Optional[Image.Image]:
332
- """Get cached image if valid"""
333
- if cache_key in self.image_cache:
334
- image, timestamp = self.image_cache[cache_key]
335
- if datetime.now() - timestamp < self.cache_ttl:
336
- return image
337
- else:
338
- del self.image_cache[cache_key]
339
- return None
340
 
341
- def cache_image(self, cache_key: str, image: Image.Image):
342
- """Cache an image with LRU eviction"""
343
- # Remove oldest if at capacity
344
- if len(self.image_cache) >= self.max_size:
345
- oldest_key = min(self.image_cache.keys(),
346
- key=lambda k: self.image_cache[k][1])
347
- del self.image_cache[oldest_key]
348
-
349
- self.image_cache[cache_key] = (image, datetime.now())
350
-
351
- def get_cached_analysis(self, cache_key: str) -> Optional[str]:
352
- """Get cached analysis if valid"""
353
- if cache_key in self.analysis_cache:
354
- analysis, timestamp = self.analysis_cache[cache_key]
355
- if datetime.now() - timestamp < self.cache_ttl:
356
- return analysis
 
 
 
 
 
 
 
 
 
 
357
  else:
358
- del self.analysis_cache[cache_key]
359
- return None
360
-
361
- def cache_analysis(self, cache_key: str, analysis: str):
362
- """Cache an analysis"""
363
- if len(self.analysis_cache) >= self.max_size:
364
- oldest_key = min(self.analysis_cache.keys(),
365
- key=lambda k: self.analysis_cache[k][1])
366
- del self.analysis_cache[oldest_key]
367
 
368
- self.analysis_cache[cache_key] = (analysis, datetime.now())
 
 
369
 
370
- def clear_expired(self):
371
- """Clear expired cache entries"""
372
- now = datetime.now()
373
- expired_images = [k for k, (_, t) in self.image_cache.items()
374
- if now - t >= self.cache_ttl]
375
- for k in expired_images:
376
- del self.image_cache[k]
377
-
378
- expired_analyses = [k for k, (_, t) in self.analysis_cache.items()
379
- if now - t >= self.cache_ttl]
380
- for k in expired_analyses:
381
- del self.analysis_cache[k]
382
 
 
 
 
 
383
 
384
  class ImageProcessor:
385
- """Handles image generation and transformation with error handling"""
386
 
387
- def __init__(self, model_manager: ModelManager, cache_manager: CacheManager):
 
388
  self.model_manager = model_manager
389
  self.cache_manager = cache_manager
390
- self.style_suffixes = {
391
- "None": "",
392
- "Photorealistic": ", photorealistic, ultra detailed, 8k, professional photography",
393
- "Cinematic": ", cinematic lighting, movie scene, dramatic atmosphere, film grain",
394
- "Anime": ", anime style, vibrant colors, cel shaded, studio ghibli inspired",
395
- "Digital Art": ", digital art, detailed illustration, concept art",
396
- "Oil Painting": ", oil painting, classical art, rich textures",
397
- "Watercolor": ", watercolor painting, soft edges, artistic",
398
- "3D Render": ", 3D render, octane render, detailed 3D",
399
- "Fantasy": ", fantasy art, magical, ethereal atmosphere",
400
- "Sci-Fi": ", sci-fi art, futuristic, high-tech"
401
- }
402
-
403
- @GPU(duration=120)
404
- async def generate_image(
405
- self,
406
- prompt: str,
407
- style: str = "None",
408
- ratio: str = "1:1 Square (1024x1024)",
409
- steps: int = 9,
410
- seed: int = 42,
411
- randomize: bool = True,
412
- guidance_scale: float = 0.0
413
- ) -> GenerationResult:
414
  """Generate image with comprehensive error handling"""
415
- result = GenerationResult(success=False)
416
  start_time = time.time()
417
 
418
- try:
419
- # Validate inputs
420
- if not prompt or not prompt.strip():
421
- result.error_code = ErrorCode.INVALID_INPUT
422
- result.message = "Prompt cannot be empty"
423
- return result
424
-
425
- # Ensure models are loaded
426
- if not await self.model_manager.load_models():
427
- result.error_code = ErrorCode.MODEL_LOAD_ERROR
428
- result.message = "Failed to load models"
 
 
 
429
  return result
430
 
431
- # Parse dimensions
432
- width, height = self._parse_aspect_ratio(ratio)
433
-
434
- # Handle seed
435
- if randomize:
436
- seed = torch.randint(0, 2**32 - 1, (1,)).item()
 
 
437
 
438
- generator = torch.Generator().manual_seed(seed)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
 
440
- # Enhance prompt with style
441
- enhanced_prompt = prompt + self.style_suffixes.get(style, "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
- # Optimize steps based on complexity
444
- optimized_steps = self._optimize_steps(enhanced_prompt, steps)
 
 
 
 
445
 
446
- # Generate
447
- logger.info(f"Generating: {enhanced_prompt[:50]}... | {width}x{height} | {optimized_steps} steps")
448
-
449
- output = self.model_manager.pipe_t2i(
450
- prompt=enhanced_prompt,
451
- width=width,
452
- height=height,
453
- num_inference_steps=optimized_steps,
454
- guidance_scale=guidance_scale,
455
- generator=generator,
456
- output_type="pil"
457
  )
458
 
459
- # Success
460
- result.success = True
461
- result.image = output.images[0]
462
- result.seed = seed
463
- result.message = "Generated successfully"
464
- result.generation_time = time.time() - start_time
465
-
466
- logger.info(f"Generated in {result.generation_time:.2f}s")
467
 
468
  except torch.cuda.OutOfMemoryError:
469
- result.error_code = ErrorCode.RESOURCE_ERROR
470
- result.message = "GPU out of memory. Try smaller image or restart space."
471
- logger.error("GPU OOM during generation")
472
- except Exception as e:
473
- result.error_code = ErrorCode.GENERATION_ERROR
474
- result.message = f"Generation failed: {str(e)}"
475
- logger.error(f"Generation error: {e}")
476
- logger.error(traceback.format_exc())
477
-
478
- return result
479
-
480
- @GPU(duration=120)
481
- async def transform_image(
482
- self,
483
- input_image: Image.Image,
484
- prompt: str,
485
- style: str = "None",
486
- strength: float = 0.8,
487
- steps: int = 9,
488
- seed: int = 42,
489
- randomize: bool = True,
490
- guidance_scale: float = 0.0
491
- ) -> GenerationResult:
492
- """Transform image with comprehensive error handling"""
493
- result = GenerationResult(success=False)
494
- start_time = time.time()
495
-
496
- try:
497
- # Validate inputs
498
- if input_image is None:
499
- result.error_code = ErrorCode.INVALID_INPUT
500
- result.message = "Please upload an image"
501
- return result
502
-
503
- if not prompt or not prompt.strip():
504
- result.error_code = ErrorCode.INVALID_INPUT
505
- result.message = "Prompt cannot be empty"
506
- return result
507
-
508
- # Ensure models are loaded
509
- if not await self.model_manager.load_models():
510
- result.error_code = ErrorCode.MODEL_LOAD_ERROR
511
- result.message = "Failed to load models"
512
- return result
513
 
514
- # Preprocess image
515
- processed_image = self._preprocess_image(input_image)
 
 
 
 
 
 
 
516
 
517
- # Handle seed
518
- if randomize:
519
- seed = torch.randint(0, 2**32 - 1, (1,)).item()
 
 
 
520
 
521
- generator = torch.Generator().manual_seed(seed)
 
 
 
 
 
 
 
522
 
523
- # Enhance prompt
524
- enhanced_prompt = prompt + self.style_suffixes.get(style, "")
 
 
 
 
525
 
526
- # Optimize steps based on strength
527
- effective_steps = max(4, int(steps * strength)) if strength > 0 else steps
528
 
529
  # Transform
530
- logger.info(f"Transforming: {enhanced_prompt[:50]}... | strength={strength}")
531
-
532
- output = self.model_manager.pipe_i2i(
533
- prompt=enhanced_prompt,
534
- image=processed_image,
535
- strength=strength,
536
- num_inference_steps=effective_steps,
537
- guidance_scale=guidance_scale,
538
- generator=generator,
539
- output_type="pil"
 
 
 
 
 
 
 
 
 
540
  )
541
 
542
- # Success
543
- result.success = True
544
- result.image = output.images[0]
545
- result.seed = seed
546
- result.message = "Transformed successfully"
547
- result.generation_time = time.time() - start_time
548
-
549
- logger.info(f"Transformed in {result.generation_time:.2f}s")
550
 
551
- except torch.cuda.OutOfMemoryError:
552
- result.error_code = ErrorCode.RESOURCE_ERROR
553
- result.message = "GPU out of memory. Try smaller image or restart space."
554
- logger.error("GPU OOM during transform")
555
  except Exception as e:
556
- result.error_code = ErrorCode.TRANSFORM_ERROR
557
- result.message = f"Transform failed: {str(e)}"
558
- logger.error(f"Transform error: {e}")
559
- logger.error(traceback.format_exc())
560
-
561
- return result
562
-
563
- def _parse_aspect_ratio(self, ratio: str) -> Tuple[int, int]:
564
- """Parse aspect ratio string to dimensions"""
565
- ratios = {
566
- "1:1": (1024, 1024),
567
- "16:9": (1344, 768),
568
- "9:16": (768, 1344),
569
- "4:3": (1152, 896),
570
- "3:4": (896, 1152)
571
- }
572
-
573
- # Extract ratio from string
574
- for key, (w, h) in ratios.items():
575
- if key in ratio:
576
- return w, h
577
-
578
- # Default to 1:1
579
- return 1024, 1024
580
-
581
- def _optimize_steps(self, prompt: str, base_steps: int) -> int:
582
- """Optimize step count based on prompt complexity"""
583
- # Calculate complexity score
584
- words = len(prompt.split())
585
- commas = prompt.count(',')
586
- periods = prompt.count('.')
587
-
588
- complexity = words + (commas * 2) + (periods * 2)
589
-
590
- # Adjust steps
591
- if complexity < 10:
592
- return max(4, base_steps - 2)
593
- elif complexity > 30:
594
- return min(16, base_steps + 2)
595
- else:
596
- return base_steps
597
-
598
- def _preprocess_image(self, image: Image.Image) -> Image.Image:
599
- """Preprocess image for img2img pipeline"""
600
- # Convert to RGB
601
- if image.mode != "RGB":
602
- image = image.convert("RGB")
603
-
604
- # Resize to standard dimensions (maintain aspect ratio)
605
- w, h = image.size
606
-
607
- # Calculate new dimensions (multiple of 16)
608
- max_size = 1024
609
- aspect_ratio = w / h
610
-
611
- if w > h:
612
- new_w = min(max_size, w)
613
- new_h = int(new_w / aspect_ratio)
614
- else:
615
- new_h = min(max_size, h)
616
- new_w = int(new_h * aspect_ratio)
617
-
618
- # Round to nearest multiple of 16
619
- new_w = (new_w // 16) * 16
620
- new_h = (new_h // 16) * 16
621
-
622
- # Ensure minimum dimensions
623
- new_w = max(512, new_w)
624
- new_h = max(512, new_h)
625
-
626
- return image.resize((new_w, new_h), Image.LANCZOS)
627
-
628
 
629
- # Initialize global components
630
- system_monitor = SystemMonitor()
631
  model_manager = ModelManager()
632
- cache_manager = CacheManager(max_size=100)
633
- image_processor = ImageProcessor(model_manager, cache_manager)
634
-
635
- # UI Constants
636
- STYLES = ["None", "Photorealistic", "Cinematic", "Anime", "Digital Art",
637
- "Oil Painting", "Watercolor", "3D Render", "Fantasy", "Sci-Fi"]
638
-
639
- RATIOS = [
640
- "1:1 Square (1024x1024)",
641
- "16:9 Landscape (1344x768)",
642
- "9:16 Portrait (768x1344)",
643
- "4:3 Standard (1152x896)"
644
- ]
645
-
646
- # CSS for enhanced UI
647
- CSS = """
648
- :root {
649
- --primary: #3b82f6;
650
- --primary-dark: #2563eb;
651
- --secondary: #10b981;
652
- --background: #f8fafc;
653
- --surface: #ffffff;
654
- --error: #ef4444;
655
- --warning: #f59e0b;
656
- --success: #22c55e;
657
- --border-radius: 12px;
658
- --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
659
- }
660
-
661
- /* Main container */
662
- .gradio-container {
663
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
664
- background: var(--background);
665
- }
666
-
667
- /* Headers */
668
- h1 {
669
- color: #1e293b;
670
- font-weight: 700;
671
- font-size: 2.5rem;
672
- margin-bottom: 0.5rem;
673
- }
674
-
675
- h2 {
676
- color: #334155;
677
- font-weight: 600;
678
- font-size: 1.5rem;
679
- margin-top: 1.5rem;
680
- }
681
-
682
- /* Buttons */
683
- .gradio-button {
684
- border-radius: var(--border-radius);
685
- font-weight: 600;
686
- transition: all 0.2s ease;
687
- }
688
-
689
- .gradio-button.primary {
690
- background: var(--primary);
691
- border: none;
692
- }
693
-
694
- .gradio-button.primary:hover {
695
- background: var(--primary-dark);
696
- transform: translateY(-1px);
697
- box-shadow: var(--shadow);
698
- }
699
-
700
- /* Cards */
701
- .border {
702
- border: 1px solid #e2e8f0 !important;
703
- border-radius: var(--border-radius) !important;
704
- background: var(--surface);
705
- }
706
-
707
- /* Status indicators */
708
- .status-success {
709
- color: var(--success);
710
- font-weight: 600;
711
- }
712
-
713
- .status-error {
714
- color: var(--error);
715
- font-weight: 600;
716
- }
717
-
718
- .status-warning {
719
- color: var(--warning);
720
- font-weight: 600;
721
- }
722
-
723
- /* Performance metrics */
724
- .metric-card {
725
- background: var(--surface);
726
- padding: 1rem;
727
- border-radius: var(--border-radius);
728
- box-shadow: var(--shadow);
729
- }
730
-
731
- .metric-value {
732
- font-size: 2rem;
733
- font-weight: 700;
734
- color: var(--primary);
735
- }
736
-
737
- .metric-label {
738
- color: #64748b;
739
- font-size: 0.875rem;
740
- margin-top: 0.25rem;
741
- }
742
-
743
- /* Animations */
744
- @keyframes pulse {
745
- 0%, 100% { opacity: 1; }
746
- 50% { opacity: 0.5; }
747
- }
748
-
749
- .loading {
750
- animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
751
- }
752
-
753
- /* Responsive design */
754
- @media (max-width: 768px) {
755
- .gradio-row {
756
- flex-direction: column !important;
757
- }
758
- }
759
- """
760
-
761
-
762
- async def handle_generation(
763
- prompt: str,
764
- style: str,
765
- ratio: str,
766
- steps: int,
767
- seed: int,
768
- randomize: bool,
769
- guidance_scale: float
770
- ) -> Tuple[Optional[Image.Image], int, str]:
771
- """Handle image generation with caching"""
772
- try:
773
- # Check cache first
774
- cache_key = cache_manager.get_cache_key(prompt, style, ratio, steps, seed)
775
- cached_image = cache_manager.get_cached_image(cache_key)
776
-
777
- if cached_image:
778
- system_monitor.record_cache_hit()
779
- logger.info("Returning cached generation")
780
- return cached_image, seed, "✅ Retrieved from cache"
781
-
782
- system_monitor.record_cache_miss()
783
-
784
- # Generate new image
785
- result = await image_processor.generate_image(
786
- prompt=prompt,
787
- style=style,
788
- ratio=ratio,
789
- steps=steps,
790
- seed=seed,
791
- randomize=randomize,
792
- guidance_scale=guidance_scale
793
- )
794
-
795
- if result.success:
796
- # Cache the result
797
- cache_manager.cache_image(cache_key, result.image)
798
- system_monitor.record_generation(True)
799
- return result.image, result.seed, f"✅ {result.message} ({result.generation_time:.1f}s)"
800
- else:
801
- system_monitor.record_generation(False)
802
- return None, seed, f"❌ {result.message}"
803
-
804
- except Exception as e:
805
- system_monitor.record_generation(False)
806
- logger.error(f"Generation handler error: {e}")
807
- return None, seed, f"❌ Unexpected error: {str(e)}"
808
-
809
-
810
- async def handle_transform(
811
- input_image: Image.Image,
812
- prompt: str,
813
- style: str,
814
- strength: float,
815
- steps: int,
816
- seed: int,
817
- randomize: bool,
818
- guidance_scale: float
819
- ) -> Tuple[Optional[Image.Image], int, str]:
820
- """Handle image transformation"""
821
- try:
822
- result = await image_processor.transform_image(
823
- input_image=input_image,
824
- prompt=prompt,
825
- style=style,
826
- strength=strength,
827
- steps=steps,
828
- seed=seed,
829
- randomize=randomize,
830
- guidance_scale=guidance_scale
831
- )
832
-
833
- if result.success:
834
- system_monitor.record_generation(True)
835
- return result.image, result.seed, f"✅ {result.message} ({result.generation_time:.1f}s)"
836
  else:
837
- system_monitor.record_generation(False)
838
- return None, seed, f"❌ {result.message}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
839
 
840
- except Exception as e:
841
- system_monitor.record_generation(False)
842
- logger.error(f"Transform handler error: {e}")
843
- return None, seed, f" Unexpected error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
844
 
 
845
 
846
- def create_interface() -> gr.Blocks:
847
- """Create the Gradio interface"""
 
 
848
 
 
 
 
849
  with gr.Blocks(
850
- title="Z Image Turbo - Production",
851
- theme=gr.themes.Soft(),
852
- css=CSS
853
  ) as demo:
854
-
855
- # Header
856
- gr.HTML("""
857
- <div style="text-align: center; padding: 2rem 0;">
858
- <h1 style="margin: 0;">⚡ Z Image Turbo</h1>
859
- <p style="color: #64748b; font-size: 1.1rem; margin-top: 0.5rem;">
860
- Production-Ready Image Generation with Advanced Optimizations
861
- </p>
862
- </div>
863
- """)
864
 
865
  with gr.Tabs():
866
  # Generation Tab
867
- with gr.Tab("🎨 Generate", elem_id="generate-tab"):
868
  with gr.Row():
869
- with gr.Column(scale=3):
870
- gen_prompt = gr.Textbox(
871
  label="Prompt",
872
  placeholder="Describe the image you want to generate...",
873
- lines=3,
874
- max_lines=5
875
  )
876
-
877
- gen_style = gr.Dropdown(
878
- choices=STYLES,
879
- value="None",
880
- label="Style",
881
- info="Apply a style to your generation"
882
- )
883
-
884
- gen_ratio = gr.Dropdown(
885
- choices=RATIOS,
886
- value="1:1 Square (1024x1024)",
887
- label="Aspect Ratio"
888
  )
889
 
890
  with gr.Row():
891
- gen_steps = gr.Slider(
892
- minimum=4,
893
- maximum=16,
894
- value=9,
895
- step=1,
896
- label="Inference Steps",
897
- info="More steps = better quality but slower"
898
  )
899
-
900
- gen_guidance = gr.Slider(
901
- minimum=0.0,
902
- maximum=20.0,
903
- value=0.0,
904
- step=0.5,
905
- label="Guidance Scale",
906
- info="Higher = more prompt adherence"
907
  )
908
 
909
  with gr.Row():
910
- gen_seed = gr.Number(
911
- label="Seed",
912
- value=42,
913
- precision=0,
914
- info="Fixed seed for reproducible results"
 
915
  )
916
- gen_randomize = gr.Checkbox(
917
- label="Randomize Seed",
918
- value=True,
919
- info="Generate with random seed"
 
 
920
  )
921
 
922
- gen_btn = gr.Button(
 
 
 
 
 
 
923
  "🚀 Generate",
924
  variant="primary",
925
- size="lg",
926
- elem_classes=["generate-button"]
927
  )
928
 
929
- with gr.Column(scale=2):
930
- gen_output = gr.Image(
931
  label="Generated Image",
932
- type="pil",
933
- format="png",
934
- interactive=False,
935
- show_share_button=True,
936
- show_download_button=True,
937
- elem_classes=["output-image"]
938
  )
939
-
940
- gen_status = gr.Textbox(
941
- label="Status",
942
- interactive=False,
943
- max_lines=2,
944
- elem_classes=["status-text"]
945
  )
946
 
947
- gen_seed_out = gr.Number(
948
- label="Seed Used",
949
- interactive=False,
950
- precision=0
951
- )
952
-
953
- # Event handler
954
- gen_btn.click(
955
- fn=lambda *args: asyncio.run(handle_generation(*args)),
956
  inputs=[
957
- gen_prompt, gen_style, gen_ratio,
958
- gen_steps, gen_seed, gen_randomize, gen_guidance
 
 
 
 
 
959
  ],
960
- outputs=[gen_output, gen_seed_out, gen_status]
961
  )
962
 
963
  # Transform Tab
964
- with gr.Tab("🖼️ Transform", elem_id="transform-tab"):
965
- gr.Markdown("""
966
- ### Transform an existing image with AI
967
- Upload an image and provide a prompt to guide the transformation.
968
- """)
969
-
970
  with gr.Row():
971
- with gr.Column(scale=3):
972
- trans_input = gr.Image(
973
- label="Input Image",
974
- type="pil",
975
- sources=["upload", "webcam"]
976
  )
977
-
978
- trans_prompt = gr.Textbox(
979
  label="Transform Prompt",
980
  placeholder="Describe how to transform the image...",
981
- lines=2
982
  )
983
-
984
- trans_style = gr.Dropdown(
985
- choices=STYLES,
986
- value="None",
987
- label="Style"
988
  )
989
 
990
  with gr.Row():
991
- trans_strength = gr.Slider(
 
992
  minimum=0.0,
993
  maximum=1.0,
994
- value=0.8,
995
- step=0.1,
996
- label="Transformation Strength",
997
- info="Higher = more changes"
998
  )
999
-
1000
- trans_steps = gr.Slider(
1001
- minimum=4,
1002
- maximum=16,
1003
- value=9,
1004
- step=1,
1005
- label="Inference Steps"
1006
  )
1007
 
1008
- with gr.Row():
1009
- trans_seed = gr.Number(
1010
- label="Seed",
1011
- value=42,
1012
- precision=0
1013
- )
1014
- trans_randomize = gr.Checkbox(
1015
- label="Randomize Seed",
1016
- value=True
1017
- )
1018
 
1019
- trans_btn = gr.Button(
1020
- " Transform",
1021
- variant="primary",
1022
- size="lg"
1023
  )
1024
 
1025
- with gr.Column(scale=2):
1026
- trans_output = gr.Image(
1027
- label="Transformed Image",
1028
- type="pil",
1029
- format="png",
1030
- interactive=False,
1031
- show_share_button=True
1032
  )
1033
 
1034
- trans_status = gr.Textbox(
1035
- label="Status",
1036
- interactive=False,
1037
- max_lines=2
 
 
 
 
1038
  )
1039
 
1040
- # Event handler
1041
- trans_btn.click(
1042
- fn=lambda *args: asyncio.run(handle_transform(*args)),
1043
  inputs=[
1044
- trans_input, trans_prompt, trans_style,
1045
- trans_strength, trans_steps, trans_seed,
1046
- trans_randomize, gen_guidance
 
 
 
 
1047
  ],
1048
- outputs=[trans_output, trans_seed_out, trans_status]
1049
  )
1050
 
1051
  # System Monitor Tab
1052
- with gr.Tab("📊 System Monitor", elem_id="monitor-tab"):
1053
- gr.Markdown("""
1054
- ### System Performance Metrics
1055
- Real-time monitoring of system resources and application performance.
1056
- """)
1057
-
1058
- # Resource metrics
1059
- with gr.Row():
1060
- with gr.Column():
1061
- gr.Markdown("#### 🖥️ System Resources")
1062
-
1063
- with gr.Row():
1064
- mem_usage = gr.Number(
1065
- label="Memory Usage (GB)",
1066
- precision=2,
1067
- elem_classes=["metric-value"]
1068
- )
1069
- cpu_usage = gr.Number(
1070
- label="CPU Usage (%)",
1071
- precision=1,
1072
- elem_classes=["metric-value"]
1073
- )
1074
-
1075
- gpu_mem = gr.Number(
1076
- label="GPU Memory (GB)",
1077
- precision=2,
1078
- elem_classes=["metric-value"]
1079
- )
1080
-
1081
- with gr.Column():
1082
- gr.Markdown("#### 📈 Application Metrics")
1083
-
1084
- with gr.Row():
1085
- uptime = gr.Number(
1086
- label="Uptime (seconds)",
1087
- precision=0,
1088
- elem_classes=["metric-value"]
1089
- )
1090
- generations = gr.Number(
1091
- label="Total Generations",
1092
- precision=0,
1093
- elem_classes=["metric-value"]
1094
- )
1095
-
1096
- cache_rate = gr.Number(
1097
- label="Cache Hit Rate (%)",
1098
- precision=1,
1099
- elem_classes=["metric-value"]
1100
- )
1101
-
1102
- # Optimization status
1103
- with gr.Row():
1104
- opt_status = gr.JSON(
1105
- label="Optimization Status",
1106
- value={
1107
- "model_loaded": False,
1108
- "optimizations": [],
1109
- "last_update": datetime.now().isoformat()
1110
- }
1111
- )
1112
-
1113
- # Refresh button
1114
- refresh_btn = gr.Button("🔄 Refresh", size="sm")
1115
-
1116
- # Refresh handler
1117
- def refresh_metrics():
1118
- """Refresh all metrics"""
1119
- info = system_monitor.get_system_info()
1120
-
1121
- return (
1122
- info.get("memory_used_gb", 0),
1123
- info.get("cpu_percent", 0),
1124
- info.get("gpu_memory_used_gb", 0),
1125
- info.get("uptime_seconds", 0),
1126
- info.get("active_generations", 0),
1127
- info.get("cache_hit_rate", 0),
1128
- {
1129
- "model_loaded": model_manager.is_loaded,
1130
- "optimizations": model_manager.optimizations_applied,
1131
- "last_update": datetime.now().isoformat()
1132
- }
1133
- )
1134
 
1135
  refresh_btn.click(
1136
- fn=refresh_metrics,
1137
- outputs=[
1138
- mem_usage, cpu_usage, gpu_mem,
1139
- uptime, generations, cache_rate, opt_status
1140
- ]
1141
  )
1142
 
1143
- # Auto-refresh every 5 seconds
1144
- demo.load(
1145
- fn=refresh_metrics,
1146
- outputs=[
1147
- mem_usage, cpu_usage, gpu_mem,
1148
- uptime, generations, cache_rate, opt_status
1149
- ],
1150
- every=5
1151
  )
1152
 
1153
- # About Tab
1154
- with gr.Tab("ℹ️ About", elem_id="about-tab"):
1155
- gr.Markdown("""
1156
- # Z Image Turbo - Production Edition
1157
-
1158
- ## Features
1159
- - ✅ **PyTorch 2.0+ Compilation** with graceful fallback
1160
- - ✅ **xformers Optimization** with CPU fallback
1161
- - ✅ **Memory Management** with CPU offloading
1162
- - ✅ **Caching System** for improved performance
1163
- - ✅ **Comprehensive Error Handling**
1164
- - ✅ **Real-time Monitoring**
1165
- - ✅ **Production-Ready Architecture**
1166
-
1167
- ## Model
1168
- - **Base Model**: [Tongyi-MAI/Z-Image-Turbo](https://huggingface.co/Tongyi-MAI/Z-Image-Turbo)
1169
- - **Architecture**: DiT-based diffusion model
1170
- - **Optimized for**: Fast generation with high quality
1171
-
1172
- ## System Requirements
1173
- - GPU with at least 6GB VRAM recommended
1174
- - PyTorch 2.0+ for optimal performance
1175
- - Optional: xformers for memory efficiency
1176
-
1177
- ## Changelog
1178
- ### v2.0.0 Production
1179
- - Added comprehensive error handling
1180
- - Implemented PyTorch compilation with fallback
1181
- - Added xformers optimization with CPU fallback
1182
- - Integrated caching system
1183
- - Added real-time monitoring
1184
- - Improved resource management
1185
-
1186
- ---
1187
- Created with ❤️ by AI Agent Framework Specialist
1188
- """)
1189
 
1190
  return demo
1191
 
1192
-
1193
- # Health check endpoint
1194
- async def health_check() -> Dict[str, Any]:
1195
- """Application health check"""
1196
- return {
1197
- "status": "healthy" if model_manager.is_loaded else "loading",
1198
- "model_loaded": model_manager.is_loaded,
1199
- "optimizations": model_manager.optimizations_applied,
1200
- "uptime": time.time() - system_monitor.start_time,
1201
- "generation_count": system_monitor.generation_count,
1202
- "error_count": system_monitor.error_count,
1203
- "cache_hit_rate": system_monitor.get_cache_hit_rate()
1204
- }
1205
-
1206
-
1207
- # Main application entry
1208
  if __name__ == "__main__":
1209
- logger.info("Starting Z Image Turbo - Production Edition")
1210
 
1211
- # Create interface
1212
- demo = create_interface()
1213
-
1214
- # Configure for Hugging Face Spaces
1215
- demo.queue(
1216
- api_open=False,
1217
- max_size=20,
1218
- default_concurrency_limit=1
1219
- )
1220
 
1221
  # Launch with optimizations
1222
  demo.launch(
1223
  share=False,
1224
  show_error=True,
1225
- show_tips=True,
1226
  max_threads=40,
1227
- prevent_thread_lock=False
1228
- )
1229
-
1230
- logger.info("Application launched successfully")
 
14
  Version: 2.0.0 Production
15
  """
16
 
17
+ # IMPORT SPACES FIRST - Before any CUDA-related imports
18
+ try:
19
+ from spaces import GPU
20
+ SPACES_AVAILABLE = True
21
+ except ImportError:
22
+ SPACES_AVAILABLE = False
23
+ print("Warning: spaces package not available, GPU acceleration disabled")
24
+
25
  import os
26
  import sys
27
  import time
28
  import asyncio
 
29
  import logging
30
+ import hashlib
31
+ import gc
32
+ import psutil
33
+ import threading
34
+ from datetime import datetime, timedelta
35
+ from typing import Optional, Dict, Any, List, Tuple
36
  from dataclasses import dataclass
37
+ from pathlib import Path
38
+ import json
39
 
40
+ # Now import CUDA-related packages
 
41
  import torch
42
  import numpy as np
43
  from PIL import Image
44
+ import gradio as gr
 
 
 
 
 
 
 
45
 
46
+ # Import diffusers after spaces
47
+ try:
48
+ from diffusers import DiffusionPipeline
49
+ from diffusers.utils import logging as diffusers_logging
50
+ DIFFUSERS_AVAILABLE = True
51
+ except ImportError:
52
+ DIFFUSERS_AVAILABLE = False
53
+ print("Warning: diffusers not properly installed")
54
 
55
+ # Configure logging
56
  logging.basicConfig(
57
  level=logging.INFO,
58
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
59
  handlers=[
60
+ logging.FileHandler('z_image_turbo.log'),
61
+ logging.StreamHandler(sys.stdout)
62
  ]
63
  )
64
  logger = logging.getLogger(__name__)
65
 
66
+ # Suppress verbose logging
67
+ diffusers_logging.set_verbosity_error()
68
+
69
+ # Constants
70
+ MODEL_ID = "Tongyi-MAI/Z-Image-Turbo"
71
+ DEFAULT_ASPECT_RATIO = "1:1"
72
+ STYLE_PRESETS = [
73
+ "None",
74
+ "Cinematic",
75
+ "Photographic",
76
+ "Anime",
77
+ "Oil Painting",
78
+ "Watercolor",
79
+ "Cyberpunk",
80
+ "Fantasy Art",
81
+ "3D Render",
82
+ "Vintage"
83
+ ]
84
+ ASPECT_RATIOS = {
85
+ "1:1": (512, 512),
86
+ "16:9": (768, 432),
87
+ "9:16": (432, 768),
88
+ "4:3": (576, 432),
89
+ "3:4": (432, 576),
90
+ "3:2": (612, 408),
91
+ "2:3": (408, 612)
92
+ }
93
 
94
+ # Custom CSS for better UI
95
+ CUSTOM_CSS = """
96
+ .footer {
97
+ text-align: center;
98
+ margin-top: 20px;
99
+ padding: 10px;
100
+ background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
101
+ border-radius: 10px;
102
+ color: white;
103
+ }
 
104
 
105
+ .generate-btn {
106
+ background: linear-gradient(45deg, #f093fb 0%, #f5576c 100%) !important;
107
+ border: none !important;
108
+ font-weight: bold !important;
109
+ }
110
+
111
+ .main-container {
112
+ max-width: 1200px;
113
+ margin: 0 auto;
114
+ }
115
+
116
+ .system-monitor {
117
+ font-family: 'Courier New', monospace;
118
+ background: #f8f9fa;
119
+ padding: 10px;
120
+ border-radius: 5px;
121
+ margin: 10px 0;
122
+ }
123
+
124
+ .error-message {
125
+ background: #fee;
126
+ border: 1px solid #fcc;
127
+ padding: 10px;
128
+ border-radius: 5px;
129
+ color: #c00;
130
+ }
131
+
132
+ .success-message {
133
+ background: #efe;
134
+ border: 1px solid #cfc;
135
+ padding: 10px;
136
+ border-radius: 5px;
137
+ color: #080;
138
+ }
139
+ """
140
 
141
  @dataclass
142
  class GenerationResult:
143
+ """Result of image generation with metadata"""
144
  success: bool
145
  image: Optional[Image.Image] = None
146
+ error: Optional[str] = None
147
+ error_code: Optional[int] = None
148
+ generation_time: Optional[float] = None
149
+ cache_hit: bool = False
150
+ optimization_used: List[str] = None
151
+
152
+ class CacheManager:
153
+ """Intelligent cache with LRU eviction and TTL"""
154
+
155
+ def __init__(self, max_size: int = 50, ttl_minutes: int = 30):
156
+ self.max_size = max_size
157
+ self.ttl = timedelta(minutes=ttl_minutes)
158
+ self.cache: Dict[str, Tuple[Image.Image, datetime]] = {}
159
+ self.access_times: Dict[str, datetime] = {}
160
+ self.lock = threading.Lock()
161
+
162
+ def _generate_key(self, prompt: str, negative_prompt: str, style: str,
163
+ aspect_ratio: str, guidance_scale: float,
164
+ inference_steps: int, seed: int) -> str:
165
+ """Generate cache key from parameters"""
166
+ key_data = f"{prompt}|{negative_prompt}|{style}|{aspect_ratio}|{guidance_scale}|{inference_steps}|{seed}"
167
+ return hashlib.md5(key_data.encode()).hexdigest()
168
+
169
+ def get(self, prompt: str, negative_prompt: str, style: str,
170
+ aspect_ratio: str, guidance_scale: float,
171
+ inference_steps: int, seed: int) -> Optional[Image.Image]:
172
+ """Get cached image if available and not expired"""
173
+ key = self._generate_key(prompt, negative_prompt, style, aspect_ratio,
174
+ guidance_scale, inference_steps, seed)
175
+
176
+ with self.lock:
177
+ if key in self.cache:
178
+ image, timestamp = self.cache[key]
179
+ if datetime.now() - timestamp < self.ttl:
180
+ self.access_times[key] = datetime.now()
181
+ logger.info(f"Cache hit for key: {key[:8]}...")
182
+ return image.copy()
183
+ else:
184
+ # Expired entry
185
+ del self.cache[key]
186
+ if key in self.access_times:
187
+ del self.access_times[key]
188
+ return None
189
 
190
+ def put(self, prompt: str, negative_prompt: str, style: str,
191
+ aspect_ratio: str, guidance_scale: float,
192
+ inference_steps: int, seed: int, image: Image.Image):
193
+ """Cache image with LRU eviction"""
194
+ key = self._generate_key(prompt, negative_prompt, style, aspect_ratio,
195
+ guidance_scale, inference_steps, seed)
196
+
197
+ with self.lock:
198
+ # Evict if necessary
199
+ if len(self.cache) >= self.max_size and key not in self.cache:
200
+ # Find least recently used
201
+ lru_key = min(self.access_times.keys(),
202
+ key=lambda k: self.access_times[k])
203
+ del self.cache[lru_key]
204
+ del self.access_times[lru_key]
205
+ logger.info(f"Evicted LRU entry: {lru_key[:8]}...")
206
+
207
+ self.cache[key] = (image.copy(), datetime.now())
208
+ self.access_times[key] = datetime.now()
209
+ logger.info(f"Cached new image: {key[:8]}...")
210
+
211
+ def clear(self):
212
+ """Clear all cache entries"""
213
+ with self.lock:
214
+ self.cache.clear()
215
+ self.access_times.clear()
216
+ logger.info("Cache cleared")
217
+
218
+ def get_stats(self) -> Dict[str, Any]:
219
+ """Get cache statistics"""
220
+ with self.lock:
221
+ return {
222
+ "size": len(self.cache),
223
+ "max_size": self.max_size,
224
+ "usage_percent": (len(self.cache) / self.max_size) * 100
225
+ }
226
 
227
  class SystemMonitor:
228
+ """Monitor system resources and performance metrics"""
229
 
230
  def __init__(self):
231
+ self.start_time = datetime.now()
232
  self.generation_count = 0
233
+ self.success_count = 0
234
  self.error_count = 0
235
+ self.total_generation_time = 0
236
+ self.lock = threading.Lock()
237
+
238
+ def log_generation(self, success: bool, generation_time: float):
239
+ """Log generation metrics"""
240
+ with self.lock:
241
+ self.generation_count += 1
242
+ self.total_generation_time += generation_time
243
+ if success:
244
+ self.success_count += 1
245
+ else:
246
+ self.error_count += 1
247
 
248
+ def get_stats(self) -> Dict[str, Any]:
249
+ """Get comprehensive system stats"""
250
+ with self.lock:
251
+ uptime = datetime.now() - self.start_time
252
+ avg_gen_time = (self.total_generation_time / self.generation_count
253
+ if self.generation_count > 0 else 0)
254
 
255
+ stats = {
256
+ # System resources
 
 
 
257
  "cpu_percent": psutil.cpu_percent(interval=0.1),
258
+ "memory_percent": psutil.virtual_memory().percent,
259
+ "disk_percent": psutil.disk_usage('/').percent,
260
+
261
+ # GPU info if available
262
+ "gpu_available": torch.cuda.is_available(),
263
+ "gpu_memory": None,
264
+
265
+ # Performance metrics
266
+ "uptime_seconds": uptime.total_seconds(),
267
+ "uptime_str": str(uptime).split('.')[0],
268
+ "generation_count": self.generation_count,
269
+ "success_count": self.success_count,
270
  "error_count": self.error_count,
271
+ "success_rate": (self.success_count / self.generation_count * 100
272
+ if self.generation_count > 0 else 0),
273
+ "avg_generation_time": round(avg_gen_time, 2),
274
+ "generations_per_minute": (self.generation_count / uptime.total_seconds() * 60
275
+ if uptime.total_seconds() > 0 else 0)
276
  }
 
 
 
277
 
278
+ # Add GPU stats if available
 
 
279
  if torch.cuda.is_available():
280
+ stats["gpu_memory"] = {
281
+ "allocated": torch.cuda.memory_allocated() / 1024**3,
282
+ "cached": torch.cuda.memory_reserved() / 1024**3,
283
+ "total": torch.cuda.get_device_properties(0).total_memory / 1024**3
284
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
+ return stats
287
 
288
  class ModelManager:
289
+ """Handle model loading and optimization"""
290
 
291
  def __init__(self):
292
+ self.pipeline = None
293
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
294
+ self.dtype = self._get_optimal_dtype()
 
295
  self.optimizations_applied = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
 
297
  def _get_optimal_dtype(self) -> torch.dtype:
298
+ """Determine optimal data type for the hardware"""
299
+ if torch.cuda.is_available():
300
+ # Check GPU capabilities
301
+ gpu_props = torch.cuda.get_device_properties(0)
302
+ if gpu_props.major >= 8: # Ampere and newer
303
  return torch.bfloat16
 
 
 
 
 
304
  else:
305
+ return torch.float16
306
+ return torch.float32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
 
308
+ def _try_enable_torch_compile(self) -> bool:
309
+ """Try to enable torch.compile for better performance"""
310
+ try:
311
+ if hasattr(torch, 'compile') and torch.__version__ >= "2.0":
312
+ logger.info("PyTorch 2.0+ detected, enabling compilation")
313
+ return True
314
+ else:
315
+ logger.info("PyTorch < 2.0 detected, compilation not available")
316
+ return False
317
+ except Exception as e:
318
+ logger.warning(f"Could not enable torch.compile: {e}")
319
+ return False
320
 
321
  def _try_enable_xformers(self) -> bool:
322
+ """Try to enable xformers for memory efficiency"""
323
  try:
324
+ import xformers
325
  import xformers.ops
326
+ logger.info("xformers is available and will be used")
 
 
327
  return True
328
  except ImportError:
329
+ logger.info("xformers not available, using standard attention")
330
  return False
331
  except Exception as e:
332
+ logger.warning(f"Could not enable xformers: {e}")
333
  return False
334
 
335
+ def load_model(self):
336
+ """Load and optimize the model"""
337
+ if self.pipeline is not None:
338
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
 
 
340
  try:
341
+ logger.info(f"Loading model {MODEL_ID} on {self.device} with dtype {self.dtype}")
342
+
343
+ # Load pipeline
344
+ self.pipeline = DiffusionPipeline.from_pretrained(
345
+ MODEL_ID,
346
+ torch_dtype=self.dtype,
347
+ use_safetensors=True,
348
+ variant=None # Remove variant to avoid fp16 issues
349
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
+ # Move to device
352
+ self.pipeline = self.pipeline.to(self.device)
353
+
354
+ # Enable optimizations
355
+ if self.device == "cuda":
356
+ # Try xformers
357
+ if self._try_enable_xformers():
358
+ self.pipeline.enable_xformers_memory_efficient_attention()
359
+ self.optimizations_applied.append("xformers")
360
+
361
+ # Try CPU offloading for memory efficiency
362
+ if torch.cuda.get_device_properties(0).total_memory < 8 * 1024**3: # < 8GB
363
+ logger.info("Low GPU memory detected, enabling CPU offloading")
364
+ self.pipeline.enable_sequential_cpu_offload()
365
+ self.optimizations_applied.append("cpu_offload")
366
+ else:
367
+ self.optimizations_applied.append("gpu_only")
368
+
369
+ # Try torch.compile
370
+ if self._try_enable_torch_compile():
371
+ # Compile the UNet for better performance
372
+ try:
373
+ self.pipeline.unet = torch.compile(self.pipeline.unet, mode="reduce-overhead")
374
+ self.optimizations_applied.append("torch_compile")
375
+ except Exception as e:
376
+ logger.warning(f"Could not compile UNet: {e}")
377
  else:
378
+ self.optimizations_applied.append("cpu_only")
 
 
 
 
 
 
 
 
379
 
380
+ # Enable VAE slicing for memory efficiency
381
+ self.pipeline.enable_vae_slicing()
382
+ self.optimizations_applied.append("vae_slicing")
383
 
384
+ logger.info(f"Model loaded successfully with optimizations: {', '.join(self.optimizations_applied)}")
385
+ return True
 
 
 
 
 
 
 
 
 
 
386
 
387
+ except Exception as e:
388
+ logger.error(f"Failed to load model: {e}")
389
+ self.pipeline = None
390
+ return False
391
 
392
  class ImageProcessor:
393
+ """Process image generation and transformation"""
394
 
395
+ def __init__(self, model_manager: ModelManager, cache_manager: CacheManager,
396
+ system_monitor: SystemMonitor):
397
  self.model_manager = model_manager
398
  self.cache_manager = cache_manager
399
+ self.system_monitor = system_monitor
400
+
401
+ def generate_image(self, prompt: str, negative_prompt: str = "", style: str = "None",
402
+ aspect_ratio: str = "1:1", guidance_scale: float = 7.5,
403
+ inference_steps: int = 4, seed: int = -1,
404
+ use_cache: bool = True) -> GenerationResult:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  """Generate image with comprehensive error handling"""
 
406
  start_time = time.time()
407
 
408
+ # Check cache first
409
+ if use_cache:
410
+ cached_image = self.cache_manager.get(
411
+ prompt, negative_prompt, style, aspect_ratio,
412
+ guidance_scale, inference_steps, seed
413
+ )
414
+ if cached_image:
415
+ result = GenerationResult(
416
+ success=True,
417
+ image=cached_image,
418
+ generation_time=time.time() - start_time,
419
+ cache_hit=True
420
+ )
421
+ self.system_monitor.log_generation(True, result.generation_time)
422
  return result
423
 
424
+ # Validate model
425
+ if not self.model_manager.load_model():
426
+ return GenerationResult(
427
+ success=False,
428
+ error="Model not loaded",
429
+ error_code=1001,
430
+ generation_time=time.time() - start_time
431
+ )
432
 
433
+ try:
434
+ # Set seed
435
+ if seed != -1:
436
+ torch.manual_seed(seed)
437
+ if torch.cuda.is_available():
438
+ torch.cuda.manual_seed_all(seed)
439
+
440
+ # Get dimensions
441
+ width, height = ASPECT_RATIOS.get(aspect_ratio, ASPECT_RATIOS[DEFAULT_ASPECT_RATIO])
442
+
443
+ # Apply style to prompt
444
+ if style != "None":
445
+ style_prompt = f"{prompt}, {style.lower()} style"
446
+ else:
447
+ style_prompt = prompt
448
 
449
+ # Generate
450
+ logger.info(f"Generating image: {style_prompt[:50]}...")
451
+
452
+ # Adjust parameters for quality/speed balance
453
+ if inference_steps < 4:
454
+ inference_steps = 4 # Minimum for quality
455
+ elif inference_steps > 50:
456
+ inference_steps = 50 # Maximum for efficiency
457
+
458
+ with torch.cuda.amp.autocast() if self.model_manager.device == "cuda" else torch.no_grad():
459
+ result_image = self.model_manager.pipeline(
460
+ prompt=style_prompt,
461
+ negative_prompt=negative_prompt,
462
+ num_inference_steps=inference_steps,
463
+ guidance_scale=guidance_scale,
464
+ width=width,
465
+ height=height,
466
+ num_images_per_prompt=1,
467
+ generator=torch.Generator(device=self.model_manager.device).manual_seed(seed) if seed != -1 else None
468
+ ).images[0]
469
 
470
+ # Cache the result
471
+ if use_cache:
472
+ self.cache_manager.put(
473
+ prompt, negative_prompt, style, aspect_ratio,
474
+ guidance_scale, inference_steps, seed, result_image
475
+ )
476
 
477
+ generation_time = time.time() - start_time
478
+ result = GenerationResult(
479
+ success=True,
480
+ image=result_image,
481
+ generation_time=generation_time,
482
+ cache_hit=False,
483
+ optimization_used=self.model_manager.optimizations_applied.copy()
 
 
 
 
484
  )
485
 
486
+ self.system_monitor.log_generation(True, generation_time)
487
+ logger.info(f"Image generated successfully in {generation_time:.2f}s")
488
+ return result
 
 
 
 
 
489
 
490
  except torch.cuda.OutOfMemoryError:
491
+ logger.error("CUDA out of memory")
492
+ # Clear cache and try to free memory
493
+ self.cache_manager.clear()
494
+ gc.collect()
495
+ if torch.cuda.is_available():
496
+ torch.cuda.empty_cache()
497
+
498
+ self.system_monitor.log_generation(False, time.time() - start_time)
499
+ return GenerationResult(
500
+ success=False,
501
+ error="GPU out of memory. Try smaller image size or restart space.",
502
+ error_code=3001,
503
+ generation_time=time.time() - start_time
504
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
 
506
+ except Exception as e:
507
+ logger.error(f"Generation failed: {e}")
508
+ self.system_monitor.log_generation(False, time.time() - start_time)
509
+ return GenerationResult(
510
+ success=False,
511
+ error=str(e),
512
+ error_code=1002,
513
+ generation_time=time.time() - start_time
514
+ )
515
 
516
+ def transform_image(self, source_image: Image.Image, prompt: str,
517
+ negative_prompt: str = "", strength: float = 0.7,
518
+ guidance_scale: float = 7.5, inference_steps: int = 4,
519
+ seed: int = -1) -> GenerationResult:
520
+ """Transform existing image"""
521
+ start_time = time.time()
522
 
523
+ # Validate model
524
+ if not self.model_manager.load_model():
525
+ return GenerationResult(
526
+ success=False,
527
+ error="Model not loaded",
528
+ error_code=1001,
529
+ generation_time=time.time() - start_time
530
+ )
531
 
532
+ try:
533
+ # Set seed
534
+ if seed != -1:
535
+ torch.manual_seed(seed)
536
+ if torch.cuda.is_available():
537
+ torch.cuda.manual_seed_all(seed)
538
 
539
+ # Prepare image
540
+ source_image = source_image.convert("RGB")
541
 
542
  # Transform
543
+ logger.info(f"Transforming image with prompt: {prompt[:50]}...")
544
+
545
+ with torch.cuda.amp.autocast() if self.model_manager.device == "cuda" else torch.no_grad():
546
+ result_image = self.model_manager.pipeline(
547
+ image=source_image,
548
+ prompt=prompt,
549
+ negative_prompt=negative_prompt,
550
+ strength=strength,
551
+ num_inference_steps=inference_steps,
552
+ guidance_scale=guidance_scale,
553
+ generator=torch.Generator(device=self.model_manager.device).manual_seed(seed) if seed != -1 else None
554
+ ).images[0]
555
+
556
+ generation_time = time.time() - start_time
557
+ result = GenerationResult(
558
+ success=True,
559
+ image=result_image,
560
+ generation_time=generation_time,
561
+ optimization_used=self.model_manager.optimizations_applied.copy()
562
  )
563
 
564
+ self.system_monitor.log_generation(True, generation_time)
565
+ logger.info(f"Image transformed successfully in {generation_time:.2f}s")
566
+ return result
 
 
 
 
 
567
 
 
 
 
 
568
  except Exception as e:
569
+ logger.error(f"Transform failed: {e}")
570
+ self.system_monitor.log_generation(False, time.time() - start_time)
571
+ return GenerationResult(
572
+ success=False,
573
+ error=str(e),
574
+ error_code=1003,
575
+ generation_time=time.time() - start_time
576
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
 
578
+ # Create global instances
 
579
  model_manager = ModelManager()
580
+ cache_manager = CacheManager()
581
+ system_monitor = SystemMonitor()
582
+ image_processor = ImageProcessor(model_manager, cache_manager, system_monitor)
583
+
584
+ # Preload model if possible
585
+ model_manager.load_model()
586
+
587
+ def format_system_stats(stats: Dict[str, Any]) -> str:
588
+ """Format system stats for display"""
589
+ html = "<div class='system-monitor'>"
590
+ html += "<h4>🖥️ System Resources</h4>"
591
+ html += f"<strong>CPU:</strong> {stats['cpu_percent']:.1f}%<br>"
592
+ html += f"<strong>Memory:</strong> {stats['memory_percent']:.1f}%<br>"
593
+ html += f"<strong>Disk:</strong> {stats['disk_percent']:.1f}%<br>"
594
+
595
+ if stats['gpu_available']:
596
+ gpu_mem = stats.get('gpu_memory', {})
597
+ if gpu_mem:
598
+ html += f"<strong>GPU Memory:</strong> {gpu_mem.get('allocated', 0):.1f}GB / {gpu_mem.get('total', 0):.1f}GB<br>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  else:
600
+ html += "<strong>GPU:</strong> Available<br>"
601
+ else:
602
+ html += "<strong>GPU:</strong> Not Available<br>"
603
+
604
+ html += "<h4>📊 Performance Metrics</h4>"
605
+ html += f"<strong>Uptime:</strong> {stats['uptime_str']}<br>"
606
+ html += f"<strong>Generations:</strong> {stats['generation_count']}<br>"
607
+ html += f"<strong>Success Rate:</strong> {stats['success_rate']:.1f}%<br>"
608
+ html += f"<strong>Avg Time:</strong> {stats['avg_generation_time']}s<br>"
609
+ html += f"<strong>Gen/Min:</strong> {stats['generations_per_minute']:.1f}<br>"
610
+
611
+ cache_stats = cache_manager.get_stats()
612
+ html += "<h4>💾 Cache</h4>"
613
+ html += f"<strong>Size:</strong> {cache_stats['size']}/{cache_stats['max_size']}<br>"
614
+ html += f"<strong>Usage:</strong> {cache_stats['usage_percent']:.1f}%<br>"
615
+
616
+ if model_manager.optimizations_applied:
617
+ html += "<h4>⚡ Active Optimizations</h4>"
618
+ html += f"{' • '.join(model_manager.optimizations_applied)}<br>"
619
+
620
+ html += "</div>"
621
+ return html
622
+
623
+ def generate_image_wrapper(prompt: str, negative_prompt: str, style: str,
624
+ aspect_ratio: str, guidance_scale: float,
625
+ inference_steps: int, seed: int, progress=gr.Progress()) -> Tuple[Optional[Image.Image], str]:
626
+ """Wrapper for image generation with progress tracking"""
627
+ progress(0.1, desc="Preparing generation...")
628
+
629
+ result = image_processor.generate_image(
630
+ prompt=prompt,
631
+ negative_prompt=negative_prompt,
632
+ style=style,
633
+ aspect_ratio=aspect_ratio,
634
+ guidance_scale=guidance_scale,
635
+ inference_steps=int(inference_steps),
636
+ seed=int(seed)
637
+ )
638
 
639
+ progress(0.9, desc="Finalizing...")
640
+
641
+ if result.success:
642
+ message = f" Generated in {result.generation_time:.2f}s"
643
+ if result.cache_hit:
644
+ message += " (from cache)"
645
+ return result.image, message
646
+ else:
647
+ error_msg = f"❌ Error {result.error_code}: {result.error}"
648
+ return None, error_msg
649
+
650
+ def transform_image_wrapper(source_image: Image.Image, prompt: str,
651
+ negative_prompt: str, strength: float,
652
+ guidance_scale: float, inference_steps: int,
653
+ seed: int, progress=gr.Progress()) -> Tuple[Optional[Image.Image], str]:
654
+ """Wrapper for image transformation with progress tracking"""
655
+ if source_image is None:
656
+ return None, "❌ Please upload an image"
657
+
658
+ progress(0.1, desc="Preparing transformation...")
659
+
660
+ result = image_processor.transform_image(
661
+ source_image=source_image,
662
+ prompt=prompt,
663
+ negative_prompt=negative_prompt,
664
+ strength=strength,
665
+ guidance_scale=guidance_scale,
666
+ inference_steps=int(inference_steps),
667
+ seed=int(seed)
668
+ )
669
 
670
+ progress(0.9, desc="Finalizing...")
671
 
672
+ if result.success:
673
+ return result.image, f" Transformed in {result.generation_time:.2f}s"
674
+ else:
675
+ return None, f"❌ Error {result.error_code}: {result.error}"
676
 
677
+ # Build Gradio interface
678
+ def build_interface():
679
+ """Build the Gradio interface"""
680
  with gr.Blocks(
681
+ title="Z Image Turbo",
682
+ css=CUSTOM_CSS,
683
+ theme=gr.themes.Soft()
684
  ) as demo:
685
+ gr.Markdown("# 🎨 Z Image Turbo")
686
+ gr.Markdown("High-performance image generation and transformation")
 
 
 
 
 
 
 
 
687
 
688
  with gr.Tabs():
689
  # Generation Tab
690
+ with gr.TabItem(" Generate"):
691
  with gr.Row():
692
+ with gr.Column(scale=2):
693
+ prompt_input = gr.Textbox(
694
  label="Prompt",
695
  placeholder="Describe the image you want to generate...",
696
+ lines=3
 
697
  )
698
+ negative_prompt_input = gr.Textbox(
699
+ label="Negative Prompt",
700
+ placeholder="What you don't want in the image...",
701
+ lines=2,
702
+ value=""
 
 
 
 
 
 
 
703
  )
704
 
705
  with gr.Row():
706
+ style_dropdown = gr.Dropdown(
707
+ label="Style",
708
+ choices=STYLE_PRESETS,
709
+ value="None"
 
 
 
710
  )
711
+ aspect_ratio_dropdown = gr.Dropdown(
712
+ label="Aspect Ratio",
713
+ choices=list(ASPECT_RATIOS.keys()),
714
+ value=DEFAULT_ASPECT_RATIO
 
 
 
 
715
  )
716
 
717
  with gr.Row():
718
+ guidance_scale = gr.Slider(
719
+ label="Guidance Scale",
720
+ minimum=1.0,
721
+ maximum=20.0,
722
+ value=7.5,
723
+ step=0.5
724
  )
725
+ inference_steps = gr.Slider(
726
+ label="Inference Steps",
727
+ minimum=1,
728
+ maximum=50,
729
+ value=4,
730
+ step=1
731
  )
732
 
733
+ seed_input = gr.Number(
734
+ label="Seed (-1 for random)",
735
+ value=-1,
736
+ precision=0
737
+ )
738
+
739
+ generate_btn = gr.Button(
740
  "🚀 Generate",
741
  variant="primary",
742
+ elem_classes=["generate-btn"]
 
743
  )
744
 
745
+ with gr.Column(scale=1):
746
+ output_image = gr.Image(
747
  label="Generated Image",
748
+ type="pil"
 
 
 
 
 
749
  )
750
+ output_info = gr.Textbox(
751
+ label="Information",
752
+ interactive=False
 
 
 
753
  )
754
 
755
+ generate_btn.click(
756
+ fn=generate_image_wrapper,
 
 
 
 
 
 
 
757
  inputs=[
758
+ prompt_input,
759
+ negative_prompt_input,
760
+ style_dropdown,
761
+ aspect_ratio_dropdown,
762
+ guidance_scale,
763
+ inference_steps,
764
+ seed_input
765
  ],
766
+ outputs=[output_image, output_info]
767
  )
768
 
769
  # Transform Tab
770
+ with gr.TabItem("🔄 Transform"):
 
 
 
 
 
771
  with gr.Row():
772
+ with gr.Column(scale=2):
773
+ source_image_input = gr.Image(
774
+ label="Source Image",
775
+ type="pil"
 
776
  )
777
+ transform_prompt = gr.Textbox(
 
778
  label="Transform Prompt",
779
  placeholder="Describe how to transform the image...",
780
+ lines=3
781
  )
782
+ transform_negative_prompt = gr.Textbox(
783
+ label="Negative Prompt",
784
+ placeholder="What to avoid in the transformation...",
785
+ lines=2,
786
+ value=""
787
  )
788
 
789
  with gr.Row():
790
+ transform_strength = gr.Slider(
791
+ label="Transform Strength",
792
  minimum=0.0,
793
  maximum=1.0,
794
+ value=0.7,
795
+ step=0.1
 
 
796
  )
797
+ transform_guidance = gr.Slider(
798
+ label="Guidance Scale",
799
+ minimum=1.0,
800
+ maximum=20.0,
801
+ value=7.5,
802
+ step=0.5
 
803
  )
804
 
805
+ transform_steps = gr.Slider(
806
+ label="Inference Steps",
807
+ minimum=1,
808
+ maximum=50,
809
+ value=4,
810
+ step=1
811
+ )
 
 
 
812
 
813
+ transform_seed = gr.Number(
814
+ label="Seed (-1 for random)",
815
+ value=-1,
816
+ precision=0
817
  )
818
 
819
+ transform_btn = gr.Button(
820
+ "🔄 Transform",
821
+ variant="primary"
 
 
 
 
822
  )
823
 
824
+ with gr.Column(scale=1):
825
+ transformed_image = gr.Image(
826
+ label="Transformed Image",
827
+ type="pil"
828
+ )
829
+ transform_info = gr.Textbox(
830
+ label="Information",
831
+ interactive=False
832
  )
833
 
834
+ transform_btn.click(
835
+ fn=transform_image_wrapper,
 
836
  inputs=[
837
+ source_image_input,
838
+ transform_prompt,
839
+ transform_negative_prompt,
840
+ transform_strength,
841
+ transform_guidance,
842
+ transform_steps,
843
+ transform_seed
844
  ],
845
+ outputs=[transformed_image, transform_info]
846
  )
847
 
848
  # System Monitor Tab
849
+ with gr.TabItem("📊 System Monitor"):
850
+ system_stats = gr.HTML(
851
+ value=format_system_stats(system_monitor.get_stats()),
852
+ label="System Statistics"
853
+ )
854
+ refresh_btn = gr.Button("🔄 Refresh")
855
+ clear_cache_btn = gr.Button("🗑️ Clear Cache")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
856
 
857
  refresh_btn.click(
858
+ fn=lambda: format_system_stats(system_monitor.get_stats()),
859
+ outputs=[system_stats]
 
 
 
860
  )
861
 
862
+ clear_cache_btn.click(
863
+ fn=lambda: (cache_manager.clear(), format_system_stats(system_monitor.get_stats()))[1],
864
+ outputs=[system_stats]
 
 
 
 
 
865
  )
866
 
867
+ # Footer
868
+ gr.HTML("""
869
+ <div class="footer">
870
+ <p>🚀 Z Image Turbo - Production Edition v2.0.0</p>
871
+ <p>Created with ❤️ by AI Agent Framework Specialist</p>
872
+ </div>
873
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
874
 
875
  return demo
876
 
877
+ # Create and launch the demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
878
  if __name__ == "__main__":
879
+ logger.info("Starting Z Image Turbo application...")
880
 
881
+ demo = build_interface()
 
 
 
 
 
 
 
 
882
 
883
  # Launch with optimizations
884
  demo.launch(
885
  share=False,
886
  show_error=True,
 
887
  max_threads=40,
888
+ prevent_thread_lock=False,
889
+ enable_queue=True
890
+ )