Public Access
feat: Intégration de l'API OpenRouter, suppression de Groq et mise à jour du README
This commit is contained in:
1 parent
909e2e995a
commit
317ecddc56
5 files changed
+128
-121
No files matched your search
+18
-15
@@ -1,11 +1,8 @@
|
||||
from modules.logger import log_event
|
||||
from modules.ai_gemini import interroger_gemini
|
||||
from modules.ai_groq import interroger_groq
|
||||
from modules.ai_deepseek import interroger_deepseek
|
||||
from modules.ai_mistral import interroger_mistral
|
||||
from modules.ai_huggingface import interroger_huggingface
|
||||
from modules.ai_cloudflare import interroger_cloudflare
|
||||
from modules.ai_local import interroger_local_ollama
|
||||
# Le nouveau module que nous allons créer à la prochaine étape
|
||||
from modules.ai_openrouter import interroger_openrouter
|
||||
|
||||
SYSTEM_CONFIDENTIALITY_INSTRUCTION = (
|
||||
"RÈGLE DE CONFIDENTIALITÉ ABSOLUE : Tu es un assistant intégré à la plateforme privée Aethas38. "
|
||||
@@ -15,21 +12,27 @@ SYSTEM_CONFIDENTIALITY_INSTRUCTION = (
|
||||
)
|
||||
|
||||
def executer_requete_ia(fournisseur, prompt):
|
||||
"""Moteur central gérant les consignes de sécurité et la cascade intelligente."""
|
||||
"""Moteur central gérant les consignes de sécurité et la cascade intelligente vers OpenRouter."""
|
||||
prompt_securise = f"{SYSTEM_CONFIDENTIALITY_INSTRUCTION}\n\nRequête utilisateur :\n{prompt}"
|
||||
|
||||
# 🔄 Ordre de cascade intelligent défini par l'orchestrateur (repli automatique)
|
||||
if fournisseur == "gemini": ordre_repli = [interroger_gemini, interroger_groq, interroger_mistral, interroger_deepseek, interroger_huggingface, interroger_cloudflare, interroger_local_ollama]
|
||||
elif fournisseur == "groq": ordre_repli = [interroger_groq, interroger_mistral, interroger_deepseek, interroger_gemini, interroger_huggingface, interroger_cloudflare, interroger_local_ollama]
|
||||
elif fournisseur == "deepseek": ordre_repli = [interroger_deepseek, interroger_gemini, interroger_groq, interroger_mistral, interroger_local_ollama]
|
||||
elif fournisseur == "mistral": ordre_repli = [interroger_mistral, interroger_groq, interroger_gemini, interroger_huggingface, interroger_local_ollama]
|
||||
elif fournisseur == "huggingface": ordre_repli = [interroger_huggingface, interroger_cloudflare, interroger_groq, interroger_gemini, interroger_local_ollama]
|
||||
elif fournisseur == "cloudflare": ordre_repli = [interroger_cloudflare, interroger_huggingface, interroger_groq, interroger_gemini, interroger_local_ollama]
|
||||
else: ordre_repli = [interroger_local_ollama, interroger_gemini, interroger_groq]
|
||||
# 🔄 Ordre de cascade recentré sur OpenRouter (Gratuit), Gemini et le Local
|
||||
if fournisseur == "gemini":
|
||||
ordre_repli = [interroger_gemini, interroger_openrouter, interroger_local_ollama]
|
||||
elif fournisseur == "openrouter_code":
|
||||
ordre_repli = [interroger_openrouter, interroger_gemini, interroger_local_ollama]
|
||||
elif fournisseur == "openrouter_chat":
|
||||
ordre_repli = [interroger_openrouter, interroger_gemini, interroger_local_ollama]
|
||||
else:
|
||||
ordre_repli = [interroger_local_ollama, interroger_openrouter, interroger_gemini]
|
||||
|
||||
for fonction_api in ordre_repli:
|
||||
try:
|
||||
texte, couleur, nom_fournisseur, nom_modele = fonction_api(prompt_securise)
|
||||
# Transfert du 'fournisseur' cible pour qu'OpenRouter sache s'il doit appeler un modèle Code ou Chat
|
||||
if fonction_api.__name__ == 'interroger_openrouter':
|
||||
texte, couleur, nom_fournisseur, nom_modele = fonction_api(prompt_securise, type_modele=fournisseur)
|
||||
else:
|
||||
texte, couleur, nom_fournisseur, nom_modele = fonction_api(prompt_securise)
|
||||
|
||||
if texte:
|
||||
return texte, couleur, nom_fournisseur, nom_modele
|
||||
except Exception as e:
|
||||
|
||||
+54
-27
@@ -1,36 +1,63 @@
|
||||
import os
|
||||
import time
|
||||
import openai
|
||||
from openai import OpenAI
|
||||
from modules.logger import log_event
|
||||
|
||||
def interroger_openrouter(prompt_securise):
|
||||
"""Module dédié à OpenRouter.ai pour accéder à +100 modèles gratuitement."""
|
||||
api_key = os.getenv("OPENROUTER_API_KEY")
|
||||
if not api_key: raise Exception("Clé API OpenRouter manquante.")
|
||||
# Initialisation du client OpenRouter avec l'API compatible OpenAI
|
||||
client = OpenAI(
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
api_key=os.getenv("OPENROUTER_API_KEY", "VOTRE_CLE_ICI") # À définir dans le .env
|
||||
)
|
||||
|
||||
def interroger_openrouter(prompt, type_modele="openrouter_chat", max_retries=3):
|
||||
"""
|
||||
Interroge l'API OpenRouter avec gestion des erreurs 402 (Plafond) et 429 (Rate Limit).
|
||||
Sélectionne un modèle gratuit selon l'intention (Chat ou Code).
|
||||
"""
|
||||
|
||||
# Client compatible OpenAI pointant vers l'agrégateur OpenRouter
|
||||
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key)
|
||||
|
||||
# Liste de modèles 100% gratuits et performants sur OpenRouter
|
||||
modeles = [
|
||||
"meta-llama/llama-3.1-8b-instruct:free",
|
||||
"mistralai/mistral-7b-instruct:free",
|
||||
"google/gemma-2-9b-it:free",
|
||||
"microsoft/phi-3-mini-128k-instruct:free"
|
||||
]
|
||||
|
||||
for modele in modeles:
|
||||
# Sélection des modèles 100% gratuits et performants sur OpenRouter
|
||||
if type_modele == "openrouter_code":
|
||||
nom_modele_api = "qwen/qwen-2.5-coder-32b-instruct:free"
|
||||
nom_affichage = "OpenRouter (Qwen Coder - Gratuit)"
|
||||
couleur = "#10a37f" # Vert émeraude
|
||||
else:
|
||||
nom_modele_api = "meta-llama/llama-3.1-8b-instruct:free"
|
||||
nom_affichage = "OpenRouter (Llama 3.1 - Gratuit)"
|
||||
couleur = "#4a90e2" # Bleu clair
|
||||
|
||||
for tentative in range(max_retries):
|
||||
try:
|
||||
resp = client.chat.completions.create(
|
||||
model=modele,
|
||||
messages=[
|
||||
{"role": "system", "content": "Réponds en français et en format Markdown."},
|
||||
{"role": "user", "content": prompt_securise}
|
||||
]
|
||||
start_time = time.time()
|
||||
response = client.chat.completions.create(
|
||||
model=nom_modele_api,
|
||||
messages=[{"role": "user", "content": prompt}]
|
||||
)
|
||||
if resp and resp.choices:
|
||||
log_event("INFO", f"OPENROUTER-{modele}", "Succès de la requête.")
|
||||
return resp.choices[0].message.content, "#8b5cf6", "OPENROUTER (Gratuit)", modele
|
||||
texte = response.choices[0].message.content
|
||||
return texte, couleur, "OpenRouter", nom_affichage
|
||||
|
||||
except openai.RateLimitError as e:
|
||||
temps_attente = 2 ** tentative
|
||||
log_event("WARNING", "OPENROUTER", f"Limite de taux atteinte (429). Essai {tentative + 1}/{max_retries}. Attente de {temps_attente}s...")
|
||||
time.sleep(temps_attente)
|
||||
if tentative == max_retries - 1:
|
||||
log_event("ERROR", "OPENROUTER", "Échec définitif suite aux limites de taux (429).")
|
||||
raise Exception("Service temporairement saturé. Veuillez réessayer.")
|
||||
|
||||
except openai.APIStatusError as e:
|
||||
if e.status_code == 402:
|
||||
log_event("CRITICAL", "OPENROUTER", "Erreur 402 : Fonds insuffisants ou limite de clé atteinte.")
|
||||
raise Exception("Action requise : Limite de facturation atteinte sur OpenRouter.")
|
||||
else:
|
||||
log_event("ERROR", "OPENROUTER", f"Code {e.status_code} : {e.message}")
|
||||
raise Exception(f"Erreur API OpenRouter : {e.status_code}")
|
||||
|
||||
except openai.APIConnectionError as e:
|
||||
log_event("ERROR", "OPENROUTER", f"Erreur réseau : {e}")
|
||||
raise Exception("Impossible de joindre les serveurs d'OpenRouter.")
|
||||
|
||||
except Exception as e:
|
||||
log_event("WARNING", f"OPENROUTER-{modele}", f"Erreur: {e}")
|
||||
log_event("ERROR", "OPENROUTER", f"Erreur inattendue : {e}")
|
||||
raise e
|
||||
|
||||
raise Exception("Tous les modèles gratuits OpenRouter ont échoué.")
|
||||
return None, None, None, None
|
||||
+8
-10
@@ -33,26 +33,24 @@ async def handle_chat_request(request, session):
|
||||
liste_fichiers_str = ', '.join(file_names) if file_names else 'Aucun fichier'
|
||||
log_event("INFO", "SYSTEM", f"Requête reçue. Fichiers joints : {liste_fichiers_str}")
|
||||
|
||||
# L'IA Local (Qwen) orchestre la décision !
|
||||
# L'IA Local (Qwen) orchestre la décision ! Focus sur l'efficience.
|
||||
sec_prompt = f"""Tu es l'Orchestrateur système. Analyse la requête et choisis le meilleur fournisseur IA :
|
||||
- "gemini" : (OBLIGATOIRE si fichiers joints), rédaction complexe, comparaisons ou images.
|
||||
- "groq" : texte ultra-rapide.
|
||||
- "deepseek" : code, mathématiques, logique complexe.
|
||||
- "mistral" : rédaction française soignée.
|
||||
- "huggingface" : modèles open-source.
|
||||
- "cloudflare" : requêtes générales.
|
||||
- "local" : questions basiques, salutations.
|
||||
Renvoie STRICTEMENT un JSON : {{"intention": "Résumé...", "fournisseur": "gemini|groq|deepseek|mistral|huggingface|cloudflare|local"}}
|
||||
- "gemini" : (OBLIGATOIRE si fichiers joints), rédaction complexe, comparaisons ou recherches web.
|
||||
- "openrouter_chat" : requêtes générales, texte rapide, traduction, bureautique (Modèles gratuits).
|
||||
- "openrouter_code" : programmation, scripts, mathématiques, logique (Modèles gratuits).
|
||||
- "local" : salutations basiques, questions très simples.
|
||||
Renvoie STRICTEMENT un JSON : {{"intention": "Résumé...", "fournisseur": "gemini|openrouter_chat|openrouter_code|local"}}
|
||||
Requête : {prompt}"""
|
||||
|
||||
try:
|
||||
# Qwen 2.5 3B reste le meilleur compromis VRAM/Efficience pour votre GTX 1650
|
||||
texte_routeur, _, _, _ = interroger_local_ollama(sec_prompt, "qwen2.5:3b")
|
||||
decision = nettoyer_decision_router(texte_routeur)
|
||||
except Exception:
|
||||
decision = {"intention": "Analyse de secours", "fournisseur": "gemini"}
|
||||
|
||||
intention = decision.get('intention', 'Analyse')
|
||||
fournisseur = decision.get('fournisseur', 'gemini').lower()
|
||||
fournisseur = decision.get('fournisseur', 'openrouter_chat').lower()
|
||||
|
||||
# Sécurité Absolue Fichiers -> Gemini (Fenêtre de contexte maximale)
|
||||
if file_names:
|
||||
|
||||
Reference in new issue
Block a user