Public Access
Auto-updater, limites CPU 60% et facturation corrigés
Build and Push Docker Image / build-and-push (push) Successful in 1m55s
Build and Push Docker Image / build-and-push (push) Successful in 1m55s
This commit is contained in:
1 parent
71d6f51ee0
commit
b4ed2947d4
15 files changed
+7413
-122
No files matched your search
+90
-31
@@ -1,18 +1,54 @@
|
||||
import os
|
||||
import json
|
||||
from datetime import date
|
||||
import uuid
|
||||
import time
|
||||
import re
|
||||
import psutil
|
||||
from fasthtml.common import *
|
||||
from modules.database import load_history, save_history
|
||||
from modules.scraper import rechercher_sur_google, extraire_contenu_urls
|
||||
from modules.router import nettoyer_decision_router
|
||||
from modules.ai_local import interroger_local_ollama
|
||||
from modules.ai_engine import executer_requete_ia
|
||||
from modules.ui_components import render_user_msg, render_ai_msg, render_sidebar
|
||||
from modules.ui_components import render_user_msg, render_ai_msg, render_sidebar, render_popup_paiement
|
||||
from modules.logger import log_event
|
||||
from modules.billing_manager import verifier_et_valider_budget
|
||||
from modules.rag_manager import rechercher_contexte_rag
|
||||
from modules.billing_manager import verifier_et_valider_budget, obtenir_solde
|
||||
from modules.rag_manager import rechercher_contexte_rag, auto_apprendre_rag
|
||||
from modules.moderator import analyser_intention_illicite, appliquer_sanction_et_archiver
|
||||
|
||||
def check_and_update_resource_quota(utilisateur_nom, role_utilisateur):
|
||||
"""Vérifie si l'utilisateur a dépassé son quota quotidien d'utilisation intensive du serveur (>60%)."""
|
||||
quota_file = "/DATA/AppData/MULTI-IA-AETHAS38/Utilisateurs/quotas_ressources.json"
|
||||
today = str(date.today())
|
||||
|
||||
limites = {"super admin": 10, "admin": 10, "vip": 5, "famille": 5, "standard": 3}
|
||||
limite_max = limites.get(role_utilisateur.lower(), 3)
|
||||
|
||||
try:
|
||||
if os.path.exists(quota_file):
|
||||
with open(quota_file, "r") as f:
|
||||
data = json.load(f)
|
||||
else:
|
||||
data = {}
|
||||
except Exception:
|
||||
data = {}
|
||||
|
||||
if data.get("date") != today:
|
||||
data = {"date": today, "users": {}}
|
||||
|
||||
user_uses = data["users"].get(utilisateur_nom, 0)
|
||||
|
||||
if user_uses >= limite_max:
|
||||
return False # Quota dépassé
|
||||
|
||||
data["users"][utilisateur_nom] = user_uses + 1
|
||||
os.makedirs(os.path.dirname(quota_file), exist_ok=True)
|
||||
with open(quota_file, "w") as f:
|
||||
json.dump(data, f)
|
||||
|
||||
return True
|
||||
|
||||
async def handle_chat_request(request, session):
|
||||
start_time = time.time()
|
||||
try:
|
||||
@@ -23,6 +59,13 @@ async def handle_chat_request(request, session):
|
||||
|
||||
mode_payant = form.get("mode_payant") == "on"
|
||||
mode_multi_ia = form.get("mode_multi_ia") == "on"
|
||||
validation_acquise = form.get("validation_acquise") == "true"
|
||||
choix_utilisateur = form.get("choix_utilisateur", "aucun")
|
||||
|
||||
if choix_utilisateur != "aucun":
|
||||
log_event("INFO", "BILLING", f"Décision utilisateur face au pop-up : {choix_utilisateur}")
|
||||
if choix_utilisateur == "gratuit_force":
|
||||
mode_payant = False # Le client a reculé devant le prix
|
||||
|
||||
session["current_chat_id"] = chat_id
|
||||
history = load_history()
|
||||
@@ -31,16 +74,20 @@ async def handle_chat_request(request, session):
|
||||
role_utilisateur = "Super Admin" if utilisateur_nom.lower() == "xavier" else ("VIP" if utilisateur_nom.lower() in ["céline", "anaëlle", "renan", "audric"] else "standard")
|
||||
ip_client = request.client.host if request.client else "Inconnue"
|
||||
|
||||
# Modération préventive locale
|
||||
est_illicite, raison = analyser_intention_illicite(prompt, "qwen2.5-coder:3b")
|
||||
# Protection CPU/RAM (60%) combinée au Quota Utilisateur
|
||||
cpu_usage = psutil.cpu_percent(interval=0.1)
|
||||
ram_usage = psutil.virtual_memory().percent
|
||||
if cpu_usage > 60.0 or ram_usage > 60.0:
|
||||
if not check_and_update_resource_quota(utilisateur_nom, role_utilisateur):
|
||||
log_event("WARNING", "SYSTEM", f"Surcharge (CPU: {cpu_usage}%, RAM: {ram_usage}%) + Quota intensif dépassé pour {utilisateur_nom}.")
|
||||
return Div(H3("⚠️ Limite de requêtes intensives atteinte", style="color:#ff9800;"), P(f"Le serveur est actuellement très sollicité (>60%). En tant que '{role_utilisateur}', vous avez épuisé votre quota de passages en force pour aujourd'hui."), style="background:#333; padding:15px; border-radius:10px; border: 1px solid #ff9800;")
|
||||
else:
|
||||
log_event("INFO", "SYSTEM", f"Serveur en charge (>60%), mais quota intensif autorisé utilisé par {utilisateur_nom}.")
|
||||
|
||||
est_illicite, raison = analyser_intention_illicite(prompt)
|
||||
if est_illicite:
|
||||
sanction = appliquer_sanction_et_archiver(utilisateur_nom, prompt, raison, ip_client)
|
||||
return Div(
|
||||
H3("⚠️ Avertissement de Sécurité"),
|
||||
P("Votre requête enfreint les conditions d'utilisation de la plateforme."),
|
||||
P(f"Sanction : {sanction}"),
|
||||
style="color:red; background:#300; padding:15px; border-radius:10px;"
|
||||
)
|
||||
return Div(H3("⚠️ Avertissement de Sécurité"), P("Votre requête enfreint les conditions d'utilisation de la plateforme."), P(f"Sanction : {sanction}"), style="color:red; background:#300; padding:15px; border-radius:10px;")
|
||||
|
||||
file_summary = []
|
||||
file_names = []
|
||||
@@ -54,7 +101,6 @@ async def handle_chat_request(request, session):
|
||||
log_mode = "PAYANT 👑" if mode_payant else "GRATUIT 🆓"
|
||||
log_event("INFO", "SYSTEM", f"Requête reçue [{log_mode}]. Fichiers joints : {liste_fichiers_str}")
|
||||
|
||||
# NOUVEAU PROMPT : On demande à Qwen de deviner le thème RAG
|
||||
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.
|
||||
- "openrouter_chat" : requêtes générales, texte rapide, traduction.
|
||||
@@ -66,7 +112,7 @@ async def handle_chat_request(request, session):
|
||||
Requête : {prompt}"""
|
||||
|
||||
try:
|
||||
texte_routeur, _, _, _ = interroger_local_ollama(sec_prompt, "qwen2.5-coder:3b")
|
||||
texte_routeur, _, _, _ = interroger_local_ollama(sec_prompt)
|
||||
decision = nettoyer_decision_router(texte_routeur)
|
||||
except Exception:
|
||||
decision = {"intention": "Analyse de secours", "fournisseur": "openrouter_code" if "minecraft" in prompt.lower() else "openrouter_chat", "theme_rag": "aucun"}
|
||||
@@ -75,14 +121,17 @@ async def handle_chat_request(request, session):
|
||||
fournisseur = decision.get("fournisseur", "openrouter_chat").lower()
|
||||
thematique_rag = decision.get("theme_rag", "aucun").lower()
|
||||
|
||||
if mode_payant or mode_multi_ia:
|
||||
fournisseur = "openrouter_code"
|
||||
intention += " (Forcé API Premium/Multi via UI)"
|
||||
|
||||
if file_names:
|
||||
fournisseur = "gemini"
|
||||
intention += " (Forcé Gemini via Sécurité Fichiers)"
|
||||
|
||||
# Récupération dynamique du contexte RAG
|
||||
contexte_rag = ""
|
||||
if thematique_rag != "aucun":
|
||||
contexte_rag = rechercher_contexte_rag(prompt, thematique=thematique_rag)
|
||||
contexte_rag = rechercher_contexte_rag(prompt, categorie=thematique_rag)
|
||||
|
||||
urls = re.findall(r"https?://[^\s]+", prompt)
|
||||
mots_cles = ["cherche", "recherche", "sur internet", "sur le web", "sur le net", "trouve", "site"]
|
||||
@@ -91,44 +140,54 @@ async def handle_chat_request(request, session):
|
||||
|
||||
web_text = extraire_contenu_urls(list(set(urls))) if urls else ""
|
||||
|
||||
# Concaténation de tous les contextes
|
||||
full_prompt = prompt + ("\n" + "\n".join(file_summary) if file_summary else "")
|
||||
if contexte_rag:
|
||||
full_prompt += f"\n\n[Contexte Documentaire RAG (Base de connaissances interne) :\n{contexte_rag}]"
|
||||
if web_text:
|
||||
full_prompt += f"\n\n[Contexte web :\n{web_text}]"
|
||||
|
||||
# Validation financière
|
||||
if "openrouter" in fournisseur:
|
||||
modele_estime = "anthropic/claude-3.5-sonnet" if mode_payant else "cohere/north-mini-code:free"
|
||||
verifier_et_valider_budget(full_prompt, modele_estime, utilisateur_nom=utilisateur_nom, rang_utilisateur=role_utilisateur, multi_ia=mode_multi_ia)
|
||||
|
||||
if mode_payant and not validation_acquise:
|
||||
from modules.billing_manager import estimer_cout_facture
|
||||
_, prix_estime, _ = estimer_cout_facture(full_prompt, modele_estime, rang_utilisateur=role_utilisateur, multi_ia=mode_multi_ia)
|
||||
if prix_estime > 0:
|
||||
return render_popup_paiement(prix_estime, prompt, chat_id, mode_multi_ia)
|
||||
|
||||
# Exécution rapide
|
||||
reponse, couleur, fournisseur_nom, modele = executer_requete_ia(
|
||||
fournisseur, full_prompt, mode_payant=mode_payant, multi_ia=mode_multi_ia
|
||||
)
|
||||
# Empêche un utilisateur classique avec un solde à zéro de consommer du Premium
|
||||
if mode_payant and role_utilisateur.lower() not in ["super admin", "admin", "vip", "famille"]:
|
||||
from modules.billing_manager import estimer_cout_facture
|
||||
_, prix_estime, _ = estimer_cout_facture(full_prompt, modele_estime, rang_utilisateur=role_utilisateur, multi_ia=mode_multi_ia)
|
||||
if prix_estime > obtenir_solde(utilisateur_nom):
|
||||
return Div(H3("❌ Solde Insuffisant", style="color:red;"), P("Veuillez recharger votre compte."), style="background:#300; padding:15px; border-radius:10px;")
|
||||
|
||||
reponse, couleur, fournisseur_nom, modele = executer_requete_ia(fournisseur, full_prompt, mode_payant=mode_payant, multi_ia=mode_multi_ia, chat_id=chat_id)
|
||||
|
||||
if "openrouter" in fournisseur_nom.lower() and mode_payant:
|
||||
verifier_et_valider_budget(full_prompt, modele, utilisateur_nom=utilisateur_nom, rang_utilisateur=role_utilisateur, multi_ia=mode_multi_ia)
|
||||
|
||||
if thematique_rag != "aucun" and reponse:
|
||||
auto_apprendre_rag(prompt, reponse, thematique_rag)
|
||||
|
||||
if chat_id not in history:
|
||||
history[chat_id] = {"title": (prompt[:30] if prompt else "Discussion") + "...", "pinned": False, "timestamp": time.time(), "messages": []}
|
||||
|
||||
execution_time = round(time.time() - start_time, 1)
|
||||
|
||||
display_msg = prompt
|
||||
if file_names:
|
||||
display_msg += f"\n\n📎 **Fichiers joints :** {', '.join(file_names)}"
|
||||
display_msg += "\n> *🗑️ Note : Fichiers traités de manière éphémère en mémoire vive.*"
|
||||
display_msg += f"\n\n📎 **Fichiers joints :** {', '.join(file_names)}\n> *🗑️ Note : Fichiers traités de manière éphémère en mémoire vive.*"
|
||||
|
||||
history[chat_id]["messages"].append({"role": "user", "content": display_msg})
|
||||
history[chat_id]["messages"].append({
|
||||
"role": "ai", "content": reponse, "intention": intention,
|
||||
"fournisseur": fournisseur_nom, "modele": modele,
|
||||
"couleur": couleur, "execution_time": execution_time
|
||||
})
|
||||
history[chat_id]["messages"].append({"role": "ai", "content": reponse, "intention": intention, "fournisseur": fournisseur_nom, "modele": modele, "couleur": couleur, "execution_time": execution_time})
|
||||
save_history(history)
|
||||
|
||||
|
||||
log_event("INFO", fournisseur_nom, f"Réponse générée en {execution_time}s.")
|
||||
return render_user_msg(display_msg), render_ai_msg(reponse, intention, fournisseur_nom, modele, couleur, execution_time), Script("renderMarkdown();"), render_sidebar()
|
||||
|
||||
except Exception as e:
|
||||
if "SOLDE_INSUFFISANT" in str(e):
|
||||
details = str(e).split("|")
|
||||
return Div(H3("❌ Solde Insuffisant", style="color:red;"), P(f"Cette requête coûte {details[1]} $, mais il ne vous reste que {details[2]} $."), P("Veuillez recharger votre compte ou désactiver le mode Payant."), style="background:#300; padding:15px; border-radius:10px; border: 1px solid red;")
|
||||
log_event("CRITICAL", "SYSTEM", f"Erreur dans handle_chat_request : {e}")
|
||||
return Div(f"Erreur système : {e}", style="color:red; background:#300; padding:15px; border-radius:10px;")
|
||||
Reference in new issue
Block a user