Déploiement serveur pour test de génération multimédia (HuggingFace + Cloudflare)
Build and Push Docker Image / build-and-push (push) Successful in 34s

This commit is contained in:
xavier committed 2026-08-24 21:14:17 +02:00
1 parent 916af3e62b
commit 85a6fe1d27
2 files changed
+69 -29

No files matched your search

+67 -27
View File
@@ -6,7 +6,6 @@ import base64
from modules.logger import log_event from modules.logger import log_event
def generer_image_cloudflare(prompt: str) -> bytes: def generer_image_cloudflare(prompt: str) -> bytes:
"""Appel à l'API Cloudflare Workers AI avec décodage Base64"""
account_id = os.getenv("CLOUDFLARE_ACCOUNT_ID") account_id = os.getenv("CLOUDFLARE_ACCOUNT_ID")
api_token = os.getenv("CLOUDFLARE_API_TOKEN") api_token = os.getenv("CLOUDFLARE_API_TOKEN")
if not account_id or not api_token: return None if not account_id or not api_token: return None
@@ -27,35 +26,64 @@ def generer_image_cloudflare(prompt: str) -> bytes:
log_event("WARNING", "MEDIA", f"Échec Cloudflare : {e}") log_event("WARNING", "MEDIA", f"Échec Cloudflare : {e}")
return None return None
def generer_image_huggingface(prompt: str) -> bytes: def generer_image_openrouter(prompt: str) -> tuple:
"""Appel à l'API HuggingFace""" api_key = os.getenv("OPENROUTER_API_KEY")
if not api_key: return None, None
url = "https://openrouter.ai/api/v1/images"
headers = {
"Authorization": f"Bearer {api_key}",
"HTTP-Referer": "https://aethas38.duckdns.org",
"X-Title": "Aethas38"
}
modele = "bytedance-seed/seedream-4.5"
payload = {"prompt": prompt, "model": modele}
try:
resp = requests.post(url, headers=headers, json=payload, timeout=120)
if resp.status_code == 200:
data = resp.json()
if "data" in data and len(data["data"]) > 0 and "b64_json" in data["data"][0]:
img_bytes = base64.b64decode(data["data"][0]["b64_json"])
return img_bytes, modele
except Exception as e:
log_event("WARNING", "MEDIA", f"Échec OpenRouter Image : {e}")
return None, None
def generer_image_huggingface(prompt: str) -> tuple:
api_key = os.getenv("HUGGINGFACE_API_KEY") api_key = os.getenv("HUGGINGFACE_API_KEY")
if not api_key: return None if not api_key: return None, "Clé API manquante"
url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0" url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
headers = {"Authorization": f"Bearer {api_key}"} headers = {"Authorization": f"Bearer {api_key}"}
try: try:
resp = requests.post(url, headers=headers, json={"inputs": prompt}, timeout=120) resp = requests.post(url, headers=headers, json={"inputs": prompt}, timeout=120)
if resp.status_code == 200: return resp.content if resp.status_code == 200:
return resp.content, "Succès"
else:
return None, f"Erreur HTTP {resp.status_code} : {resp.text}"
except Exception as e: except Exception as e:
log_event("WARNING", "MEDIA", f"Échec HF : {e}") log_event("WARNING", "MEDIA", f"Échec HF : {e}")
return None return None, f"Exception système : {str(e)}"
def generer_les_deux_images(prompt: str, fichiers: list) -> tuple: def generer_les_images(prompt: str, fichiers: list) -> tuple:
trace = [] trace = []
if fichiers: if fichiers:
trace.append("🟢 Mode Image-to-Image (Désactivé en multi-fournisseur)") trace.append("🟢 Mode Image-to-Image")
return f"*(BETA)* Le mode Image-to-Image nécessite une API spécifique. \n\nFichiers : {', '.join(fichiers)}", "#ff9800", "Système", "En dev", trace return f"*(BETA)* Le mode Image-to-Image est désactivé temporairement. \n\nFichiers : {', '.join(fichiers)}", "#ff9800", "Système", "En dev", trace
trace.append("🟢 Lancement Text-to-Image (HuggingFace + Cloudflare)") trace.append("🟢 Lancement Text-to-Image (OR + CF + HF)")
# Exécution des deux API en parallèle pour gagner du temps # Exécution des TROIS API en parallèle
with concurrent.futures.ThreadPoolExecutor() as executor: with concurrent.futures.ThreadPoolExecutor() as executor:
future_hf = executor.submit(generer_image_huggingface, prompt) future_or = executor.submit(generer_image_openrouter, prompt)
future_cf = executor.submit(generer_image_cloudflare, prompt) future_cf = executor.submit(generer_image_cloudflare, prompt)
img_hf = future_hf.result() future_hf = executor.submit(generer_image_huggingface, prompt)
img_or, nom_modele_or = future_or.result()
img_cf = future_cf.result() img_cf = future_cf.result()
img_hf, msg_hf = future_hf.result()
dossier_media = "/DATA/AppData/MULTI-IA-AETHAS38/RAG/Media" dossier_media = "/DATA/AppData/MULTI-IA-AETHAS38/RAG/Media"
os.makedirs(dossier_media, exist_ok=True) os.makedirs(dossier_media, exist_ok=True)
@@ -63,38 +91,50 @@ def generer_les_deux_images(prompt: str, fichiers: list) -> tuple:
reponse_md = f"**Prompt analysé :** {prompt}\n\n" reponse_md = f"**Prompt analysé :** {prompt}\n\n"
fournisseurs_actifs = [] fournisseurs_actifs = []
# Traitement OpenRouter
if img_or:
nom_or = f"img_or_{int(time.time())}.png"
with open(os.path.join(dossier_media, nom_or), 'wb') as f: f.write(img_or)
reponse_md += f"### 🌐 OpenRouter ({nom_modele_or})\n![OpenRouter](/Media/{nom_or})\n"
reponse_md += f"**[📥 Télécharger l'image OR](/Media/{nom_or})**\n\n---\n\n"
fournisseurs_actifs.append("OpenRouter")
trace.append("🟢 OR : Succès")
else:
trace.append("🔴 OR : Échec")
# Traitement Cloudflare # Traitement Cloudflare
if img_cf: if img_cf:
nom_cf = f"img_cf_{int(time.time())}.png" 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) 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" reponse_md += f"### ☁️ Cloudflare Workers AI\n![Cloudflare](/Media/{nom_cf})\n"
reponse_md += f"**[📥 Cliquez ici pour télécharger l'image](/Media/{nom_cf})**\n\n---\n\n" reponse_md += f"**[📥 Télécharger l'image CF](/Media/{nom_cf})**\n\n---\n\n"
fournisseurs_actifs.append("Cloudflare") fournisseurs_actifs.append("Cloudflare")
trace.append("🟢 Cloudflare : Succès") trace.append("🟢 CF : Succès")
else: else:
trace.append("🔴 Cloudflare : Échec") trace.append("🔴 CF : Échec")
# Traitement HuggingFace # Traitement HuggingFace (Avec diagnostic intégré)
if img_hf: if img_hf:
nom_hf = f"img_hf_{int(time.time())}.png" 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) 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" reponse_md += f"### 🤗 HuggingFace (SDXL)\n![HuggingFace](/Media/{nom_hf})\n"
reponse_md += f"**[📥 Cliquez ici pour télécharger l'image](/Media/{nom_hf})**\n\n" reponse_md += f"**[📥 Télécharger l'image HF](/Media/{nom_hf})**\n\n"
fournisseurs_actifs.append("HuggingFace") fournisseurs_actifs.append("HuggingFace")
trace.append("🟢 HF : Succès") trace.append("🟢 HF : Succès")
else: else:
trace.append("🔴 HF : Échec") erreur_courte = str(msg_hf)[:50] + "..." if len(str(msg_hf)) > 50 else str(msg_hf)
trace.append(f"🔴 HF : Échec ({erreur_courte})")
reponse_md += f"### 🤗 HuggingFace (SDXL)\n*⚠️ Échec de la génération : {msg_hf}*\n\n"
# Si les deux ont échoué if not img_or and not img_cf and not img_hf:
if not img_hf and not img_cf: return "*Échec critique : Aucune des 3 API n'a pu générer d'image.*", "#ff4444", "Système", "Erreur", trace
return "*Échec critique : HuggingFace et Cloudflare ont échoué.*", "#ff4444", "Système", "Erreur", trace
return reponse_md, "#00e5ff", " + ".join(fournisseurs_actifs), "Stable Diffusion XL", trace return reponse_md, "#00e5ff", " + ".join(fournisseurs_actifs), "Moteur Triple-IA", trace
def router_media(prompt: str, type_media: str, premium: bool, fichiers: list) -> tuple: def router_media(prompt: str, type_media: str, premium: bool, fichiers: list) -> tuple:
trace = [f"🟢 Routage Média : {type_media.upper()}"] trace = [f"🟢 Routage Média : {type_media.upper()}"]
if type_media == "image": if type_media == "image":
return generer_les_deux_images(prompt, fichiers) return generer_les_images(prompt, fichiers)
elif type_media == "video": 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 "*La génération vidéo nécessite une API payante.* \n\nPrompt : " + prompt, "#ff4444", "Système", "En attente API", trace
return "Type de média non reconnu.", "#333", "Erreur", "Aucun", trace return "Type de média non reconnu.", "#333", "Erreur", "Aucun", trace
+2 -2
View File
@@ -32,7 +32,7 @@ def get_theme_hdrs():
/* --- CONTENU CENTRAL & CONTRASTE MOBILE ACCRU --- */ /* --- CONTENU CENTRAL & CONTRASTE MOBILE ACCRU --- */
.main-content { flex: 1; display: flex; flex-direction: column; height: 100vh; position: relative; box-sizing: border-box; } .main-content { flex: 1; display: flex; flex-direction: column; height: 100vh; position: relative; box-sizing: border-box; }
.chat-container { flex: 1; padding: 20px; max-width: 1000px; margin: 0 auto; width: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 20px; padding-bottom: 160px; box-sizing: border-box; } .chat-container { flex: 1; padding: 20px; max-width: 1000px; margin: 0 auto; width: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 20px; padding-bottom: 220px; box-sizing: border-box; }
.user-bubble { background-color: #2a2a35; color: #ffffff; padding: 15px 20px; border-radius: 15px 15px 0 15px; align-self: flex-end; max-width: 80%; border: 1px solid #555; margin-left: auto; word-break: break-word; overflow-wrap: break-word; } .user-bubble { background-color: #2a2a35; color: #ffffff; padding: 15px 20px; border-radius: 15px 15px 0 15px; align-self: flex-end; max-width: 80%; border: 1px solid #555; margin-left: auto; word-break: break-word; overflow-wrap: break-word; }
/* BULLE IA : Contraste maximal forcé pour le confort de lecture mobile */ /* BULLE IA : Contraste maximal forcé pour le confort de lecture mobile */
@@ -80,7 +80,7 @@ def get_theme_hdrs():
.sidebar { position: absolute; left: -100%; top: 0; z-index: 1000; width: 280px; height: 100vh; box-shadow: 5px 0 15px rgba(0,0,0,0.8); } .sidebar { position: absolute; left: -100%; top: 0; z-index: 1000; width: 280px; height: 100vh; box-shadow: 5px 0 15px rgba(0,0,0,0.8); }
.sidebar.sidebar-open { left: 0; } .sidebar.sidebar-open { left: 0; }
.main-content { flex: 1; height: calc(100vh - 60px); } .main-content { flex: 1; height: calc(100vh - 60px); }
.chat-container { padding-bottom: 200px; padding-left: 10px; padding-right: 10px; } .chat-container { padding-bottom: 260px; padding-left: 10px; padding-right: 10px; }
.user-bubble, .ai-bubble { max-width: 95%; } .user-bubble, .ai-bubble { max-width: 95%; }
.input-area { padding: 10px; } .input-area { padding: 10px; }
.options-row { justify-content: center; gap: 10px;} .options-row { justify-content: center; gap: 10px;}