StablecogAPI / app.py
MySafeCode's picture
Update app.py
be61cc3 verified
raw
history blame
28.1 kB
import os
import json
import requests
import gradio as gr
from datetime import datetime
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# API Configuration
API_KEY = os.getenv('StableCogKey', '')
if not API_KEY:
API_KEY = "StableCogKey"
API_HOST = 'https://api.stablecog.com'
# API Endpoints
CREDITS_ENDPOINT = '/v1/credits'
MODELS_ENDPOINT = '/v1/image/generation/models'
CREDITS_URL = f'{API_HOST}{CREDITS_ENDPOINT}'
MODELS_URL = f'{API_HOST}{MODELS_ENDPOINT}'
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
def check_credits():
"""Check StableCog credits and return formatted results"""
try:
response = requests.get(CREDITS_URL, headers=headers, timeout=10)
if response.status_code == 200:
res_json = response.json()
# Parse the actual response structure
total_remaining_credits = res_json.get('total_remaining_credits', 0)
credits_list = res_json.get('credits', [])
# Calculate total initial credits from all credit entries
total_initial_credits = 0
total_used_credits = 0
credit_details = []
for credit in credits_list:
credit_type = credit.get('type', {})
initial_amount = credit_type.get('amount', 0)
remaining_amount = credit.get('remaining_amount', 0)
credit_name = credit_type.get('name', 'Unknown')
total_initial_credits += initial_amount
used_credits = initial_amount - remaining_amount
total_used_credits += used_credits
# Store credit details for display
credit_details.append({
'name': credit_name,
'initial': initial_amount,
'remaining': remaining_amount,
'used': used_credits,
'expires': credit.get('expires_at', 'Never'),
'description': credit_type.get('description', '')
})
# Use total_remaining_credits from API or calculate it
if total_remaining_credits == 0 and credits_list:
total_remaining_credits = sum(credit.get('remaining_amount', 0) for credit in credits_list)
# Calculate total credits
total_credits = max(total_initial_credits, total_remaining_credits + total_used_credits)
# Calculate percentage used
if total_credits > 0:
percentage_used = (total_used_credits / total_credits * 100)
else:
percentage_used = 0
# Create formatted output
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")
result = {
"success": True,
"total_credits": total_credits,
"total_remaining_credits": total_remaining_credits,
"total_used_credits": total_used_credits,
"percentage_used": round(percentage_used, 2),
"credit_details": credit_details,
"total_credit_types": len(credits_list),
"timestamp": timestamp,
"raw_data": json.dumps(res_json, indent=2)
}
return result
else:
return {
"success": False,
"error": f"API Error: {response.status_code}",
"message": response.text if response.text else "No response text",
"status_code": response.status_code
}
except requests.exceptions.Timeout:
return {
"success": False,
"error": "Timeout Error",
"message": "The request timed out. Please try again."
}
except requests.exceptions.ConnectionError:
return {
"success": False,
"error": "Connection Error",
"message": "Could not connect to the API. Check your internet connection."
}
except Exception as e:
return {
"success": False,
"error": f"Unexpected Error: {type(e).__name__}",
"message": str(e)
}
def get_available_models():
"""Get available StableCog models"""
try:
response = requests.get(MODELS_URL, headers=headers, timeout=10)
if response.status_code == 200:
res_json = response.json()
models_list = res_json.get('models', [])
# Organize models by type
organized_models = []
for model in models_list:
# Extract model information
model_info = {
'id': model.get('id', ''),
'name': model.get('name', 'Unknown'),
'description': model.get('description', ''),
'type': model.get('type', 'unknown'),
'is_public': model.get('is_public', False),
'is_default': model.get('is_default', False),
'is_community': model.get('is_community', False),
'created_at': model.get('created_at', ''),
'updated_at': model.get('updated_at', '')
}
organized_models.append(model_info)
# Sort models: default/public first, then by name
organized_models.sort(key=lambda x: (
not x['is_default'],
not x['is_public'],
x['name'].lower()
))
# Count by type
model_count = len(organized_models)
public_count = sum(1 for m in organized_models if m['is_public'])
default_count = sum(1 for m in organized_models if m['is_default'])
community_count = sum(1 for m in organized_models if m['is_community'])
result = {
"success": True,
"models": organized_models,
"total_models": model_count,
"public_models": public_count,
"default_models": default_count,
"community_models": community_count,
"raw_data": json.dumps(res_json, indent=2),
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")
}
return result
else:
return {
"success": False,
"error": f"API Error: {response.status_code}",
"message": response.text if response.text else "No response text",
"status_code": response.status_code
}
except requests.exceptions.Timeout:
return {
"success": False,
"error": "Timeout Error",
"message": "The request timed out. Please try again."
}
except requests.exceptions.ConnectionError:
return {
"success": False,
"error": "Connection Error",
"message": "Could not connect to the API. Check your internet connection."
}
except Exception as e:
return {
"success": False,
"error": f"Unexpected Error: {type(e).__name__}",
"message": str(e)
}
def update_display():
"""Update the UI with credit information"""
result = check_credits()
if result["success"]:
# Create a visual progress bar with color coding
percentage = result["percentage_used"]
if percentage < 50:
bar_color = "#4CAF50" # Green
status = "🟢 Good"
status_color = "#4CAF50"
elif percentage < 80:
bar_color = "#FF9800" # Orange
status = "🟡 Moderate"
status_color = "#FF9800"
else:
bar_color = "#F44336" # Red
status = "🔴 Low"
status_color = "#F44336"
# Build credit details HTML
credit_details_html = ""
for i, credit in enumerate(result["credit_details"]):
if credit['remaining'] > 0 or credit['initial'] > 0:
credit_percentage = (credit['used'] / credit['initial'] * 100) if credit['initial'] > 0 else 0
credit_details_html += f"""
<div style='margin-bottom: 15px; padding: 12px; background: rgba(255,255,255,0.05); border-radius: 8px;'>
<div style='display: flex; justify-content: space-between; margin-bottom: 5px;'>
<span style='font-weight: bold;'>{credit['name']}</span>
<span>{credit['remaining']} / {credit['initial']} ⭐</span>
</div>
<div style='font-size: 12px; opacity: 0.8; margin-bottom: 8px;'>{credit['description']}</div>
<div style='height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden;'>
<div style='height: 100%; width: {min(credit_percentage, 100)}%; background: {bar_color}; border-radius: 3px;'></div>
</div>
</div>
"""
if credit_details_html:
credit_details_section = f"""
<div style='margin-top: 20px;'>
<h3 style='margin-bottom: 15px; font-size: 18px;'>📊 Credit Breakdown</h3>
{credit_details_html}
</div>
"""
else:
credit_details_section = ""
html_content = f"""
<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);'>
<h2 style='text-align: center; margin-bottom: 30px; font-size: 28px; font-weight: 600;'>🎨 StableCog Credit Status</h2>
<div style='background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); padding: 25px; border-radius: 12px; margin-bottom: 25px; border: 1px solid rgba(255,255,255,0.2);'>
<div style='display: flex; justify-content: space-between; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.1);'>
<span style='font-size: 16px; opacity: 0.9;'>Total Credits:</span>
<span style='font-weight: bold; font-size: 24px;'>{result['total_credits']} ⭐</span>
</div>
<div style='display: flex; justify-content: space-between; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.1);'>
<span style='font-size: 16px; opacity: 0.9;'>Remaining Credits:</span>
<span style='font-weight: bold; font-size: 24px; color: #90EE90;'>{result['total_remaining_credits']} ⭐</span>
</div>
<div style='display: flex; justify-content: space-between; margin-bottom: 20px;'>
<span style='font-size: 16px; opacity: 0.9;'>Used Credits:</span>
<span style='font-weight: bold; font-size: 20px;'>{result['total_used_credits']} ⭐</span>
</div>
<div style='margin-bottom: 20px;'>
<div style='display: flex; justify-content: space-between; margin-bottom: 8px;'>
<span style='font-size: 16px; opacity: 0.9;'>Overall Usage:</span>
<span style='font-weight: bold;'>{result['percentage_used']}%</span>
</div>
<div style='height: 22px; background: rgba(255,255,255,0.15); border-radius: 11px; overflow: hidden; position: relative;'>
<div style='height: 100%; width: {min(result['percentage_used'], 100)}%; background: {bar_color};
border-radius: 11px; transition: width 0.5s ease-in-out; box-shadow: 0 0 10px {bar_color}80;'></div>
<div style='position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: white; font-size: 12px; font-weight: bold; text-shadow: 0 1px 2px rgba(0,0,0,0.5);'>
{result['percentage_used']}%
</div>
</div>
</div>
<div style='display: flex; justify-content: space-between; align-items: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.2);'>
<span style='font-size: 16px; opacity: 0.9;'>Status:</span>
<span style='font-weight: bold; font-size: 18px; color: {status_color}; padding: 5px 15px; background: rgba(255,255,255,0.1); border-radius: 20px;'>
{status}
</span>
</div>
{credit_details_section}
</div>
<div style='text-align: center; font-size: 14px; opacity: 0.7; margin-top: 10px;'>
⏰ Last checked: {result['timestamp']} | 📋 Credit types: {result['total_credit_types']}
</div>
</div>
"""
return html_content, result['raw_data'], result['total_remaining_credits'], result['percentage_used']
else:
# Error display
html_content = f"""
<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);'>
<h2 style='margin-bottom: 20px; font-size: 26px;'>⚠️ API Connection Error</h2>
<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);'>
<p style='margin: 0 0 10px 0; font-size: 18px; font-weight: bold;'>{result.get('error', 'Unknown error')}</p>
<p style='margin: 0; font-size: 14px; opacity: 0.9;'>{result.get('message', '')}</p>
{'<p style="margin: 10px 0 0 0; font-size: 14px;">Status Code: ' + str(result.get('status_code', '')) + '</p>' if result.get('status_code') else ''}
</div>
<div style='margin-top: 25px; padding: 15px; background: rgba(255,255,255,0.1); border-radius: 10px;'>
<h3 style='margin-top: 0;'>🔧 Troubleshooting Tips:</h3>
<ul style='text-align: left; margin: 10px 0; padding-left: 20px;'>
<li>Check if your API key is set in Hugging Face Secrets</li>
<li>Verify the API key has proper permissions</li>
<li>Ensure StableCog API is currently available</li>
<li>Check your internet connection</li>
</ul>
</div>
</div>
"""
return html_content, f"Error: {result.get('error', 'Unknown error')}\n\nDetails: {result.get('message', '')}", 0, 0
def display_models():
"""Display available models in a formatted way"""
result = get_available_models()
if result["success"]:
models_html = ""
model_counter = 0
for model in result["models"]:
model_counter += 1
# Determine model type badge
model_type = model['type'].upper() if model['type'] else 'UNKNOWN'
if model['is_community']:
model_type_badge = f"<span style='background: #FF6B6B; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>COMMUNITY</span>"
elif model['is_default']:
model_type_badge = f"<span style='background: #4CAF50; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>DEFAULT</span>"
elif model['is_public']:
model_type_badge = f"<span style='background: #2196F3; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>PUBLIC</span>"
else:
model_type_badge = f"<span style='background: #9E9E9E; padding: 2px 8px; border-radius: 12px; font-size: 11px; margin-left: 5px;'>{model_type}</span>"
# Model ID (shortened)
model_id_short = model['id'][:8] + "..." if len(model['id']) > 8 else model['id']
models_html += f"""
<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;'>
<div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;'>
<h3 style='margin: 0; font-size: 16px;'>
#{model_counter}. {model['name']}
{model_type_badge}
</h3>
<span style='font-size: 12px; opacity: 0.7;'>ID: {model_id_short}</span>
</div>
<p style='margin: 0 0 10px 0; font-size: 14px; opacity: 0.9;'>{model['description'] or 'No description available'}</p>
<div style='display: flex; gap: 10px; font-size: 12px; opacity: 0.7;'>
<span>📅 Created: {model['created_at'][:10] if model['created_at'] else 'Unknown'}</span>
<span>🔄 Updated: {model['updated_at'][:10] if model['updated_at'] else 'Unknown'}</span>
</div>
</div>
"""
# Create stats section
stats_html = f"""
<div style='display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 20px;'>
<div style='background: rgba(102, 126, 234, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
<div style='font-size: 28px; font-weight: bold;'>{result['total_models']}</div>
<div style='font-size: 12px; opacity: 0.8;'>Total Models</div>
</div>
<div style='background: rgba(76, 175, 80, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
<div style='font-size: 28px; font-weight: bold;'>{result['public_models']}</div>
<div style='font-size: 12px; opacity: 0.8;'>Public Models</div>
</div>
<div style='background: rgba(33, 150, 243, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
<div style='font-size: 28px; font-weight: bold;'>{result['default_models']}</div>
<div style='font-size: 12px; opacity: 0.8;'>Default Models</div>
</div>
<div style='background: rgba(255, 107, 107, 0.2); padding: 15px; border-radius: 8px; text-align: center;'>
<div style='font-size: 28px; font-weight: bold;'>{result['community_models']}</div>
<div style='font-size: 12px; opacity: 0.8;'>Community Models</div>
</div>
</div>
"""
html_content = f"""
<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);'>
<h2 style='text-align: center; margin-bottom: 25px; font-size: 28px; font-weight: 600;'>🤖 Available StableCog Models</h2>
<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);'>
{stats_html}
<div style='max-height: 500px; overflow-y: auto; padding-right: 10px;'>
{models_html}
</div>
</div>
<div style='text-align: center; font-size: 14px; opacity: 0.7; margin-top: 10px;'>
⏰ Last updated: {result['timestamp']} | 🔄 Refresh to see latest models
</div>
</div>
"""
return html_content, result['raw_data']
else:
# Error display
html_content = f"""
<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);'>
<h2 style='margin-bottom: 20px; font-size: 26px;'>⚠️ Failed to Load Models</h2>
<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);'>
<p style='margin: 0 0 10px 0; font-size: 18px; font-weight: bold;'>{result.get('error', 'Unknown error')}</p>
<p style='margin: 0; font-size: 14px; opacity: 0.9;'>{result.get('message', '')}</p>
</div>
</div>
"""
return html_content, f"Error: {result.get('error', 'Unknown error')}\n\nDetails: {result.get('message', '')}"
def get_recommendation(remaining_credits, percentage_used):
"""Provide recommendations based on credit status"""
if remaining_credits == 0:
return "💸 **No credits remaining.** Please add credits to continue using StableCog services."
elif percentage_used >= 90:
return "🛑 **Critically low credits!** Consider purchasing more credits before starting new projects."
elif percentage_used >= 75:
return "⚠️ **Credits are running low.** You can still do some work, but plan ahead for larger projects."
elif remaining_credits < 10:
return "📝 **Limited credits available.** Good for small tasks, testing, or single images."
elif remaining_credits < 50:
return "✨ **Credits available!** Suitable for several medium-sized projects or batch processing."
else:
return "🚀 **Plenty of credits!** Ready for extensive image generation work and experimentation."
# Create Gradio interface with simplified theme configuration for Gradio 6
# In Gradio 6, theme is set differently or uses default
with gr.Blocks(
title="StableCog Dashboard",
css="""
footer {display: none !important;}
.gradio-container {max-width: 1400px !important;}
.tab-nav {background: rgba(255,255,255,0.1) !important; border-radius: 10px !important; padding: 5px !important;}
.stat-box input {font-weight: bold !important; font-size: 18px !important;}
.recommendation-box textarea {font-size: 16px !important; line-height: 1.5 !important;}
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.gradio-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
"""
) as demo:
gr.Markdown("""
# 🎯 StableCog Dashboard
*Monitor your credits and explore available AI models for image generation.*
""")
with gr.Tabs() as tabs:
with gr.Tab("💰 Credits Dashboard", id="credits"):
# Status row
with gr.Row():
with gr.Column(scale=2):
credits_html_output = gr.HTML(label="Credit Status")
with gr.Column(scale=1):
credits_raw_output = gr.Code(
label="📋 Raw API Response",
language="json",
interactive=False,
lines=15
)
# Stats row
with gr.Row():
with gr.Column():
credits_display = gr.Number(
label="Remaining Credits",
interactive=False
)
with gr.Column():
usage_display = gr.Number(
label="Usage Percentage",
interactive=False
)
# Recommendation row
with gr.Row():
recommendation_box = gr.Textbox(
label="🎯 AI Recommendation",
interactive=False,
lines=3
)
# Control row
with gr.Row():
check_credits_btn = gr.Button(
"🔄 Check Credits",
variant="primary"
)
with gr.Tab("🤖 Available Models", id="models"):
with gr.Row():
with gr.Column(scale=2):
models_html_output = gr.HTML(label="Available Models")
with gr.Column(scale=1):
models_raw_output = gr.Code(
label="📋 Raw API Response",
language="json",
interactive=False,
lines=15
)
with gr.Row():
check_models_btn = gr.Button(
"🔄 Refresh Models",
variant="primary"
)
# Instructions and info
with gr.Accordion("📚 How to Use & Setup", open=False):
gr.Markdown("""
### Setting Up on Hugging Face Spaces:
1. **Add your API key as a Secret:**
- Go to your Space's Settings → Secrets
- Add a new secret with:
- Key: `STABLECOG_API_KEY`
- Value: `your_actual_api_key_here`
### Features:
- **💰 Credits Dashboard**: Monitor your credit usage and remaining balance
- **🤖 Available Models**: Browse all StableCog image generation models
- **Smart Recommendations**: Get AI-powered suggestions based on your credits
- **Credit Breakdown**: See detailed breakdown by credit type
### Understanding Credits:
- **Free Credits**: Base credits provided to users
- **Refund Credits**: Credits returned for failed generations
- Each credit type may have different amounts and expiration
""")
gr.Markdown("""
---
*Built with ❤️ for StableCog users | [Report Issues](https://github.com/stability-ai/stablecog/issues)*
""")
# Initialize displays on load
demo.load(
fn=update_display,
inputs=None,
outputs=[credits_html_output, credits_raw_output, credits_display, usage_display]
)
demo.load(
fn=display_models,
inputs=None,
outputs=[models_html_output, models_raw_output]
)
# Connect buttons
def update_all_credits():
html, raw, credits, usage = update_display()
recommendation = get_recommendation(credits, usage)
return html, raw, credits, usage, recommendation
check_credits_btn.click(
fn=update_all_credits,
inputs=None,
outputs=[credits_html_output, credits_raw_output, credits_display, usage_display, recommendation_box]
)
check_models_btn.click(
fn=display_models,
inputs=None,
outputs=[models_html_output, models_raw_output]
)
if __name__ == "__main__":
# For Hugging Face Spaces - Gradio 6 syntax
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
debug=False
)