import os import json import requests import threading import time from modules.logger import log_event CATALOGUE_FILE = "/DATA/AppData/MULTI-IA-AETHAS38/catalogue_dynamique.json" LOCK_FILE = "/DATA/AppData/MULTI-IA-AETHAS38/updater_lock.txt" 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(): # --- SYSTÈME ANTI-SPAM (VERROU INTER-PROCESSUS) --- now = time.time() try: if os.path.exists(LOCK_FILE): with open(LOCK_FILE, "r") as f: last_update = float(f.read().strip()) # Si la dernière MAJ date de moins d'une heure (3600 sec), on annule l'exécution de ce thread if now - last_update < 3600: return except Exception: pass # En cas d'erreur de lecture, on force la MAJ # On pose le verrou avec le timestamp actuel pour bloquer les autres workers try: os.makedirs(os.path.dirname(LOCK_FILE), exist_ok=True) with open(LOCK_FILE, "w") as f: f.write(str(now)) except Exception as e: log_event("WARNING", "UPDATER", f"Impossible d'écrire le verrou : {e}") # --- FIN DU SYSTÈME ANTI-SPAM --- ollama_host = os.getenv("OLLAMA_HOST", "http://192.168.1.20:11434") try: # MAJ locale Abliterated resp_local = requests.post( f"{ollama_host}/api/pull", json={"name": "richardyoung/llama-3.2-3b-instruct-abliterated"}, timeout=300 ) if resp_local.status_code == 200: log_event("INFO", "UPDATER", "Modèle local Abliterated vérifié/mis à jour.") # MAJ DU CATALOGUE COMPLET OPENROUTER resp_or = requests.get("https://openrouter.ai/api/v1/models", timeout=30) modeles_gratuits = [] modeles_payants = [] if resp_or.status_code == 200: data = resp_or.json().get("data", []) for m in data: pricing = m.get("pricing", {}) # S'il est 100% gratuit if pricing.get("prompt") == "0" and pricing.get("completion") == "0": if "nvidia/nemotron" not in m["id"].lower(): # On garde l'exclusion sécurité modeles_gratuits.append(m["id"]) else: # Intégration de TOUS les modèles payants modeles_payants.append(m["id"]) else: log_event("WARNING", "UPDATER", "Échec de récupération du catalogue OpenRouter.") # 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: log_event("ERROR", "UPDATER", f"Erreur lors de la MAJ auto : {e}") def trigger_update_now(): # Force la mise à jour en supprimant le verrou au préalable if os.path.exists(LOCK_FILE): try: os.remove(LOCK_FILE) except: pass threading.Thread(target=_do_update, daemon=True).start() def update_models_background(): # Boucle de fond while True: _do_update() time.sleep(7200) # Attente 2 heures threading.Thread(target=update_models_background, daemon=True).start()