lora-solar-generator / api_server.py
K2MAR's picture
Deploy LoRA Solar Panel Generator API
930b748
#!/usr/bin/env python3
"""
API Flask pour générer des images
Endpoint unique: /generate
"""
import torch
from pathlib import Path
from flask import Flask, request, jsonify, send_file
from diffusers import StableDiffusionPipeline
from datetime import datetime
import io
app = Flask(__name__)
# Configuration
MODEL_PATH = Path("/app/model")
OUTPUT_DIR = Path("/app/generated_images")
OUTPUT_DIR.mkdir(exist_ok=True, parents=True)
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# Variable globale
pipeline = None
model_loaded = False
def load_model():
"""Charge le modèle au démarrage"""
global pipeline, model_loaded
print("\n" + "="*70)
print("🤖 Chargement du modèle fusionné...")
print("="*70 + "\n")
if not MODEL_PATH.exists():
print(f"❌ Erreur: Le modèle n'existe pas à {MODEL_PATH}")
return False
try:
print(f"📱 Appareil: {DEVICE}")
print(f"📁 Modèle: {MODEL_PATH}\n")
print("Chargement...", end=" ", flush=True)
# Sur CPU: toujours float32, sur GPU: float16
dtype = torch.float32 if DEVICE == "cpu" else torch.float16
pipeline = StableDiffusionPipeline.from_pretrained(
str(MODEL_PATH),
torch_dtype=dtype,
safety_checker=None
).to(DEVICE)
print("✅\n")
print("Optimisations...", end=" ", flush=True)
pipeline.enable_attention_slicing()
print("✅\n")
model_loaded = True
print("="*70)
print("✅ Modèle prêt!")
print("="*70 + "\n")
return True
except Exception as e:
print(f"❌ Erreur: {e}")
return False
@app.route('/health', methods=['GET'])
def health():
"""Health check"""
return jsonify({
"status": "ok" if model_loaded else "loading",
"device": DEVICE,
"model_loaded": model_loaded
})
@app.route('/generate', methods=['POST'])
def generate():
"""
Endpoint unique pour générer une image
POST /generate
{
"prompt": "electrical wiring schematic",
"steps": 30,
"guidance_scale": 7.5
}
"""
if not model_loaded:
return jsonify({"error": "Model not loaded"}), 503
try:
data = request.get_json()
if not data or "prompt" not in data:
return jsonify({"error": "Missing 'prompt' in request"}), 400
prompt = data.get("prompt", "")
steps = int(data.get("steps", 30))
guidance_scale = float(data.get("guidance_scale", 7.5))
if not prompt:
return jsonify({"error": "Prompt cannot be empty"}), 400
if steps < 1 or steps > 50:
return jsonify({"error": "Steps must be 1-50"}), 400
print(f"\n🎨 Génération...")
print(f" Prompt: {prompt}")
print(f" Steps: {steps}, Guidance: {guidance_scale}")
with torch.no_grad():
image = pipeline(
prompt,
num_inference_steps=steps,
guidance_scale=guidance_scale,
height=512,
width=512
).images[0]
# Sauvegarder
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"generated_{timestamp}.png"
filepath = OUTPUT_DIR / filename
image.save(filepath)
print(f" ✅ Image générée: {filename}\n")
# Retourner l'image
img_io = io.BytesIO()
image.save(img_io, 'PNG')
img_io.seek(0)
return send_file(img_io, mimetype='image/png')
except Exception as e:
print(f"❌ Erreur: {str(e)}\n")
return jsonify({"error": str(e)}), 500
@app.route('/', methods=['GET'])
def home():
"""Info sur l'API"""
return jsonify({
"service": "LoRA Solar Panel Generator API",
"version": "1.0",
"device": DEVICE,
"model_loaded": model_loaded,
"endpoints": {
"health": "GET /health",
"generate": "POST /generate"
}
})
if __name__ == '__main__':
if not load_model():
exit(1)
# HF Spaces écoute sur le port 7860
port = 7860
print(f"\n🚀 Serveur démarrage sur 0.0.0.0:{port}\n")
app.run(host='0.0.0.0', port=port, debug=False, threaded=True)
#!/usr/bin/env python3
"""
API Flask simple pour générer des images
Utilise UNIQUEMENT le modèle fusionné
"""
import torch
from pathlib import Path
from flask import Flask, request, jsonify, send_file
from diffusers import StableDiffusionPipeline
from datetime import datetime
import io
import os
app = Flask(__name__)
# Configuration
MODEL_PATH = Path("/app/model")
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
OUTPUT_DIR = Path("/app/generated_images")
OUTPUT_DIR.mkdir(exist_ok=True, parents=True)
# Variable globale
pipeline = None
model_loaded = False
def load_model():
"""Charge le modèle au démarrage"""
global pipeline, model_loaded
print("\n" + "="*70)
print("🤖 Chargement du modèle fusionné...")
print("="*70 + "\n")
if not MODEL_PATH.exists():
print(f"❌ Erreur: Le modèle n'existe pas à {MODEL_PATH}")
return False
try:
print(f"📱 Appareil: {DEVICE}")
print(f"📁 Modèle: {MODEL_PATH}\n")
print("Chargement...", end=" ", flush=True)
pipeline = StableDiffusionPipeline.from_pretrained(
str(MODEL_PATH),
torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32,
safety_checker=None
).to(DEVICE)
print("✅\n")
print("Optimisations...", end=" ", flush=True)
pipeline.enable_attention_slicing()
print("✅\n")
model_loaded = True
print("="*70)
print("✅ Modèle prêt!")
print("="*70 + "\n")
return True
except Exception as e:
print(f"❌ Erreur: {e}")
return False
@app.route('/health', methods=['GET'])
def health():
"""Health check"""
return jsonify({
"status": "ok" if model_loaded else "loading",
"device": DEVICE,
"model_loaded": model_loaded
})
@app.route('/generate', methods=['POST'])
def generate():
"""
Endpoint unique pour générer une image
POST /generate
{
"prompt": "electrical wiring schematic",
"steps": 30,
"guidance_scale": 7.5
}
"""
if not model_loaded:
return jsonify({"error": "Model not loaded"}), 503
try:
data = request.get_json()
if not data or "prompt" not in data:
return jsonify({"error": "Missing 'prompt' in request"}), 400
prompt = data.get("prompt", "")
steps = int(data.get("steps", 30))
guidance_scale = float(data.get("guidance_scale", 7.5))
if not prompt:
return jsonify({"error": "Prompt cannot be empty"}), 400
if steps < 1 or steps > 50:
return jsonify({"error": "Steps must be 1-50"}), 400
print(f"\n🎨 Génération...")
print(f" Prompt: {prompt}")
print(f" Steps: {steps}, Guidance: {guidance_scale}")
with torch.no_grad():
image = pipeline(
prompt,
num_inference_steps=steps,
guidance_scale=guidance_scale,
height=512,
width=512
).images[0]
# Sauvegarder
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"generated_{timestamp}.png"
filepath = OUTPUT_DIR / filename
image.save(filepath)
print(f" ✅ Image générée: {filename}\n")
# Retourner l'image
img_io = io.BytesIO()
image.save(img_io, 'PNG')
img_io.seek(0)
return send_file(img_io, mimetype='image/png')
except Exception as e:
print(f"❌ Erreur: {str(e)}\n")
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
if not load_model():
exit(1)
print("\n🚀 Serveur démarrage sur 0.0.0.0:5000\n")
app.run(host='0.0.0.0', port=5000, debug=False)