Public Access
Ajout du connecteur Hugging Face et mise à jour du Multi-IA
Build and Push Docker Image / build-and-push (push) Successful in 37s
Build and Push Docker Image / build-and-push (push) Successful in 37s
This commit is contained in:
1 parent
a510916247
commit
eab18d8324
4 files changed
+131
-32
No files matched your search
+38
-9
@@ -3,6 +3,7 @@ from modules.logger import log_event
|
|||||||
from modules.ai_gemini import interroger_gemini
|
from modules.ai_gemini import interroger_gemini
|
||||||
from modules.ai_local import interroger_local_ollama
|
from modules.ai_local import interroger_local_ollama
|
||||||
from modules.ai_openrouter import interroger_openrouter, interroger_openrouter_multi
|
from modules.ai_openrouter import interroger_openrouter, interroger_openrouter_multi
|
||||||
|
from modules.ai_huggingface import interroger_huggingface
|
||||||
|
|
||||||
SYSTEM_CONFIDENTIALITY_INSTRUCTION = (
|
SYSTEM_CONFIDENTIALITY_INSTRUCTION = (
|
||||||
"RÈGLE DE CONFIDENTIALITÉ ABSOLUE : Tu es un assistant intégré à la plateforme privée Aethas38. "
|
"RÈGLE DE CONFIDENTIALITÉ ABSOLUE : Tu es un assistant intégré à la plateforme privée Aethas38. "
|
||||||
@@ -12,9 +13,33 @@ SYSTEM_CONFIDENTIALITY_INSTRUCTION = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
def executer_synthese_multi_ia(prompt_securise: str, fournisseur: str, mode_payant: bool = False, chat_id: str = None):
|
def executer_synthese_multi_ia(prompt_securise: str, fournisseur: str, mode_payant: bool = False, chat_id: str = None):
|
||||||
|
reponses_brutes = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Passage du chat_id (code hexadécimal) à l'API Multi
|
# 1. Lancement PARALLÈLE vers OpenRouter et Hugging Face
|
||||||
reponses_brutes = interroger_openrouter_multi(prompt_securise, type_modele=fournisseur, premium=mode_payant, chat_id=chat_id)
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||||
|
future_or = executor.submit(interroger_openrouter_multi, prompt_securise, type_modele=fournisseur, premium=mode_payant, chat_id=chat_id)
|
||||||
|
future_hf = executor.submit(interroger_huggingface, prompt_securise)
|
||||||
|
|
||||||
|
# 2. Récupération des résultats OpenRouter
|
||||||
|
try:
|
||||||
|
resultat_or = future_or.result()
|
||||||
|
if resultat_or:
|
||||||
|
reponses_brutes.extend(resultat_or)
|
||||||
|
except Exception as e:
|
||||||
|
log_event("WARNING", "MULTI_IA", f"Échec OpenRouter : {e}")
|
||||||
|
|
||||||
|
# 3. Récupération des résultats Hugging Face
|
||||||
|
try:
|
||||||
|
resultat_hf = future_hf.result()
|
||||||
|
if resultat_hf and resultat_hf[0]:
|
||||||
|
texte_hf, _, _, modele_hf = resultat_hf
|
||||||
|
# Formatage identique à OpenRouter pour la synthèse
|
||||||
|
reponses_brutes.append({"modele": f"HuggingFace ({modele_hf})", "texte": texte_hf})
|
||||||
|
except Exception as e:
|
||||||
|
log_event("WARNING", "MULTI_IA", f"Échec HuggingFace : {e}")
|
||||||
|
|
||||||
|
# 4. Synthèse par Gemini si au moins un modèle a répondu
|
||||||
if len(reponses_brutes) >= 1:
|
if len(reponses_brutes) >= 1:
|
||||||
instruction_synthese = (
|
instruction_synthese = (
|
||||||
"RÈGLE DE CONFIDENTIALITÉ : Tu es un assistant IA. "
|
"RÈGLE DE CONFIDENTIALITÉ : Tu es un assistant IA. "
|
||||||
@@ -29,9 +54,13 @@ def executer_synthese_multi_ia(prompt_securise: str, fournisseur: str, mode_paya
|
|||||||
|
|
||||||
prompt_synthese += f"Demande initiale :\n{prompt_securise}"
|
prompt_synthese += f"Demande initiale :\n{prompt_securise}"
|
||||||
texte, couleur, _, _ = interroger_gemini(prompt_synthese)
|
texte, couleur, _, _ = interroger_gemini(prompt_synthese)
|
||||||
return texte, couleur, "Multi-IA (Synthèse Gemini)", "Pipeline Multi-Modèles"
|
|
||||||
|
# Mise à jour du nom de fournisseur pour refléter le travail de l'orchestrateur
|
||||||
|
vrai_fournisseur = f"Multi-IA (OR + HF -> Gemini)" if len(reponses_brutes) > 1 else "Multi-IA (Partiel -> Gemini)"
|
||||||
|
return texte, couleur, vrai_fournisseur, f"Synthèse de {len(reponses_brutes)} modèles"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_event("WARNING", "MULTI_IA", f"Échec synthèse, repli : {e}")
|
log_event("WARNING", "MULTI_IA", f"Échec total synthèse : {e}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -42,14 +71,14 @@ def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False
|
|||||||
if multi_ia or fournisseur in ["openrouter_code", "openrouter_droit"]:
|
if multi_ia or fournisseur in ["openrouter_code", "openrouter_droit"]:
|
||||||
resultat_multi = executer_synthese_multi_ia(prompt_securise, fournisseur, mode_payant, chat_id)
|
resultat_multi = executer_synthese_multi_ia(prompt_securise, fournisseur, mode_payant, chat_id)
|
||||||
if resultat_multi:
|
if resultat_multi:
|
||||||
return resultat_multi[0], resultat_multi[1], resultat_multi[2], resultat_multi[3], ["🟢 Multi-IA / Synthèse Gemini réussie"]
|
return resultat_multi[0], resultat_multi[1], resultat_multi[2], resultat_multi[3], [f"🟢 {resultat_multi[2]} réussie"]
|
||||||
|
|
||||||
if fournisseur == "gemini":
|
if fournisseur == "gemini":
|
||||||
ordre_repli = [interroger_gemini, interroger_openrouter, interroger_local_ollama]
|
ordre_repli = [interroger_gemini, interroger_openrouter, interroger_huggingface, interroger_local_ollama]
|
||||||
elif fournisseur in ["openrouter_code", "openrouter_chat", "openrouter_droit"]:
|
elif fournisseur in ["openrouter_code", "openrouter_chat", "openrouter_droit"]:
|
||||||
ordre_repli = [interroger_openrouter, interroger_gemini, interroger_local_ollama]
|
ordre_repli = [interroger_openrouter, interroger_gemini, interroger_huggingface, interroger_local_ollama]
|
||||||
else:
|
else:
|
||||||
ordre_repli = [interroger_local_ollama, interroger_openrouter, interroger_gemini]
|
ordre_repli = [interroger_local_ollama, interroger_openrouter, interroger_huggingface, interroger_gemini]
|
||||||
|
|
||||||
for fonction_api in ordre_repli:
|
for fonction_api in ordre_repli:
|
||||||
nom_tente = fonction_api.__name__.replace("interroger_", "")
|
nom_tente = fonction_api.__name__.replace("interroger_", "")
|
||||||
@@ -65,7 +94,7 @@ def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False
|
|||||||
trace_tentatives.append(f"🟢 {nom_tente.upper()} ({nom_modele}) : Succès")
|
trace_tentatives.append(f"🟢 {nom_tente.upper()} ({nom_modele}) : Succès")
|
||||||
return texte, couleur, nom_fournisseur, nom_modele, trace_tentatives
|
return texte, couleur, nom_fournisseur, nom_modele, trace_tentatives
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg_err = str(e).split("-")[0].strip()[:50] # On raccourcit l'erreur pour l'affichage
|
msg_err = str(e).split("-")[0].strip()[:50]
|
||||||
trace_tentatives.append(f"🔴 {nom_tente.upper()} : Échec ({msg_err})")
|
trace_tentatives.append(f"🔴 {nom_tente.upper()} : Échec ({msg_err})")
|
||||||
log_event("WARNING", "ORCHESTRATEUR", f"Basculement suite à l'échec de {fonction_api.__name__} : {e}")
|
log_event("WARNING", "ORCHESTRATEUR", f"Basculement suite à l'échec de {fonction_api.__name__} : {e}")
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
from modules.logger import log_event
|
||||||
|
|
||||||
|
def interroger_huggingface(prompt: str, modele_cible="mistralai/Mistral-7B-Instruct-v0.3"):
|
||||||
|
"""
|
||||||
|
Connecteur pour l'API Serverless gratuite de Hugging Face.
|
||||||
|
S'intègre nativement dans la boucle de repli de ai_engine.py.
|
||||||
|
"""
|
||||||
|
api_key = os.getenv("HUGGINGFACE_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
raise Exception("Clé API HuggingFace manquante dans le .env")
|
||||||
|
|
||||||
|
url = f"https://api-inference.huggingface.co/models/{modele_cible}"
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {api_key}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Paramètres optimisés pour les LLMs d'instruction
|
||||||
|
payload = {
|
||||||
|
"inputs": prompt,
|
||||||
|
"parameters": {
|
||||||
|
"max_new_tokens": 2048,
|
||||||
|
"return_full_text": False,
|
||||||
|
"temperature": 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = requests.post(url, headers=headers, json=payload, timeout=60)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
data = resp.json()
|
||||||
|
if isinstance(data, list) and len(data) > 0 and "generated_text" in data[0]:
|
||||||
|
texte = data[0]["generated_text"].strip()
|
||||||
|
# Renvoie la signature exacte attendue par ai_engine (texte, couleur, fournisseur, modele)
|
||||||
|
return texte, "#ffb000", "HuggingFace", modele_cible
|
||||||
|
|
||||||
|
raise Exception(f"Erreur {resp.status_code} : {resp.text}")
|
||||||
|
except Exception as e:
|
||||||
|
log_event("WARNING", "HUGGINGFACE", f"Échec de la requête : {e}")
|
||||||
|
raise e
|
||||||
@@ -7,6 +7,41 @@ from modules.logger import log_event
|
|||||||
|
|
||||||
CATALOGUE_FILE = "/DATA/AppData/MULTI-IA-AETHAS38/catalogue_dynamique.json"
|
CATALOGUE_FILE = "/DATA/AppData/MULTI-IA-AETHAS38/catalogue_dynamique.json"
|
||||||
|
|
||||||
|
def recuperer_modeles_hf():
|
||||||
|
"""
|
||||||
|
Récupère les modèles populaires de Hugging Face (Texte, Image, Vidéo).
|
||||||
|
Les identifiants sont préfixés par 'huggingface/' pour le routage Multi-IA.
|
||||||
|
"""
|
||||||
|
api_key = os.getenv("HUGGINGFACE_API_KEY")
|
||||||
|
headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
|
||||||
|
|
||||||
|
# Catégories et nombre limite de modèles à récupérer pour garantir les plus fiables
|
||||||
|
categories = {
|
||||||
|
"text-generation": 20, # 20 LLMs texte
|
||||||
|
"text-to-image": 5, # 5 Générateurs d'images
|
||||||
|
"text-to-video": 2 # 2 Générateurs vidéo
|
||||||
|
}
|
||||||
|
|
||||||
|
hf_models = []
|
||||||
|
for tag, limit in categories.items():
|
||||||
|
url = "https://huggingface.co/api/models"
|
||||||
|
params = {
|
||||||
|
"pipeline_tag": tag,
|
||||||
|
"sort": "downloads",
|
||||||
|
"direction": -1,
|
||||||
|
"limit": limit
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
resp = requests.get(url, headers=headers, params=params, timeout=15)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
for m in resp.json():
|
||||||
|
# Formatage identifiable pour l'orchestrateur
|
||||||
|
hf_models.append(f"huggingface/{m['id']}")
|
||||||
|
except Exception as e:
|
||||||
|
log_event("WARNING", "UPDATER", f"Erreur de récupération HF pour {tag}: {e}")
|
||||||
|
|
||||||
|
return hf_models
|
||||||
|
|
||||||
def _do_update():
|
def _do_update():
|
||||||
ollama_host = os.getenv("OLLAMA_HOST", "http://192.168.1.20:11434")
|
ollama_host = os.getenv("OLLAMA_HOST", "http://192.168.1.20:11434")
|
||||||
try:
|
try:
|
||||||
@@ -21,25 +56,32 @@ def _do_update():
|
|||||||
|
|
||||||
# MAJ DU CATALOGUE COMPLET OPENROUTER
|
# MAJ DU CATALOGUE COMPLET OPENROUTER
|
||||||
resp_or = requests.get("https://openrouter.ai/api/v1/models", timeout=30)
|
resp_or = requests.get("https://openrouter.ai/api/v1/models", timeout=30)
|
||||||
|
modeles_gratuits = []
|
||||||
|
modeles_payants = []
|
||||||
|
|
||||||
if resp_or.status_code == 200:
|
if resp_or.status_code == 200:
|
||||||
data = resp_or.json().get("data", [])
|
data = resp_or.json().get("data", [])
|
||||||
modeles_gratuits = []
|
|
||||||
modeles_payants = []
|
|
||||||
|
|
||||||
for m in data:
|
for m in data:
|
||||||
pricing = m.get("pricing", {})
|
pricing = m.get("pricing", {})
|
||||||
|
|
||||||
# S'il est 100% gratuit
|
# S'il est 100% gratuit
|
||||||
if pricing.get("prompt") == "0" and pricing.get("completion") == "0":
|
if pricing.get("prompt") == "0" and pricing.get("completion") == "0":
|
||||||
if "nvidia/nemotron" not in m["id"].lower(): # On garde juste cette exclusion sécurité
|
if "nvidia/nemotron" not in m["id"].lower(): # On garde l'exclusion sécurité
|
||||||
modeles_gratuits.append(m["id"])
|
modeles_gratuits.append(m["id"])
|
||||||
else:
|
else:
|
||||||
# On intègre TOUS les modèles payants (Texte, Vision, 3D, etc.)
|
# Intégration de TOUS les modèles payants
|
||||||
modeles_payants.append(m["id"])
|
modeles_payants.append(m["id"])
|
||||||
|
else:
|
||||||
with open(CATALOGUE_FILE, "w", encoding="utf-8") as f:
|
log_event("WARNING", "UPDATER", "Échec de récupération du catalogue OpenRouter.")
|
||||||
json.dump({"free_models": modeles_gratuits, "paid_models": modeles_payants}, f)
|
|
||||||
log_event("INFO", "UPDATER", f"Catalogue Global mis à jour : {len(modeles_gratuits)} gratuits, {len(modeles_payants)} payants détectés.")
|
# MAJ DU CATALOGUE HUGGING FACE
|
||||||
|
hf_gratuits = recuperer_modeles_hf()
|
||||||
|
modeles_gratuits.extend(hf_gratuits)
|
||||||
|
|
||||||
|
with open(CATALOGUE_FILE, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({"free_models": modeles_gratuits, "paid_models": modeles_payants}, f)
|
||||||
|
|
||||||
|
log_event("INFO", "UPDATER", f"Catalogue Global mis à jour : {len(modeles_gratuits)} gratuits (dont {len(hf_gratuits)} HF), {len(modeles_payants)} payants détectés.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_event("ERROR", "UPDATER", f"Erreur lors de la MAJ auto : {e}")
|
log_event("ERROR", "UPDATER", f"Erreur lors de la MAJ auto : {e}")
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import json
|
|
||||||
import re
|
|
||||||
|
|
||||||
def nettoyer_decision_router(raw_text):
|
|
||||||
try:
|
|
||||||
clean = re.sub(r'```(?:json)?\s*', '', raw_text)
|
|
||||||
clean = clean.replace('```', '').strip()
|
|
||||||
start = clean.find('{')
|
|
||||||
end = clean.rfind('}') + 1
|
|
||||||
if start != -1 and end != 0:
|
|
||||||
return json.loads(clean[start:end])
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return {"intention": "Analyse", "fournisseur": "gemini"}
|
|
||||||
Reference in new issue
Block a user