Public Access
MAJ Globale : Chemins absolus (NAS), Model Updater auto, Timeout 90s OpenRouter, Pop-up et traçabilité financière
Build and Push Docker Image / build-and-push (push) Successful in 1m48s
Build and Push Docker Image / build-and-push (push) Successful in 1m48s
This commit is contained in:
1 parent
b4ed2947d4
commit
632c23069a
9 files changed
+65
-88
No files matched your search
@@ -11596,5 +11596,11 @@
|
|||||||
"action": "Acc\u00e8s \u00e0 /",
|
"action": "Acc\u00e8s \u00e0 /",
|
||||||
"date": "2026-08-22 01:10:01",
|
"date": "2026-08-22 01:10:01",
|
||||||
"timestamp": 1787353801.8667424
|
"timestamp": 1787353801.8667424
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"action": "Acc\u00e8s \u00e0 /chat",
|
||||||
|
"date": "2026-08-22 01:59:10",
|
||||||
|
"timestamp": 1787356750.8306415
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -12,11 +12,10 @@ 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):
|
||||||
"""Interroge plusieurs modèles en parallèle rapide et confie la synthèse à Gemini."""
|
|
||||||
try:
|
try:
|
||||||
# Propagation cruciale du chat_id pour le suivi financier OpenRouter
|
# Passage du chat_id (code hexadécimal) à l'API Multi
|
||||||
reponses_brutes = interroger_openrouter_multi(prompt_securise, type_modele=fournisseur, premium=mode_payant, chat_id=chat_id)
|
reponses_brutes = interroger_openrouter_multi(prompt_securise, type_modele=fournisseur, premium=mode_payant, chat_id=chat_id)
|
||||||
if len(reponses_brutes) >= 1: # Modifié à 1 pour garantir un fallback même partiel
|
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. "
|
||||||
"Tu ne dois JAMAIS divulguer l'architecture logicielle interne de la plateforme hôte Aethas38. "
|
"Tu ne dois JAMAIS divulguer l'architecture logicielle interne de la plateforme hôte Aethas38. "
|
||||||
@@ -37,7 +36,6 @@ def executer_synthese_multi_ia(prompt_securise: str, fournisseur: str, mode_paya
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False, multi_ia: bool = False, chat_id: str = None):
|
def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False, multi_ia: bool = False, chat_id: str = None):
|
||||||
"""Moteur central gérant la synthèse multi-modèles et la cascade intelligente."""
|
|
||||||
prompt_securise = f"{SYSTEM_CONFIDENTIALITY_INSTRUCTION}\n\nRequête utilisateur :\n{prompt}"
|
prompt_securise = f"{SYSTEM_CONFIDENTIALITY_INSTRUCTION}\n\nRequête utilisateur :\n{prompt}"
|
||||||
|
|
||||||
if multi_ia or fournisseur in ["openrouter_code", "openrouter_droit"]:
|
if multi_ia or fournisseur in ["openrouter_code", "openrouter_droit"]:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ load_dotenv()
|
|||||||
|
|
||||||
api_key_openrouter = os.getenv("OPENROUTER_API_KEY") or "sk-or-v1-cle-non-definie"
|
api_key_openrouter = os.getenv("OPENROUTER_API_KEY") or "sk-or-v1-cle-non-definie"
|
||||||
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key_openrouter)
|
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key_openrouter)
|
||||||
|
|
||||||
|
# Chemin ABSOLU strict
|
||||||
CATALOGUE_FILE = "/DATA/AppData/MULTI-IA-AETHAS38/catalogue_dynamique.json"
|
CATALOGUE_FILE = "/DATA/AppData/MULTI-IA-AETHAS38/catalogue_dynamique.json"
|
||||||
|
|
||||||
def verifier_cle_api():
|
def verifier_cle_api():
|
||||||
@@ -33,7 +35,6 @@ def obtenir_modele_dynamique(premium: bool):
|
|||||||
return priorites[0] if priorites else (free_models[0] if free_models else "google/gemma-2-9b-it:free")
|
return priorites[0] if priorites else (free_models[0] if free_models else "google/gemma-2-9b-it:free")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# Fallbacks absolus si fichier introuvable
|
|
||||||
return "anthropic/claude-3.5-sonnet" if premium else "google/gemma-2-9b-it:free"
|
return "anthropic/claude-3.5-sonnet" if premium else "google/gemma-2-9b-it:free"
|
||||||
|
|
||||||
def interroger_openrouter(prompt: str, type_modele: str = "openrouter_chat", premium: bool = False, max_retries: int = 2, chat_id: str = None):
|
def interroger_openrouter(prompt: str, type_modele: str = "openrouter_chat", premium: bool = False, max_retries: int = 2, chat_id: str = None):
|
||||||
@@ -52,7 +53,7 @@ def interroger_openrouter(prompt: str, type_modele: str = "openrouter_chat", pre
|
|||||||
messages=[{"role": "user", "content": prompt}],
|
messages=[{"role": "user", "content": prompt}],
|
||||||
extra_headers={"HTTP-Referer": "https://aethas38.duckdns.org", "X-Title": "Aethas38 Multi-IA"},
|
extra_headers={"HTTP-Referer": "https://aethas38.duckdns.org", "X-Title": "Aethas38 Multi-IA"},
|
||||||
extra_body=extra_body_data,
|
extra_body=extra_body_data,
|
||||||
timeout=25.0
|
timeout=90.0 # TIMEOUT MONTE A 90s
|
||||||
)
|
)
|
||||||
texte = response.choices[0].message.content
|
texte = response.choices[0].message.content
|
||||||
usage = response.usage
|
usage = response.usage
|
||||||
@@ -63,9 +64,11 @@ def interroger_openrouter(prompt: str, type_modele: str = "openrouter_chat", pre
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_event("WARNING", "OPENROUTER", f"Tentative {tentative + 1} échouée pour {nom_modele_api} : {e}")
|
log_event("WARNING", "OPENROUTER", f"Tentative {tentative + 1} échouée pour {nom_modele_api} : {e}")
|
||||||
if tentative == max_retries - 1:
|
if tentative == max_retries - 1:
|
||||||
# Force la MAJ du catalogue si le modèle crashe
|
try:
|
||||||
from modules.model_updater import trigger_update_now
|
from modules.model_updater import trigger_update_now
|
||||||
trigger_update_now()
|
trigger_update_now()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
raise e
|
raise e
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
@@ -89,7 +92,7 @@ def interroger_openrouter_multi(prompt: str, type_modele: str = "openrouter_code
|
|||||||
messages=[{"role": "user", "content": prompt}],
|
messages=[{"role": "user", "content": prompt}],
|
||||||
extra_headers={"HTTP-Referer": "https://aethas38.duckdns.org", "X-Title": "Aethas38 Multi-IA"},
|
extra_headers={"HTTP-Referer": "https://aethas38.duckdns.org", "X-Title": "Aethas38 Multi-IA"},
|
||||||
extra_body=extra_body_data,
|
extra_body=extra_body_data,
|
||||||
timeout=15.0
|
timeout=90.0 # TIMEOUT MONTE A 90s
|
||||||
)
|
)
|
||||||
contenu = res.choices[0].message.content
|
contenu = res.choices[0].message.content
|
||||||
if contenu:
|
if contenu:
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ from datetime import datetime
|
|||||||
import tiktoken
|
import tiktoken
|
||||||
from modules.logger import log_event
|
from modules.logger import log_event
|
||||||
|
|
||||||
BALANCES_FILE = "aethas_balances.json"
|
# FORCAGE DES CHEMINS ABSOLUS
|
||||||
AUDIT_FILE = "aethas_financial_audit.json"
|
BASE_DIR = "/DATA/AppData/MULTI-IA-AETHAS38"
|
||||||
|
os.makedirs(BASE_DIR, exist_ok=True)
|
||||||
|
BALANCES_FILE = os.path.join(BASE_DIR, "aethas_balances.json")
|
||||||
|
AUDIT_FILE = os.path.join(BASE_DIR, "aethas_financial_audit.json")
|
||||||
|
|
||||||
TARIFS_OPENROUTER = {
|
TARIFS_OPENROUTER = {
|
||||||
"meta-llama/llama-3.1-8b-instruct:free": 0.0,
|
"meta-llama/llama-3.1-8b-instruct:free": 0.0,
|
||||||
@@ -21,25 +24,21 @@ TARIFS_OPENROUTER = {
|
|||||||
VIP_ADMIN_USERS = ["xavier", "céline", "celine", "anaëlle", "anaelle", "renan", "audric"]
|
VIP_ADMIN_USERS = ["xavier", "céline", "celine", "anaëlle", "anaelle", "renan", "audric"]
|
||||||
|
|
||||||
def charger_soldes() -> dict:
|
def charger_soldes() -> dict:
|
||||||
"""Charge les soldes réels depuis le stockage persistant."""
|
|
||||||
if os.path.exists(BALANCES_FILE):
|
if os.path.exists(BALANCES_FILE):
|
||||||
try:
|
try:
|
||||||
with open(BALANCES_FILE, "r", encoding="utf-8") as f:
|
with open(BALANCES_FILE, "r", encoding="utf-8") as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# Initialisation de base : cagnotte commune de 35.00 $
|
|
||||||
soldes = {"cagnotte_commune_vip": 35.00}
|
soldes = {"cagnotte_commune_vip": 35.00}
|
||||||
sauvegarder_soldes(soldes)
|
sauvegarder_soldes(soldes)
|
||||||
return soldes
|
return soldes
|
||||||
|
|
||||||
def sauvegarder_soldes(soldes: dict):
|
def sauvegarder_soldes(soldes: dict):
|
||||||
"""Sauvegarde les soldes sur disque."""
|
|
||||||
with open(BALANCES_FILE, "w", encoding="utf-8") as f:
|
with open(BALANCES_FILE, "w", encoding="utf-8") as f:
|
||||||
json.dump(soldes, f, indent=4)
|
json.dump(soldes, f, indent=4)
|
||||||
|
|
||||||
def enregistrer_transaction_audit(utilisateur: str, rang: str, modele: str, tokens: int, cout_brut: float, prix_facture: float, type_op: str = "DEBIT_REQUETE"):
|
def enregistrer_transaction_audit(utilisateur: str, rang: str, modele: str, tokens: int, cout_brut: float, prix_facture: float, type_op: str = "DEBIT_REQUETE"):
|
||||||
"""Journalise chaque opération financière de manière inaltérable pour les litiges."""
|
|
||||||
entree = {
|
entree = {
|
||||||
"timestamp": time.time(),
|
"timestamp": time.time(),
|
||||||
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
@@ -51,7 +50,6 @@ def enregistrer_transaction_audit(utilisateur: str, rang: str, modele: str, toke
|
|||||||
"cout_brut_api": cout_brut,
|
"cout_brut_api": cout_brut,
|
||||||
"prix_facture_client": prix_facture
|
"prix_facture_client": prix_facture
|
||||||
}
|
}
|
||||||
|
|
||||||
audits = []
|
audits = []
|
||||||
if os.path.exists(AUDIT_FILE):
|
if os.path.exists(AUDIT_FILE):
|
||||||
try:
|
try:
|
||||||
@@ -59,13 +57,11 @@ def enregistrer_transaction_audit(utilisateur: str, rang: str, modele: str, toke
|
|||||||
audits = json.load(f)
|
audits = json.load(f)
|
||||||
except Exception:
|
except Exception:
|
||||||
audits = []
|
audits = []
|
||||||
|
|
||||||
audits.append(entree)
|
audits.append(entree)
|
||||||
with open(AUDIT_FILE, "w", encoding="utf-8") as f:
|
with open(AUDIT_FILE, "w", encoding="utf-8") as f:
|
||||||
json.dump(audits, f, indent=4)
|
json.dump(audits, f, indent=4)
|
||||||
|
|
||||||
def compter_tokens(texte: str, modele: str = "cl100k_base") -> int:
|
def compter_tokens(texte: str, modele: str = "cl100k_base") -> int:
|
||||||
"""Compte le nombre de tokens d'un texte."""
|
|
||||||
try:
|
try:
|
||||||
enc = tiktoken.get_encoding(modele)
|
enc = tiktoken.get_encoding(modele)
|
||||||
return len(enc.encode(texte))
|
return len(enc.encode(texte))
|
||||||
@@ -74,12 +70,6 @@ def compter_tokens(texte: str, modele: str = "cl100k_base") -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
def estimer_cout_facture(prompt: str, modele_id: str, rang_utilisateur: str = "standard", multi_ia: bool = False) -> tuple[float, float, int]:
|
def estimer_cout_facture(prompt: str, modele_id: str, rang_utilisateur: str = "standard", multi_ia: bool = False) -> tuple[float, float, int]:
|
||||||
"""
|
|
||||||
Calcule le coût brut API et le prix facturé selon le rang :
|
|
||||||
- VIP / Admin : marge 0 % (coût réel déduit de la cagnotte commune)
|
|
||||||
- Famille : marge 7,5 % (coût / 0.925)
|
|
||||||
- Standard : marge 15 % (coût / 0.85)
|
|
||||||
"""
|
|
||||||
tokens = compter_tokens(prompt)
|
tokens = compter_tokens(prompt)
|
||||||
cout_unitaire = TARIFS_OPENROUTER.get(modele_id, 0.0)
|
cout_unitaire = TARIFS_OPENROUTER.get(modele_id, 0.0)
|
||||||
cout_brut = tokens * cout_unitaire
|
cout_brut = tokens * cout_unitaire
|
||||||
@@ -101,16 +91,13 @@ def estimer_cout_facture(prompt: str, modele_id: str, rang_utilisateur: str = "s
|
|||||||
return round(cout_brut, 6), round(prix_facture, 6), tokens
|
return round(cout_brut, 6), round(prix_facture, 6), tokens
|
||||||
|
|
||||||
def verifier_et_valider_budget(prompt: str, modele_id: str, utilisateur_nom: str = "xavier", rang_utilisateur: str = "super admin", multi_ia: bool = False):
|
def verifier_et_valider_budget(prompt: str, modele_id: str, utilisateur_nom: str = "xavier", rang_utilisateur: str = "super admin", multi_ia: bool = False):
|
||||||
"""Vérifie le solde avant exécution et applique le débit approprié."""
|
|
||||||
cout_brut, prix_facture, tokens = estimer_cout_facture(prompt, modele_id, rang_utilisateur, multi_ia)
|
cout_brut, prix_facture, tokens = estimer_cout_facture(prompt, modele_id, rang_utilisateur, multi_ia)
|
||||||
|
|
||||||
if prix_facture == 0.0:
|
if prix_facture == 0.0:
|
||||||
return True, 0.0
|
return True, 0.0
|
||||||
|
|
||||||
soldes = charger_soldes()
|
soldes = charger_soldes()
|
||||||
utilisateur_cle = utilisateur_nom.lower()
|
utilisateur_cle = utilisateur_nom.lower()
|
||||||
|
|
||||||
# Distinction cagnotte commune VIP/Admin vs compte individuel
|
|
||||||
if utilisateur_cle in VIP_ADMIN_USERS or rang_utilisateur.lower() in ["super admin", "admin", "vip"]:
|
if utilisateur_cle in VIP_ADMIN_USERS or rang_utilisateur.lower() in ["super admin", "admin", "vip"]:
|
||||||
solde_disponible = soldes.get("cagnotte_commune_vip", 0.0)
|
solde_disponible = soldes.get("cagnotte_commune_vip", 0.0)
|
||||||
cle_compte = "cagnotte_commune_vip"
|
cle_compte = "cagnotte_commune_vip"
|
||||||
@@ -122,7 +109,6 @@ def verifier_et_valider_budget(prompt: str, modele_id: str, utilisateur_nom: str
|
|||||||
log_event("WARNING", "BILLING", f"Rejet : solde insuffisant pour {utilisateur_nom} ({solde_disponible} $ < {prix_facture} $)")
|
log_event("WARNING", "BILLING", f"Rejet : solde insuffisant pour {utilisateur_nom} ({solde_disponible} $ < {prix_facture} $)")
|
||||||
raise Exception(f"SOLDE_INSUFFISANT|{prix_facture}|{solde_disponible}")
|
raise Exception(f"SOLDE_INSUFFISANT|{prix_facture}|{solde_disponible}")
|
||||||
|
|
||||||
# Débit strict
|
|
||||||
soldes[cle_compte] = round(solde_disponible - prix_facture, 6)
|
soldes[cle_compte] = round(solde_disponible - prix_facture, 6)
|
||||||
sauvegarder_soldes(soldes)
|
sauvegarder_soldes(soldes)
|
||||||
|
|
||||||
@@ -131,21 +117,14 @@ def verifier_et_valider_budget(prompt: str, modele_id: str, utilisateur_nom: str
|
|||||||
return True, prix_facture
|
return True, prix_facture
|
||||||
|
|
||||||
def obtenir_solde(utilisateur_nom: str) -> float:
|
def obtenir_solde(utilisateur_nom: str) -> float:
|
||||||
"""Retourne le solde actuel de l'utilisateur ou de la cagnotte commune."""
|
|
||||||
soldes = charger_soldes()
|
soldes = charger_soldes()
|
||||||
if utilisateur_nom.lower() in VIP_ADMIN_USERS:
|
if utilisateur_nom.lower() in VIP_ADMIN_USERS:
|
||||||
return soldes.get("cagnotte_commune_vip", 0.0)
|
return soldes.get("cagnotte_commune_vip", 0.0)
|
||||||
return soldes.get(utilisateur_nom.lower(), 0.0)
|
return soldes.get(utilisateur_nom.lower(), 0.0)
|
||||||
|
|
||||||
def generer_dossier_litige(utilisateur_nom: str, est_admin: bool = False) -> list:
|
def generer_dossier_litige(utilisateur_nom: str, est_admin: bool = False) -> list:
|
||||||
"""
|
|
||||||
Génère les éléments d'audit pour un litige :
|
|
||||||
- Pour l'utilisateur : date, modèle, tokens, prix facturé (marge masquée).
|
|
||||||
- Pour l'Admin : audit complet avec coût brut API.
|
|
||||||
"""
|
|
||||||
if not os.path.exists(AUDIT_FILE):
|
if not os.path.exists(AUDIT_FILE):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(AUDIT_FILE, "r", encoding="utf-8") as f:
|
with open(AUDIT_FILE, "r", encoding="utf-8") as f:
|
||||||
tous_les_audits = json.load(f)
|
tous_les_audits = json.load(f)
|
||||||
@@ -158,7 +137,6 @@ def generer_dossier_litige(utilisateur_nom: str, est_admin: bool = False) -> lis
|
|||||||
if est_admin:
|
if est_admin:
|
||||||
dossier.append(item)
|
dossier.append(item)
|
||||||
else:
|
else:
|
||||||
# Filtrage des marges pour le client
|
|
||||||
dossier.append({
|
dossier.append({
|
||||||
"date": item.get("date"),
|
"date": item.get("date"),
|
||||||
"type_operation": item.get("type_operation"),
|
"type_operation": item.get("type_operation"),
|
||||||
|
|||||||
+4
-47
@@ -1,10 +1,6 @@
|
|||||||
import os
|
|
||||||
import json
|
|
||||||
from datetime import date
|
|
||||||
import uuid
|
import uuid
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import psutil
|
|
||||||
from fasthtml.common import *
|
from fasthtml.common import *
|
||||||
from modules.database import load_history, save_history
|
from modules.database import load_history, save_history
|
||||||
from modules.scraper import rechercher_sur_google, extraire_contenu_urls
|
from modules.scraper import rechercher_sur_google, extraire_contenu_urls
|
||||||
@@ -17,41 +13,10 @@ from modules.billing_manager import verifier_et_valider_budget, obtenir_solde
|
|||||||
from modules.rag_manager import rechercher_contexte_rag, auto_apprendre_rag
|
from modules.rag_manager import rechercher_contexte_rag, auto_apprendre_rag
|
||||||
from modules.moderator import analyser_intention_illicite, appliquer_sanction_et_archiver
|
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):
|
async def handle_chat_request(request, session):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
try:
|
try:
|
||||||
|
# Psutil et la gestion de surcharge retirés comme demandé
|
||||||
form = await request.form()
|
form = await request.form()
|
||||||
prompt = form.get("prompt", "")
|
prompt = form.get("prompt", "")
|
||||||
chat_id = form.get("chat_id", str(uuid.uuid4()))
|
chat_id = form.get("chat_id", str(uuid.uuid4()))
|
||||||
@@ -65,7 +30,7 @@ async def handle_chat_request(request, session):
|
|||||||
if choix_utilisateur != "aucun":
|
if choix_utilisateur != "aucun":
|
||||||
log_event("INFO", "BILLING", f"Décision utilisateur face au pop-up : {choix_utilisateur}")
|
log_event("INFO", "BILLING", f"Décision utilisateur face au pop-up : {choix_utilisateur}")
|
||||||
if choix_utilisateur == "gratuit_force":
|
if choix_utilisateur == "gratuit_force":
|
||||||
mode_payant = False # Le client a reculé devant le prix
|
mode_payant = False
|
||||||
|
|
||||||
session["current_chat_id"] = chat_id
|
session["current_chat_id"] = chat_id
|
||||||
history = load_history()
|
history = load_history()
|
||||||
@@ -74,16 +39,6 @@ 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")
|
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"
|
ip_client = request.client.host if request.client else "Inconnue"
|
||||||
|
|
||||||
# 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)
|
est_illicite, raison = analyser_intention_illicite(prompt)
|
||||||
if est_illicite:
|
if est_illicite:
|
||||||
sanction = appliquer_sanction_et_archiver(utilisateur_nom, prompt, raison, ip_client)
|
sanction = appliquer_sanction_et_archiver(utilisateur_nom, prompt, raison, ip_client)
|
||||||
@@ -162,8 +117,10 @@ async def handle_chat_request(request, session):
|
|||||||
if prix_estime > obtenir_solde(utilisateur_nom):
|
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;")
|
return Div(H3("❌ Solde Insuffisant", style="color:red;"), P("Veuillez recharger votre compte."), style="background:#300; padding:15px; border-radius:10px;")
|
||||||
|
|
||||||
|
# CRUCIAL : Transmission du chat_id à l'Engine
|
||||||
reponse, couleur, fournisseur_nom, modele = executer_requete_ia(fournisseur, full_prompt, mode_payant=mode_payant, multi_ia=mode_multi_ia, chat_id=chat_id)
|
reponse, couleur, fournisseur_nom, modele = executer_requete_ia(fournisseur, full_prompt, mode_payant=mode_payant, multi_ia=mode_multi_ia, chat_id=chat_id)
|
||||||
|
|
||||||
|
# Le paiement véritable s'exécute APRÈS la réponse de l'IA OpenRouter
|
||||||
if "openrouter" in fournisseur_nom.lower() and mode_payant:
|
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)
|
verifier_et_valider_budget(full_prompt, modele, utilisateur_nom=utilisateur_nom, rang_utilisateur=role_utilisateur, multi_ia=mode_multi_ia)
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -2,7 +2,10 @@ import os
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
|
||||||
HISTORY_FILE = "aethas_history.json"
|
# FORCAGE DU CHEMIN ABSOLU
|
||||||
|
BASE_DIR = "/DATA/AppData/MULTI-IA-AETHAS38"
|
||||||
|
os.makedirs(BASE_DIR, exist_ok=True)
|
||||||
|
HISTORY_FILE = os.path.join(BASE_DIR, "aethas_history.json")
|
||||||
|
|
||||||
def load_history():
|
def load_history():
|
||||||
if not os.path.exists(HISTORY_FILE):
|
if not os.path.exists(HISTORY_FILE):
|
||||||
|
|||||||
+4
-3
@@ -1,8 +1,9 @@
|
|||||||
|
import os
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
import os
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Chemin absolu obligatoire pour correspondre au volume Docker
|
||||||
LOG_DIR = "/DATA/AppData/MULTI-IA-AETHAS38"
|
LOG_DIR = "/DATA/AppData/MULTI-IA-AETHAS38"
|
||||||
os.makedirs(LOG_DIR, exist_ok=True)
|
os.makedirs(LOG_DIR, exist_ok=True)
|
||||||
LOG_FILE = os.path.join(LOG_DIR, "aethas_logs.txt")
|
LOG_FILE = os.path.join(LOG_DIR, "aethas_logs.txt")
|
||||||
@@ -11,7 +12,7 @@ logger = logging.getLogger("Aethas38")
|
|||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
if not logger.handlers:
|
if not logger.handlers:
|
||||||
# Conformité LCEN : Conservation des logs de connexion sur 365 jours (1 an)
|
# Conservation 365 jours pour conformité légale
|
||||||
handler = TimedRotatingFileHandler(LOG_FILE, when="midnight", interval=1, backupCount=365, encoding="utf-8")
|
handler = TimedRotatingFileHandler(LOG_FILE, when="midnight", interval=1, backupCount=365, encoding="utf-8")
|
||||||
handler.suffix = "%Y-%m-%d"
|
handler.suffix = "%Y-%m-%d"
|
||||||
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
||||||
@@ -32,5 +33,5 @@ def log_event(niveau, source, message):
|
|||||||
try:
|
try:
|
||||||
from modules.debug_mailer import envoyer_alerte_debug
|
from modules.debug_mailer import envoyer_alerte_debug
|
||||||
envoyer_alerte_debug(niveau, source, message)
|
envoyer_alerte_debug(niveau, source, message)
|
||||||
except Exception as mail_error:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -103,6 +103,7 @@ def render_main_page(chat_id, utilisateur_nom="xavier", role="Super Admin"):
|
|||||||
Div(
|
Div(
|
||||||
A("+ Nouvelle discussion", href="/new_chat", cls="new-chat-btn"),
|
A("+ Nouvelle discussion", href="/new_chat", cls="new-chat-btn"),
|
||||||
A("💾 Exporter JSON", href="/export_json", target="_blank", cls="export-btn"),
|
A("💾 Exporter JSON", href="/export_json", target="_blank", cls="export-btn"),
|
||||||
|
A("🚪 Déconnexion", href="/logout", cls="export-btn", style="background-color: #8b0000; color: white; border-color: #8b0000; margin-top: 5px;"), # NOUVEAU BOUTON ICI
|
||||||
|
|
||||||
Div(
|
Div(
|
||||||
render_paypal_donation_widget(),
|
render_paypal_donation_widget(),
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Charge ta clé API depuis le fichier .env
|
||||||
|
load_dotenv()
|
||||||
|
api_key = os.getenv("OPENROUTER_API_KEY")
|
||||||
|
|
||||||
|
if not api_key:
|
||||||
|
print("Erreur : Clé OPENROUTER_API_KEY introuvable dans le .env")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
print("🔍 Interrogation des serveurs OpenRouter...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
"https://openrouter.ai/api/v1/auth/key",
|
||||||
|
headers={"Authorization": f"Bearer {api_key}"}
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
print("\n✅ CONNEXION RÉUSSIE ! Voici le JSON de ton compte :")
|
||||||
|
# Affiche le JSON formaté de manière lisible
|
||||||
|
print(json.dumps(response.json(), indent=4))
|
||||||
|
else:
|
||||||
|
print(f"\n❌ ERREUR {response.status_code}: {response.text}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erreur de connexion : {e}")
|
||||||
Reference in new issue
Block a user