import os import requests import time import concurrent.futures import base64 # <-- NOUVEL IMPORT POUR DECODER L'IMAGE from modules.logger import log_event def generer_image_cloudflare(prompt: str) -> bytes: """Appel à l'API Cloudflare Workers AI avec décodage Base64""" account_id = os.getenv("CLOUDFLARE_ACCOUNT_ID") api_token = os.getenv("CLOUDFLARE_API_TOKEN") if not account_id or not api_token: return None url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/run/@cf/stabilityai/stable-diffusion-xl-base-1.0" headers = {"Authorization": f"Bearer {api_token}"} try: resp = requests.post(url, headers=headers, json={"prompt": prompt}, timeout=120) if resp.status_code == 200: # On vérifie si Cloudflare nous répond en texte (JSON) if "application/json" in resp.headers.get("Content-Type", ""): data = resp.json() if data.get("success") and "result" in data: # On transforme le texte Base64 en véritable fichier image binaire return base64.b64decode(data["result"]) else: # S'il répond nativement en binaire (parfois le cas selon la requête) return resp.content except Exception as e: log_event("WARNING", "MEDIA", f"Échec Cloudflare : {e}") return None # ... (le reste du fichier reste identique) def generer_image_huggingface(prompt: str) -> bytes: """Appel à l'API HuggingFace""" api_key = os.getenv("HUGGINGFACE_API_KEY") if not api_key: return None url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0" headers = {"Authorization": f"Bearer {api_key}"} try: resp = requests.post(url, headers=headers, json={"inputs": prompt}, timeout=120) if resp.status_code == 200: return resp.content except Exception as e: log_event("WARNING", "MEDIA", f"Échec HF : {e}") return None def generer_les_deux_images(prompt: str, fichiers: list) -> tuple: trace = [] if fichiers: trace.append("🟢 Mode Image-to-Image (Désactivé en multi-fournisseur)") return f"*(BETA)* Le mode Image-to-Image nécessite une API spécifique. \n\nFichiers : {', '.join(fichiers)}", "#ff9800", "Système", "En dev", trace trace.append("🟢 Lancement Text-to-Image (HuggingFace + Cloudflare)") # Exécution des deux API en parallèle pour gagner du temps with concurrent.futures.ThreadPoolExecutor() as executor: future_hf = executor.submit(generer_image_huggingface, prompt) future_cf = executor.submit(generer_image_cloudflare, prompt) img_hf = future_hf.result() img_cf = future_cf.result() dossier_media = "/DATA/AppData/MULTI-IA-AETHAS38/RAG/Media" os.makedirs(dossier_media, exist_ok=True) reponse_md = f"**Prompt analysé :** {prompt}\n\n" fournisseurs_actifs = [] # Traitement Cloudflare if img_cf: nom_cf = f"img_cf_{int(time.time())}.png" with open(os.path.join(dossier_media, nom_cf), 'wb') as f: f.write(img_cf) reponse_md += f"### ☁️ Cloudflare Workers AI (SDXL)\n![Cloudflare](/Media/{nom_cf})\n\n---\n\n" fournisseurs_actifs.append("Cloudflare") trace.append("🟢 Cloudflare : Succès") else: trace.append("🔴 Cloudflare : Échec") # Traitement HuggingFace if img_hf: nom_hf = f"img_hf_{int(time.time())}.png" with open(os.path.join(dossier_media, nom_hf), 'wb') as f: f.write(img_hf) reponse_md += f"### 🤗 HuggingFace (SDXL)\n![HuggingFace](/Media/{nom_hf})\n\n" fournisseurs_actifs.append("HuggingFace") trace.append("🟢 HF : Succès") else: trace.append("🔴 HF : Échec") # Si les deux ont échoué if not img_hf and not img_cf: return "*Échec critique : HuggingFace et Cloudflare ont échoué (ou clés manquantes).* \nVérifiez les logs du serveur.", "#ff4444", "Système", "Erreur", trace return reponse_md, "#00e5ff", " + ".join(fournisseurs_actifs), "Stable Diffusion XL", trace def router_media(prompt: str, type_media: str, premium: bool, fichiers: list) -> tuple: trace = [f"🟢 Routage Média : {type_media.upper()}"] if type_media == "image": return generer_les_deux_images(prompt, fichiers) elif type_media == "video": return "*La génération vidéo nécessite une API payante (Luma, Kling).* \n\nPrompt : " + prompt, "#ff4444", "Système", "En attente API", trace return "Type de média non reconnu.", "#333", "Erreur", "Aucun", trace