MySafeCode commited on
Commit
404123e
·
verified ·
1 Parent(s): 9111e8e

Upload app2.py

Browse files
Files changed (1) hide show
  1. app2.py +731 -0
app2.py ADDED
@@ -0,0 +1,731 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import json
4
+ import os
5
+ import time
6
+ from PIL import Image
7
+ import io
8
+ from datetime import datetime
9
+ import numpy as np
10
+
11
+ # Get API key from Hugging Face secrets
12
+ def get_api_key():
13
+ return os.environ.get("PIXAZO_API_KEY")
14
+
15
+ # Call Pixazo API
16
+ def call_pixazo_api(prompt, num_steps=4, seed=None, height=512, width=512,
17
+ style_preset=None, guidance_scale=None):
18
+
19
+ api_key = get_api_key()
20
+
21
+ if not api_key:
22
+ return None, {
23
+ "error": "API key not configured",
24
+ "instructions": "Please set PIXAZO_API_KEY in Hugging Face Space secrets",
25
+ "timestamp": datetime.now().isoformat()
26
+ }
27
+
28
+ url = "https://gateway.pixazo.ai/flux-1-schnell/v1/getData"
29
+
30
+ headers = {
31
+ 'Content-Type': 'application/json',
32
+ 'Cache-Control': 'no-cache',
33
+ 'Ocp-Apim-Subscription-Key': api_key,
34
+ }
35
+
36
+ # Prepare request body
37
+ body = {
38
+ "prompt": prompt,
39
+ "num_steps": num_steps,
40
+ "height": height,
41
+ "width": width,
42
+ }
43
+
44
+ if seed and seed > 0:
45
+ body["seed"] = seed
46
+ if style_preset and style_preset != "none":
47
+ body["style_preset"] = style_preset
48
+ if guidance_scale:
49
+ body["guidance_scale"] = guidance_scale
50
+
51
+ try:
52
+ print(f"🌐 Sending request to Pixazo API...")
53
+
54
+ response = requests.post(
55
+ url,
56
+ headers=headers,
57
+ json=body,
58
+ timeout=120
59
+ )
60
+
61
+ print(f"✅ Response status: {response.status_code}")
62
+
63
+ if response.status_code == 200:
64
+ result = response.json()
65
+
66
+ if "output" in result:
67
+ image_url = result["output"]
68
+
69
+ # Download the image from the URL
70
+ try:
71
+ print(f"⬇️ Downloading image from URL...")
72
+ img_response = requests.get(image_url, timeout=30, headers={
73
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
74
+ })
75
+
76
+ if img_response.status_code == 200:
77
+ image = Image.open(io.BytesIO(img_response.content))
78
+
79
+ # Convert to RGB if necessary
80
+ if image.mode in ('RGBA', 'LA', 'P'):
81
+ image = image.convert('RGB')
82
+
83
+ return image, {
84
+ "success": True,
85
+ "status_code": 200,
86
+ "image_url_preview": image_url[:80] + "..." if len(image_url) > 80 else image_url,
87
+ "image_size": f"{image.size[0]}x{image.size[1]}",
88
+ "timestamp": datetime.now().isoformat()
89
+ }
90
+ else:
91
+ return None, {
92
+ "error": f"Failed to download image: HTTP {img_response.status_code}",
93
+ "image_url": image_url[:80] + "..." if len(image_url) > 80 else image_url,
94
+ "download_status": img_response.status_code,
95
+ "timestamp": datetime.now().isoformat()
96
+ }
97
+
98
+ except Exception as e:
99
+ return None, {
100
+ "error": f"Error downloading image: {str(e)}",
101
+ "image_url": image_url[:80] + "..." if len(image_url) > 80 else image_url,
102
+ "timestamp": datetime.now().isoformat()
103
+ }
104
+ else:
105
+ return None, {
106
+ "error": "Response missing 'output' field",
107
+ "response_keys": list(result.keys()),
108
+ "timestamp": datetime.now().isoformat()
109
+ }
110
+ else:
111
+ try:
112
+ error_data = response.json()
113
+ error_msg = error_data.get("error", str(error_data))
114
+ except:
115
+ error_msg = response.text[:200] if response.text else "No error message"
116
+
117
+ return None, {
118
+ "error": f"API returned status {response.status_code}: {error_msg}",
119
+ "status_code": response.status_code,
120
+ "timestamp": datetime.now().isoformat()
121
+ }
122
+
123
+ except requests.exceptions.Timeout:
124
+ return None, {
125
+ "error": "Request timed out after 120 seconds",
126
+ "suggestion": "Try reducing image size or number of steps",
127
+ "timestamp": datetime.now().isoformat()
128
+ }
129
+ except requests.exceptions.RequestException as e:
130
+ return None, {
131
+ "error": f"Network error: {str(e)}",
132
+ "timestamp": datetime.now().isoformat()
133
+ }
134
+ except Exception as e:
135
+ return None, {
136
+ "error": f"Unexpected error: {str(e)}",
137
+ "timestamp": datetime.now().isoformat()
138
+ }
139
+
140
+ # Batch generate images
141
+ def generate_images(prompt, num_steps, seed, height, width, style_preset,
142
+ num_images, guidance_scale):
143
+
144
+ images = []
145
+ all_results = []
146
+
147
+ for i in range(num_images):
148
+ try:
149
+ current_seed = seed + i if seed > 0 else 0
150
+
151
+ image, result = call_pixazo_api(
152
+ prompt=prompt,
153
+ num_steps=num_steps,
154
+ seed=current_seed,
155
+ height=height,
156
+ width=width,
157
+ style_preset=style_preset,
158
+ guidance_scale=guidance_scale
159
+ )
160
+
161
+ if image:
162
+ images.append(image)
163
+
164
+ all_results.append(result)
165
+
166
+ status_text = f"Generated {i+1}/{num_images} images"
167
+ if "error" in result:
168
+ status_text = f"Image {i+1} error: {result['error'][:50]}..."
169
+
170
+ yield images if images else None, status_text, json.dumps(result, indent=2)
171
+
172
+ if i < num_images - 1:
173
+ time.sleep(1)
174
+
175
+ except Exception as e:
176
+ error_result = {"error": str(e), "image_index": i, "timestamp": datetime.now().isoformat()}
177
+ all_results.append(error_result)
178
+ yield images if images else None, f"Exception on image {i+1}: {str(e)[:50]}", json.dumps(error_result, indent=2)
179
+ break
180
+
181
+ # Final summary
182
+ success_count = len(images)
183
+
184
+ if success_count == 0:
185
+ final_status = f"❌ No images generated ({success_count}/{num_images} successful)"
186
+ gallery_val = None
187
+ elif success_count < num_images:
188
+ final_status = f"⚠️ Partial success: {success_count}/{num_images} images"
189
+ gallery_val = images
190
+ else:
191
+ final_status = f"🎉 Success! All {success_count} images generated"
192
+ gallery_val = images
193
+
194
+ summary = {
195
+ "summary": {
196
+ "total_attempts": num_images,
197
+ "successful": success_count,
198
+ "failed": num_images - success_count,
199
+ "timestamp": datetime.now().isoformat()
200
+ },
201
+ "parameters_used": {
202
+ "prompt_length": len(prompt),
203
+ "steps": num_steps,
204
+ "resolution": f"{width}x{height}",
205
+ "style": style_preset
206
+ }
207
+ }
208
+
209
+ yield gallery_val, final_status, json.dumps(summary, indent=2)
210
+
211
+ # Custom CSS for the beautiful UI
212
+ custom_css = """
213
+ :root {
214
+ --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
215
+ --primary-color: #4f46e5;
216
+ --success-color: #10b981;
217
+ --error-color: #ef4444;
218
+ --warning-color: #f59e0b;
219
+ }
220
+
221
+ .gradio-container {
222
+ max-width: 1200px !important;
223
+ margin: 0 auto !important;
224
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
225
+ }
226
+
227
+ .header-container {
228
+ background: var(--primary-gradient);
229
+ color: white;
230
+ padding: 40px 30px;
231
+ border-radius: 16px;
232
+ margin-bottom: 30px;
233
+ text-align: center;
234
+ box-shadow: 0 20px 60px rgba(102, 126, 234, 0.3);
235
+ }
236
+
237
+ .header-title {
238
+ font-size: 2.8em;
239
+ font-weight: 700;
240
+ margin-bottom: 10px;
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ gap: 15px;
245
+ }
246
+
247
+ .header-subtitle {
248
+ font-size: 1.2em;
249
+ opacity: 0.9;
250
+ max-width: 600px;
251
+ margin: 0 auto;
252
+ }
253
+
254
+ .api-status {
255
+ padding: 16px 20px;
256
+ border-radius: 12px;
257
+ margin-bottom: 25px;
258
+ font-weight: 600;
259
+ font-size: 1.1em;
260
+ border-left: 6px solid;
261
+ }
262
+
263
+ .api-status-success {
264
+ background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
265
+ color: #065f46;
266
+ border-left-color: var(--success-color);
267
+ }
268
+
269
+ .api-status-error {
270
+ background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
271
+ color: #7f1d1d;
272
+ border-left-color: var(--error-color);
273
+ }
274
+
275
+ .input-section {
276
+ background: white;
277
+ padding: 30px;
278
+ border-radius: 16px;
279
+ border: 1px solid #e5e7eb;
280
+ box-shadow: 0 4px 20px rgba(0,0,0,0.05);
281
+ margin-bottom: 25px;
282
+ }
283
+
284
+ .section-title {
285
+ color: #333;
286
+ font-size: 1.4em;
287
+ font-weight: 600;
288
+ margin-bottom: 20px;
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 10px;
292
+ }
293
+
294
+ .example-card {
295
+ background: #f8fafc;
296
+ padding: 20px;
297
+ border-radius: 12px;
298
+ border: 2px solid #e2e8f0;
299
+ margin: 12px 0;
300
+ cursor: pointer;
301
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
302
+ }
303
+
304
+ .example-card:hover {
305
+ background: #e2e8f0;
306
+ transform: translateY(-4px);
307
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
308
+ border-color: var(--primary-color);
309
+ }
310
+
311
+ .example-title {
312
+ color: var(--primary-color);
313
+ font-weight: 600;
314
+ font-size: 1.1em;
315
+ margin-bottom: 8px;
316
+ }
317
+
318
+ .example-desc {
319
+ color: #666;
320
+ font-size: 0.95em;
321
+ line-height: 1.4;
322
+ }
323
+
324
+ .generate-btn {
325
+ background: var(--primary-gradient) !important;
326
+ color: white !important;
327
+ font-weight: 700 !important;
328
+ padding: 18px 40px !important;
329
+ font-size: 1.2em !important;
330
+ border-radius: 12px !important;
331
+ border: none !important;
332
+ transition: all 0.3s ease !important;
333
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4) !important;
334
+ }
335
+
336
+ .generate-btn:hover {
337
+ transform: translateY(-3px) !important;
338
+ box-shadow: 0 15px 35px rgba(102, 126, 234, 0.5) !important;
339
+ }
340
+
341
+ .secondary-btn {
342
+ background: #f1f5f9 !important;
343
+ color: #475569 !important;
344
+ border: 2px solid #e2e8f0 !important;
345
+ }
346
+
347
+ .param-group {
348
+ background: #f9fafb;
349
+ padding: 20px;
350
+ border-radius: 12px;
351
+ margin-bottom: 20px;
352
+ border: 1px solid #e5e7eb;
353
+ }
354
+
355
+ .param-label {
356
+ font-weight: 600;
357
+ color: #374151;
358
+ margin-bottom: 8px;
359
+ display: block;
360
+ }
361
+
362
+ .gallery-container {
363
+ background: white;
364
+ padding: 25px;
365
+ border-radius: 16px;
366
+ border: 1px solid #e5e7eb;
367
+ box-shadow: 0 4px 20px rgba(0,0,0,0.05);
368
+ }
369
+
370
+ .status-box {
371
+ background: #f8fafc;
372
+ padding: 20px;
373
+ border-radius: 12px;
374
+ border-left: 5px solid var(--primary-color);
375
+ margin-bottom: 20px;
376
+ font-size: 1.1em;
377
+ }
378
+
379
+ .footer {
380
+ text-align: center;
381
+ color: #666;
382
+ font-size: 0.9em;
383
+ margin-top: 40px;
384
+ padding-top: 20px;
385
+ border-top: 1px solid #e5e7eb;
386
+ }
387
+
388
+ .tab-button {
389
+ font-weight: 600 !important;
390
+ padding: 12px 24px !important;
391
+ }
392
+
393
+ .dark .input-section, .dark .gallery-container {
394
+ background: #1f2937;
395
+ border-color: #374151;
396
+ }
397
+
398
+ .dark .example-card {
399
+ background: #374151;
400
+ border-color: #4b5563;
401
+ }
402
+
403
+ .dark .param-group {
404
+ background: #374151;
405
+ border-color: #4b5563;
406
+ }
407
+ """
408
+
409
+ # Create the Gradio interface with Gradio 6.x
410
+ with gr.Blocks(title="Pixazo Image Generator", css=custom_css) as demo:
411
+
412
+ # Custom header
413
+ gr.HTML("""
414
+ <div class="header-container">
415
+ <div class="header-title">
416
+ 🎨 Pixazo Image Generator
417
+ </div>
418
+ <div class="header-subtitle">
419
+ Generate stunning AI images with FLUX-1 Schnell model
420
+ </div>
421
+ </div>
422
+ """)
423
+
424
+ # API Status
425
+ api_key = get_api_key()
426
+ if api_key:
427
+ gr.HTML(f"""
428
+ <div class="api-status api-status-success">
429
+ ✅ API Status: Connected • Key length: {len(api_key)} characters
430
+ </div>
431
+ """)
432
+ else:
433
+ gr.HTML("""
434
+ <div class="api-status api-status-error">
435
+ ❌ API Status: Not Configured
436
+ <br><small>Go to Space Settings → Repository secrets → Add: <code>PIXAZO_API_KEY</code> = your API key</small>
437
+ </div>
438
+ """)
439
+
440
+ with gr.Row(equal_height=False):
441
+ # Left Column - Inputs
442
+ with gr.Column(scale=1, min_width=450):
443
+ # Prompt Section
444
+ with gr.Group(elem_classes="input-section"):
445
+ gr.Markdown("""
446
+ <div class="section-title">
447
+ ✨ Image Description
448
+ </div>
449
+ """)
450
+ prompt = gr.Textbox(
451
+ label="",
452
+ placeholder="Describe the image you want to generate in detail...",
453
+ lines=4,
454
+ show_label=False,
455
+ container=False
456
+ )
457
+
458
+ # Parameters Section
459
+ with gr.Group(elem_classes="input-section"):
460
+ gr.Markdown("""
461
+ <div class="section-title">
462
+ ⚙️ Generation Parameters
463
+ </div>
464
+ """)
465
+
466
+ with gr.Row():
467
+ style_preset = gr.Dropdown(
468
+ label="🎭 Style Preset",
469
+ choices=[
470
+ "none", "cyberpunk", "fantasy", "anime", "photographic",
471
+ "digital-art", "comic", "3d-model", "pixel-art",
472
+ "isometric", "watercolor", "oil-painting", "sketch",
473
+ "cinematic", "sci-fi", "renaissance", "impressionist"
474
+ ],
475
+ value="none",
476
+ info="Select an art style"
477
+ )
478
+
479
+ with gr.Row():
480
+ guidance_scale = gr.Slider(
481
+ label="🎯 Guidance Scale",
482
+ minimum=1.0,
483
+ maximum=20.0,
484
+ value=7.5,
485
+ step=0.5,
486
+ info="Higher values = more prompt adherence"
487
+ )
488
+
489
+ with gr.Row():
490
+ num_steps = gr.Slider(
491
+ label="⚡ Inference Steps",
492
+ minimum=1,
493
+ maximum=100,
494
+ value=4,
495
+ step=1,
496
+ info="More steps = better quality but slower"
497
+ )
498
+
499
+ seed = gr.Number(
500
+ label="🌱 Seed",
501
+ value=42,
502
+ minimum=0,
503
+ maximum=999999,
504
+ info="0 for random, fixed for reproducible results"
505
+ )
506
+
507
+ with gr.Row():
508
+ width = gr.Slider(
509
+ label="📏 Width",
510
+ minimum=256,
511
+ maximum=2048,
512
+ value=512,
513
+ step=64,
514
+ info="Image width in pixels"
515
+ )
516
+
517
+ height = gr.Slider(
518
+ label="📐 Height",
519
+ minimum=256,
520
+ maximum=2048,
521
+ value=512,
522
+ step=64,
523
+ info="Image height in pixels"
524
+ )
525
+
526
+ num_images = gr.Slider(
527
+ label="🖼️ Number of Images",
528
+ minimum=1,
529
+ maximum=8,
530
+ value=1,
531
+ step=1,
532
+ info="Generate multiple variations"
533
+ )
534
+
535
+ # Action Buttons
536
+ with gr.Row():
537
+ generate_btn = gr.Button(
538
+ "✨ Generate Images",
539
+ variant="primary",
540
+ scale=3,
541
+ elem_classes="generate-btn"
542
+ )
543
+ clear_btn = gr.Button(
544
+ "🗑️ Clear",
545
+ variant="secondary",
546
+ scale=1,
547
+ elem_classes="secondary-btn"
548
+ )
549
+
550
+ # Examples Section
551
+ with gr.Group(elem_classes="input-section"):
552
+ gr.Markdown("""
553
+ <div class="section-title">
554
+ 💡 Example Prompts
555
+ </div>
556
+ <p style="color: #666; margin-bottom: 15px;">Click any example to try it:</p>
557
+ """)
558
+
559
+ examples = [
560
+ ("Cyberpunk Cityscape", "A futuristic cyberpunk city at night with neon signs, flying cars, and rain reflections", "cyberpunk"),
561
+ ("Fantasy Dragon", "A majestic dragon perched on a mountain peak overlooking a magical kingdom at sunset", "fantasy"),
562
+ ("Anime Character", "A cute anime girl with magical powers in a cherry blossom garden, studio ghibli style", "anime"),
563
+ ("Space Explorer", "An astronaut on Mars with detailed reflection in visor, photorealistic", "photographic"),
564
+ ("Isometric Office", "Cozy isometric bedroom with plants, books, and warm lighting, detailed 3d render", "isometric")
565
+ ]
566
+
567
+ for title, desc, style in examples:
568
+ with gr.Row():
569
+ gr.HTML(f"""
570
+ <div class="example-card">
571
+ <div class="example-title">{title}</div>
572
+ <div class="example-desc">{desc}</div>
573
+ </div>
574
+ """)
575
+
576
+ def use_example(p=desc, s=style):
577
+ return p, s
578
+
579
+ # Make the entire card clickable
580
+ demo.load(
581
+ fn=None,
582
+ inputs=None,
583
+ outputs=None,
584
+ _js=f"""
585
+ (function() {{
586
+ setTimeout(() => {{
587
+ const cards = document.querySelectorAll('.example-card');
588
+ cards.forEach(card => {{
589
+ card.addEventListener('click', () => {{
590
+ // This would need JavaScript to update the inputs
591
+ // For now, we'll use the Python handler
592
+ }});
593
+ }});
594
+ }}, 1000);
595
+ }})();
596
+ """
597
+ )
598
+
599
+ # Add invisible button for the example
600
+ example_btn = gr.Button("Use", visible=False)
601
+ example_btn.click(
602
+ lambda p=desc, s=style: (p, s),
603
+ outputs=[prompt, style_preset]
604
+ )
605
+
606
+ # Right Column - Outputs
607
+ with gr.Column(scale=2, min_width=650):
608
+ # Status Display
609
+ gr.HTML("""
610
+ <div class="section-title">
611
+ ��� Generation Results
612
+ </div>
613
+ """)
614
+
615
+ status = gr.Textbox(
616
+ label="",
617
+ value="🎯 Ready to generate images. Enter a prompt above and click Generate!",
618
+ interactive=False,
619
+ lines=2,
620
+ container=False,
621
+ elem_classes="status-box"
622
+ )
623
+
624
+ # Gallery
625
+ with gr.Group(elem_classes="gallery-container"):
626
+ gallery = gr.Gallery(
627
+ label="🎨 Generated Images",
628
+ columns=3,
629
+ height="auto",
630
+ object_fit="contain",
631
+ show_label=True,
632
+ preview=True,
633
+ allow_preview=True
634
+ )
635
+
636
+ # Tabs for additional info
637
+ with gr.Tabs():
638
+ with gr.TabItem("📈 Generation Details", elem_classes="tab-button"):
639
+ json_output = gr.JSON(
640
+ label="API Response & Metrics",
641
+ container=True
642
+ )
643
+
644
+ with gr.TabItem("🛠️ Quick Actions", elem_classes="tab-button"):
645
+ with gr.Column():
646
+ gr.Markdown("""
647
+ ### Quick Actions
648
+
649
+ **Download Options:**
650
+ - Right-click any image to save it
651
+ - Use browser's "Save image as" option
652
+
653
+ **Tips for Better Results:**
654
+ 1. **Be descriptive** - Include details about lighting, style, mood
655
+ 2. **Use specific styles** - Try different style presets
656
+ 3. **Adjust guidance** - Higher values follow prompt more closely
657
+ 4. **Experiment with seeds** - Same seed + same prompt = same image
658
+ """)
659
+
660
+ with gr.TabItem("ℹ️ About", elem_classes="tab-button"):
661
+ gr.Markdown(f"""
662
+ ### About This Application
663
+
664
+ **Version:** 1.0.0
665
+ **Gradio:** {gr.__version__}
666
+ **API Format:** `{"output": "IMAGE_URL"}`
667
+
668
+ **How It Works:**
669
+ 1. Your prompt is sent to Pixazo's FLUX-1 Schnell API
670
+ 2. API returns a JSON response with an image URL
671
+ 3. App downloads and displays the image
672
+ 4. All metadata is shown for transparency
673
+
674
+ **Credits:**
675
+ - Powered by [Pixazo AI](https://pixazo.ai)
676
+ - Built with [Gradio](https://gradio.app)
677
+ - Hosted on [Hugging Face Spaces](https://huggingface.co/spaces)
678
+
679
+ **Note:** API key must be set in Space secrets for this to work.
680
+ """)
681
+
682
+ # Footer
683
+ gr.HTML("""
684
+ <div class="footer">
685
+ <p>Built with ❤️ using Gradio • Powered by Pixazo FLUX-1 Schnell</p>
686
+ <p>Model: flux-1-schnell • Response Format: <code>{"output": "IMAGE_URL"}</code></p>
687
+ </div>
688
+ """)
689
+
690
+ # Event Handlers
691
+ def on_generate(prompt, num_steps, seed, height, width, style_preset, num_images, guidance_scale):
692
+ if not prompt.strip():
693
+ yield None, "❌ Please enter a prompt first", {"error": "No prompt provided"}
694
+ return
695
+
696
+ # Generator function that yields progress
697
+ for gallery_val, status_val, json_val in generate_images(
698
+ prompt, num_steps, seed, height, width, style_preset, num_images, guidance_scale
699
+ ):
700
+ yield gallery_val, status_val, json_val
701
+
702
+ def on_clear():
703
+ return None, "🎯 Ready to generate images. Enter a prompt above and click Generate!", None
704
+
705
+ # Connect buttons
706
+ generate_btn.click(
707
+ fn=on_generate,
708
+ inputs=[prompt, num_steps, seed, height, width, style_preset, num_images, guidance_scale],
709
+ outputs=[gallery, status, json_output]
710
+ )
711
+
712
+ clear_btn.click(
713
+ fn=on_clear,
714
+ outputs=[gallery, status, json_output]
715
+ )
716
+
717
+ # Launch the app
718
+ if __name__ == "__main__":
719
+ print("=" * 60)
720
+ print("🚀 Pixazo Image Generator")
721
+ print("🎨 Beautiful UI with Gradio 6.2.0")
722
+ print(f"📦 Gradio Version: {gr.__version__}")
723
+ print(f"🔑 API Key Configured: {'Yes' if get_api_key() else 'No'}")
724
+ print("=" * 60)
725
+
726
+ # Launch with correct parameters for Gradio 6.x
727
+ demo.launch(
728
+ server_name="0.0.0.0",
729
+ server_port=7860,
730
+ share=False
731
+ )