import gradio as gr import requests import json import os import time from collections import defaultdict from PIL import Image import io BASE_URL = "https://api.jigsawstack.com/v1" headers = { "x-api-key": os.getenv("JIGSAWSTACK_API_KEY") } # Rate limiting configuration request_times = defaultdict(list) MAX_REQUESTS = 20 # Maximum requests per time window TIME_WINDOW = 3600 # Time window in seconds (1 hour) def get_real_ip(request: gr.Request): """Extract real IP address using x-forwarded-for header or fallback""" if not request: return "unknown" forwarded = request.headers.get("x-forwarded-for") if forwarded: ip = forwarded.split(",")[0].strip() # First IP in the list is the client's else: ip = request.client.host # fallback return ip def check_rate_limit(request: gr.Request): """Check if the current request exceeds rate limits""" if not request: return True, "Rate limit check failed - no request info" ip = get_real_ip(request) now = time.time() # Clean up old timestamps outside the time window request_times[ip] = [t for t in request_times[ip] if now - t < TIME_WINDOW] # Check if rate limit exceeded if len(request_times[ip]) >= MAX_REQUESTS: time_remaining = int(TIME_WINDOW - (now - request_times[ip][0])) time_remaining_minutes = round(time_remaining / 60, 1) time_window_minutes = round(TIME_WINDOW / 60, 1) return False, f"Rate limit exceeded. You can make {MAX_REQUESTS} requests per {time_window_minutes} minutes. Try again in {time_remaining_minutes} minutes." # Add current request timestamp request_times[ip].append(now) return True, "" def generate_image(prompt, aspect_ratio, width, height, steps, negative_prompt, guidance, seed, request: gr.Request): rate_limit_ok, rate_limit_msg = check_rate_limit(request) if not rate_limit_ok: return None, {"error": "Rate limit exceeded", "message": rate_limit_msg} # Validate required inputs if not prompt or not prompt.strip(): return None, {"error": "Prompt is required"} if len(prompt.strip()) > 5000: return None, {"error": "Prompt must be between 1-5000 characters"} # Build payload with required and optional parameters payload = { "prompt": prompt.strip() } # Add optional parameters if provided if aspect_ratio and aspect_ratio.strip(): payload["aspect_ratio"] = aspect_ratio.strip() if width: try: width_int = int(width) if 256 <= width_int <= 1920: payload["width"] = width_int else: return None, {"error": "Width must be between 256-1920 pixels"} except ValueError: return None, {"error": "Width must be a valid number"} if height: try: height_int = int(height) if 256 <= height_int <= 1920: payload["height"] = height_int else: return None, {"error": "Height must be between 256-1920 pixels"} except ValueError: return None, {"error": "Height must be a valid number"} if steps: try: steps_int = int(steps) if 1 <= steps_int <= 90: payload["steps"] = steps_int else: return None, {"error": "Steps must be between 1-90"} except ValueError: return None, {"error": "Steps must be a valid number"} if negative_prompt and negative_prompt.strip(): payload["negative_prompt"] = negative_prompt.strip() if guidance: try: guidance_float = float(guidance) if 1 <= guidance_float <= 28: payload["guidance"] = guidance_float else: return None, {"error": "Guidance must be between 1-28"} except ValueError: return None, {"error": "Guidance must be a valid number"} if seed: try: seed_int = int(seed) payload["seed"] = seed_int except ValueError: return None, {"error": "Seed must be a valid number"} try: r = requests.post(f"{BASE_URL}/ai/image_generation", headers=headers, json=payload) r.raise_for_status() # The API returns the image directly as binary data if r.headers.get('content-type', '').startswith('image/'): # Convert bytes to PIL Image for Gradio # Create PIL Image from bytes image = Image.open(io.BytesIO(r.content)) return image, {"success": True, "message": "Image generated successfully!"} else: # If not an image, try to parse as JSON for error try: error_data = r.json() return None, {"error": "Image generation failed", "details": error_data} except: return None, {"error": "Unexpected response format"} except requests.exceptions.RequestException as req_err: return None, {"error": "Request failed", "message": str(req_err)} except Exception as e: return None, {"error": "Unexpected error", "message": str(e)} # ----------------- Gradio UI ------------------ with gr.Blocks() as demo: gr.Markdown("""
Generate high-quality images from text prompts using advanced AI models.
For more details and API usage, see the documentation.