MySafeCode commited on
Commit
c139321
·
verified ·
1 Parent(s): 0cef13c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +319 -97
app.py CHANGED
@@ -8,15 +8,19 @@ from dotenv import load_dotenv
8
  # Load environment variables from .env file
9
  load_dotenv()
10
 
11
- # API Configuration - Hugging Face Spaces stores secrets in environment variables
12
  API_KEY = os.getenv('STABLECOG_API_KEY', '')
13
  if not API_KEY:
14
- # For local testing fallback
15
  API_KEY = "StableCogKey"
16
 
17
  API_HOST = 'https://api.stablecog.com'
18
- API_ENDPOINT = '/v1/credits'
19
- API_URL = f'{API_HOST}{API_ENDPOINT}'
 
 
 
 
 
20
 
21
  headers = {
22
  'Authorization': f'Bearer {API_KEY}',
@@ -26,7 +30,7 @@ headers = {
26
  def check_credits():
27
  """Check StableCog credits and return formatted results"""
28
  try:
29
- response = requests.get(API_URL, headers=headers, timeout=10)
30
 
31
  if response.status_code == 200:
32
  res_json = response.json()
@@ -62,10 +66,9 @@ def check_credits():
62
 
63
  # Use total_remaining_credits from API or calculate it
64
  if total_remaining_credits == 0 and credits_list:
65
- # Calculate total remaining from all credit entries
66
  total_remaining_credits = sum(credit.get('remaining_amount', 0) for credit in credits_list)
67
 
68
- # Calculate total credits (max of initial total or used + remaining)
69
  total_credits = max(total_initial_credits, total_remaining_credits + total_used_credits)
70
 
71
  # Calculate percentage used
@@ -118,6 +121,85 @@ def check_credits():
118
  "message": str(e)
119
  }
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  def update_display():
122
  """Update the UI with credit information"""
123
  result = check_credits()
@@ -244,6 +326,106 @@ def update_display():
244
 
245
  return html_content, f"Error: {result.get('error', 'Unknown error')}\n\nDetails: {result.get('message', '')}", 0, 0
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  def get_recommendation(remaining_credits, percentage_used):
248
  """Provide recommendations based on credit status"""
249
  if remaining_credits == 0:
@@ -259,84 +441,93 @@ def get_recommendation(remaining_credits, percentage_used):
259
  else:
260
  return "🚀 **Plenty of credits!** Ready for extensive image generation work and experimentation."
261
 
262
- # Create Gradio interface with custom theme
263
- theme = gr.themes.Soft(
264
- primary_hue="purple",
265
- secondary_hue="indigo",
266
- font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"]
267
- ).set(
268
- button_primary_background_fill="linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
269
- button_primary_background_fill_hover="linear-gradient(135deg, #764ba2 0%, #667eea 100%)",
270
- button_primary_text_color="white",
271
- button_primary_border_color="rgba(255,255,255,0.2)",
272
- )
273
-
274
- with gr.Blocks(theme=theme, title="StableCog Credit Monitor", css="footer {display: none !important;}") as demo:
 
 
 
 
275
  gr.Markdown("""
276
- # 🎯 StableCog Credit Dashboard
277
 
278
- *Monitor your API credits and plan your image generation projects efficiently.*
279
  """)
280
 
281
- # Status row
282
- with gr.Row():
283
- with gr.Column(scale=2):
284
- html_output = gr.HTML(label="Credit Status")
285
- with gr.Column(scale=1):
286
- raw_output = gr.Code(
287
- label="📋 Raw API Response",
288
- language="json",
289
- interactive=False,
290
- lines=15
291
- )
292
-
293
- # Stats row
294
- with gr.Row():
295
- with gr.Column():
296
- credits_display = gr.Number(
297
- label="Remaining Credits",
298
- interactive=False,
299
- elem_classes="stat-box"
300
- )
301
- with gr.Column():
302
- usage_display = gr.Number(
303
- label="Usage Percentage",
304
- interactive=False,
305
- elem_classes="stat-box"
306
- )
307
-
308
- # Recommendation row
309
- with gr.Row():
310
- recommendation_box = gr.Textbox(
311
- label="🎯 AI Recommendation",
312
- interactive=False,
313
- lines=3,
314
- elem_classes="recommendation-box"
315
- )
316
-
317
- # Control row
318
- with gr.Row():
319
- check_btn = gr.Button(
320
- "🔄 Check Credits Now",
321
- variant="primary",
322
- size="lg",
323
- elem_classes="refresh-btn"
324
- )
325
-
326
- # Auto-check on load
327
- demo.load(fn=update_display, inputs=None, outputs=[html_output, raw_output, credits_display, usage_display])
328
-
329
- # Connect button and update recommendation
330
- def update_all():
331
- html, raw, credits, usage = update_display()
332
- recommendation = get_recommendation(credits, usage)
333
- return html, raw, credits, usage, recommendation
334
-
335
- check_btn.click(
336
- fn=update_all,
337
- inputs=None,
338
- outputs=[html_output, raw_output, credits_display, usage_display, recommendation_box]
339
- )
 
 
 
 
 
340
 
341
  # Instructions and info
342
  with gr.Accordion("📚 How to Use & Setup", open=False):
@@ -349,33 +540,64 @@ with gr.Blocks(theme=theme, title="StableCog Credit Monitor", css="footer {displ
349
  - Key: `STABLECOG_API_KEY`
350
  - Value: `your_actual_api_key_here`
351
 
352
- 2. **Understanding Your Credits:**
353
- - **Total Credits**: Sum of all initial credits received
354
- - **Remaining Credits**: Currently available credits (sum of all credit types)
355
- - **Used Credits**: Credits spent on image generation
356
- - **Credit Types**: Different credit sources (Free, Refund, etc.)
357
-
358
- 3. **Credit Breakdown:**
359
- - **Free Credits**: Base credits provided to users
360
- - **Refund Credits**: Credits returned for failed generations
361
- - Each credit type may have different amounts and expiration
362
 
363
- 4. **Recommendations are based on:**
364
- - Remaining credit count
365
- - Usage percentage
366
- - Common workload patterns
367
  """)
368
 
369
  gr.Markdown("""
370
  ---
371
  *Built with ❤️ for StableCog users | [Report Issues](https://github.com/stability-ai/stablecog/issues)*
372
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
  if __name__ == "__main__":
375
- # For Hugging Face Spaces
376
  demo.launch(
377
  server_name="0.0.0.0",
378
  server_port=7860,
379
  share=False,
380
- show_error=True
 
381
  )
 
8
  # Load environment variables from .env file
9
  load_dotenv()
10
 
11
+ # API Configuration
12
  API_KEY = os.getenv('STABLECOG_API_KEY', '')
13
  if not API_KEY:
 
14
  API_KEY = "StableCogKey"
15
 
16
  API_HOST = 'https://api.stablecog.com'
17
+
18
+ # API Endpoints
19
+ CREDITS_ENDPOINT = '/v1/credits'
20
+ MODELS_ENDPOINT = '/v1/image/generation/models'
21
+
22
+ CREDITS_URL = f'{API_HOST}{CREDITS_ENDPOINT}'
23
+ MODELS_URL = f'{API_HOST}{MODELS_ENDPOINT}'
24
 
25
  headers = {
26
  'Authorization': f'Bearer {API_KEY}',
 
30
  def check_credits():
31
  """Check StableCog credits and return formatted results"""
32
  try:
33
+ response = requests.get(CREDITS_URL, headers=headers, timeout=10)
34
 
35
  if response.status_code == 200:
36
  res_json = response.json()
 
66
 
67
  # Use total_remaining_credits from API or calculate it
68
  if total_remaining_credits == 0 and credits_list:
 
69
  total_remaining_credits = sum(credit.get('remaining_amount', 0) for credit in credits_list)
70
 
71
+ # Calculate total credits
72
  total_credits = max(total_initial_credits, total_remaining_credits + total_used_credits)
73
 
74
  # Calculate percentage used
 
121
  "message": str(e)
122
  }
123
 
124
+ def get_available_models():
125
+ """Get available StableCog models"""
126
+ try:
127
+ response = requests.get(MODELS_URL, headers=headers, timeout=10)
128
+
129
+ if response.status_code == 200:
130
+ res_json = response.json()
131
+ models_list = res_json.get('models', [])
132
+
133
+ # Organize models by type
134
+ organized_models = []
135
+ for model in models_list:
136
+ # Extract model information
137
+ model_info = {
138
+ 'id': model.get('id', ''),
139
+ 'name': model.get('name', 'Unknown'),
140
+ 'description': model.get('description', ''),
141
+ 'type': model.get('type', 'unknown'),
142
+ 'is_public': model.get('is_public', False),
143
+ 'is_default': model.get('is_default', False),
144
+ 'is_community': model.get('is_community', False),
145
+ 'created_at': model.get('created_at', ''),
146
+ 'updated_at': model.get('updated_at', '')
147
+ }
148
+ organized_models.append(model_info)
149
+
150
+ # Sort models: default/public first, then by name
151
+ organized_models.sort(key=lambda x: (
152
+ not x['is_default'],
153
+ not x['is_public'],
154
+ x['name'].lower()
155
+ ))
156
+
157
+ # Count by type
158
+ model_count = len(organized_models)
159
+ public_count = sum(1 for m in organized_models if m['is_public'])
160
+ default_count = sum(1 for m in organized_models if m['is_default'])
161
+ community_count = sum(1 for m in organized_models if m['is_community'])
162
+
163
+ result = {
164
+ "success": True,
165
+ "models": organized_models,
166
+ "total_models": model_count,
167
+ "public_models": public_count,
168
+ "default_models": default_count,
169
+ "community_models": community_count,
170
+ "raw_data": json.dumps(res_json, indent=2),
171
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")
172
+ }
173
+
174
+ return result
175
+
176
+ else:
177
+ return {
178
+ "success": False,
179
+ "error": f"API Error: {response.status_code}",
180
+ "message": response.text if response.text else "No response text",
181
+ "status_code": response.status_code
182
+ }
183
+
184
+ except requests.exceptions.Timeout:
185
+ return {
186
+ "success": False,
187
+ "error": "Timeout Error",
188
+ "message": "The request timed out. Please try again."
189
+ }
190
+ except requests.exceptions.ConnectionError:
191
+ return {
192
+ "success": False,
193
+ "error": "Connection Error",
194
+ "message": "Could not connect to the API. Check your internet connection."
195
+ }
196
+ except Exception as e:
197
+ return {
198
+ "success": False,
199
+ "error": f"Unexpected Error: {type(e).__name__}",
200
+ "message": str(e)
201
+ }
202
+
203
  def update_display():
204
  """Update the UI with credit information"""
205
  result = check_credits()
 
326
 
327
  return html_content, f"Error: {result.get('error', 'Unknown error')}\n\nDetails: {result.get('message', '')}", 0, 0
328
 
329
+ def display_models():
330
+ """Display available models in a formatted way"""
331
+ result = get_available_models()
332
+
333
+ if result["success"]:
334
+ models_html = ""
335
+ model_counter = 0
336
+
337
+ for model in result["models"]:
338
+ model_counter += 1
339
+
340
+ # Determine model type badge
341
+ model_type = model['type'].upper() if model['type'] else 'UNKNOWN'
342
+ if model['is_community']:
343
+ model_type_badge = f"<span style='background: #FF6B6B; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>COMMUNITY</span>"
344
+ elif model['is_default']:
345
+ model_type_badge = f"<span style='background: #4CAF50; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>DEFAULT</span>"
346
+ elif model['is_public']:
347
+ model_type_badge = f"<span style='background: #2196F3; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>PUBLIC</span>"
348
+ else:
349
+ model_type_badge = f"<span style='background: #9E9E9E; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>{model_type}</span>"
350
+
351
+ # Model ID (shortened)
352
+ model_id_short = model['id'][:8] + "..." if len(model['id']) > 8 else model['id']
353
+
354
+ models_html += f"""
355
+ <div style='background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%); border-radius: 10px; padding: 15px; margin-bottom: 12px; border-left: 4px solid #667eea;'>
356
+ <div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;'>
357
+ <h3 style='margin: 0; font-size: 16px;'>
358
+ #{model_counter}. {model['name']}
359
+ {model_type_badge}
360
+ </h3>
361
+ <span style='font-size: 12px; opacity: 0.7;'>ID: {model_id_short}</span>
362
+ </div>
363
+
364
+ <p style='margin: 0 0 10px 0; font-size: 14px; opacity: 0.9;'>{model['description'] or 'No description available'}</p>
365
+
366
+ <div style='display: flex; gap: 10px; font-size: 12px; opacity: 0.7;'>
367
+ <span>📅 Created: {model['created_at'][:10] if model['created_at'] else 'Unknown'}</span>
368
+ <span>🔄 Updated: {model['updated_at'][:10] if model['updated_at'] else 'Unknown'}</span>
369
+ </div>
370
+ </div>
371
+ """
372
+
373
+ # Create stats section
374
+ stats_html = f"""
375
+ <div style='display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 20px;'>
376
+ <div style='background: rgba(102, 126, 234, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
377
+ <div style='font-size: 28px; font-weight: bold;'>{result['total_models']}</div>
378
+ <div style='font-size: 12px; opacity: 0.8;'>Total Models</div>
379
+ </div>
380
+ <div style='background: rgba(76, 175, 80, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
381
+ <div style='font-size: 28px; font-weight: bold;'>{result['public_models']}</div>
382
+ <div style='font-size: 12px; opacity: 0.8;'>Public Models</div>
383
+ </div>
384
+ <div style='background: rgba(33, 150, 243, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
385
+ <div style='font-size: 28px; font-weight: bold;'>{result['default_models']}</div>
386
+ <div style='font-size: 12px; opacity: 0.8;'>Default Models</div>
387
+ </div>
388
+ <div style='background: rgba(255, 107, 107, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
389
+ <div style='font-size: 28px; font-weight: bold;'>{result['community_models']}</div>
390
+ <div style='font-size: 12px; opacity: 0.8;'>Community Models</div>
391
+ </div>
392
+ </div>
393
+ """
394
+
395
+ html_content = f"""
396
+ <div style='font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; padding: 25px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white; box-shadow: 0 10px 30px rgba(0,0,0,0.2);'>
397
+ <h2 style='text-align: center; margin-bottom: 25px; font-size: 28px; font-weight: 600;'>🤖 Available StableCog Models</h2>
398
+
399
+ <div style='background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); padding: 25px; border-radius: 12px; margin-bottom: 20px; border: 1px solid rgba(255,255,255,0.2);'>
400
+ {stats_html}
401
+ <div style='max-height: 500px; overflow-y: auto; padding-right: 10px;'>
402
+ {models_html}
403
+ </div>
404
+ </div>
405
+
406
+ <div style='text-align: center; font-size: 14px; opacity: 0.7; margin-top: 10px;'>
407
+ ⏰ Last updated: {result['timestamp']} | 🔄 Refresh to see latest models
408
+ </div>
409
+ </div>
410
+ """
411
+
412
+ return html_content, result['raw_data']
413
+
414
+ else:
415
+ # Error display
416
+ html_content = f"""
417
+ <div style='font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; padding: 25px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); border-radius: 15px; color: white; text-align: center; box-shadow: 0 10px 30px rgba(0,0,0,0.2);'>
418
+ <h2 style='margin-bottom: 20px; font-size: 26px;'>⚠️ Failed to Load Models</h2>
419
+
420
+ <div style='background: rgba(255,255,255,0.15); backdrop-filter: blur(10px); padding: 20px; border-radius: 10px; margin-bottom: 20px; border: 1px solid rgba(255,255,255,0.2);'>
421
+ <p style='margin: 0 0 10px 0; font-size: 18px; font-weight: bold;'>{result.get('error', 'Unknown error')}</p>
422
+ <p style='margin: 0; font-size: 14px; opacity: 0.9;'>{result.get('message', '')}</p>
423
+ </div>
424
+ </div>
425
+ """
426
+
427
+ return html_content, f"Error: {result.get('error', 'Unknown error')}\n\nDetails: {result.get('message', '')}"
428
+
429
  def get_recommendation(remaining_credits, percentage_used):
430
  """Provide recommendations based on credit status"""
431
  if remaining_credits == 0:
 
441
  else:
442
  return "🚀 **Plenty of credits!** Ready for extensive image generation work and experimentation."
443
 
444
+ # Create Gradio interface with Gradio 6 theme
445
+ # Note: Gradio 6 has a simplified theme system
446
+ with gr.Blocks(
447
+ theme=gr.themes.Soft(
448
+ primary_hue="purple",
449
+ secondary_hue="indigo",
450
+ font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"]
451
+ ),
452
+ title="StableCog Dashboard",
453
+ css="""
454
+ footer {display: none !important;}
455
+ .gradio-container {max-width: 1400px !important;}
456
+ .tab-nav {background: rgba(255,255,255,0.1) !important; border-radius: 10px !important; padding: 5px !important;}
457
+ .stat-box input {font-weight: bold !important; font-size: 18px !important;}
458
+ .recommendation-box textarea {font-size: 16px !important; line-height: 1.5 !important;}
459
+ """
460
+ ) as demo:
461
  gr.Markdown("""
462
+ # 🎯 StableCog Dashboard
463
 
464
+ *Monitor your credits and explore available AI models for image generation.*
465
  """)
466
 
467
+ with gr.Tabs() as tabs:
468
+ with gr.Tab("💰 Credits Dashboard", id="credits"):
469
+ # Status row
470
+ with gr.Row():
471
+ with gr.Column(scale=2):
472
+ credits_html_output = gr.HTML(label="Credit Status")
473
+ with gr.Column(scale=1):
474
+ credits_raw_output = gr.Code(
475
+ label="📋 Raw API Response",
476
+ language="json",
477
+ interactive=False,
478
+ lines=15
479
+ )
480
+
481
+ # Stats row
482
+ with gr.Row():
483
+ with gr.Column():
484
+ credits_display = gr.Number(
485
+ label="Remaining Credits",
486
+ interactive=False,
487
+ elem_classes="stat-box"
488
+ )
489
+ with gr.Column():
490
+ usage_display = gr.Number(
491
+ label="Usage Percentage",
492
+ interactive=False,
493
+ elem_classes="stat-box"
494
+ )
495
+
496
+ # Recommendation row
497
+ with gr.Row():
498
+ recommendation_box = gr.Textbox(
499
+ label="🎯 AI Recommendation",
500
+ interactive=False,
501
+ lines=3,
502
+ elem_classes="recommendation-box"
503
+ )
504
+
505
+ # Control row
506
+ with gr.Row():
507
+ check_credits_btn = gr.Button(
508
+ "🔄 Check Credits",
509
+ variant="primary",
510
+ scale=0
511
+ )
512
+
513
+ with gr.Tab("🤖 Available Models", id="models"):
514
+ with gr.Row():
515
+ with gr.Column(scale=2):
516
+ models_html_output = gr.HTML(label="Available Models")
517
+ with gr.Column(scale=1):
518
+ models_raw_output = gr.Code(
519
+ label="📋 Raw API Response",
520
+ language="json",
521
+ interactive=False,
522
+ lines=15
523
+ )
524
+
525
+ with gr.Row():
526
+ check_models_btn = gr.Button(
527
+ "🔄 Refresh Models",
528
+ variant="primary",
529
+ scale=0
530
+ )
531
 
532
  # Instructions and info
533
  with gr.Accordion("📚 How to Use & Setup", open=False):
 
540
  - Key: `STABLECOG_API_KEY`
541
  - Value: `your_actual_api_key_here`
542
 
543
+ ### Features:
544
+ - **💰 Credits Dashboard**: Monitor your credit usage and remaining balance
545
+ - **🤖 Available Models**: Browse all StableCog image generation models
546
+ - **Smart Recommendations**: Get AI-powered suggestions based on your credits
547
+ - **Credit Breakdown**: See detailed breakdown by credit type
 
 
 
 
 
548
 
549
+ ### Understanding Credits:
550
+ - **Free Credits**: Base credits provided to users
551
+ - **Refund Credits**: Credits returned for failed generations
552
+ - Each credit type may have different amounts and expiration
553
  """)
554
 
555
  gr.Markdown("""
556
  ---
557
  *Built with ❤️ for StableCog users | [Report Issues](https://github.com/stability-ai/stablecog/issues)*
558
  """)
559
+
560
+ # Initialize displays on load
561
+ demo.load(
562
+ fn=update_display,
563
+ inputs=None,
564
+ outputs=[credits_html_output, credits_raw_output, credits_display, usage_display]
565
+ )
566
+
567
+ demo.load(
568
+ fn=display_models,
569
+ inputs=None,
570
+ outputs=[models_html_output, models_raw_output]
571
+ )
572
+
573
+ # Update recommendation after credits load
574
+ def update_recommendation_after_load():
575
+ return get_recommendation(credits_display.value, usage_display.value)
576
+
577
+ # Connect buttons
578
+ def update_all_credits():
579
+ html, raw, credits, usage = update_display()
580
+ recommendation = get_recommendation(credits, usage)
581
+ return html, raw, credits, usage, recommendation
582
+
583
+ check_credits_btn.click(
584
+ fn=update_all_credits,
585
+ inputs=None,
586
+ outputs=[credits_html_output, credits_raw_output, credits_display, usage_display, recommendation_box]
587
+ )
588
+
589
+ check_models_btn.click(
590
+ fn=display_models,
591
+ inputs=None,
592
+ outputs=[models_html_output, models_raw_output]
593
+ )
594
 
595
  if __name__ == "__main__":
596
+ # For Hugging Face Spaces - Gradio 6 syntax
597
  demo.launch(
598
  server_name="0.0.0.0",
599
  server_port=7860,
600
  share=False,
601
+ show_error=True,
602
+ debug=False
603
  )