Public Access
134 lines
6.9 KiB
Python
134 lines
6.9 KiB
Python
import uuid
|
|
import time
|
|
import re
|
|
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.logger import log_event
|
|
from modules.billing_manager import verifier_et_valider_budget
|
|
from modules.rag_manager import rechercher_contexte_rag
|
|
from modules.moderator import analyser_intention_illicite, appliquer_sanction_et_archiver
|
|
|
|
async def handle_chat_request(request, session):
|
|
start_time = time.time()
|
|
try:
|
|
form = await request.form()
|
|
prompt = form.get("prompt", "")
|
|
chat_id = form.get("chat_id", str(uuid.uuid4()))
|
|
files = form.getlist("files")
|
|
|
|
mode_payant = form.get("mode_payant") == "on"
|
|
mode_multi_ia = form.get("mode_multi_ia") == "on"
|
|
|
|
session["current_chat_id"] = chat_id
|
|
history = load_history()
|
|
|
|
utilisateur_nom = session.get("user", "xavier")
|
|
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")
|
|
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;"
|
|
)
|
|
|
|
file_summary = []
|
|
file_names = []
|
|
for f in files[:10]:
|
|
if f and f.filename:
|
|
file_names.append(f.filename)
|
|
content = await f.read()
|
|
file_summary.append(f"\n--- Fichier : {f.filename} ---\n{content.decode('utf-8', errors='ignore')}")
|
|
|
|
liste_fichiers_str = ", ".join(file_names) if file_names else "Aucun fichier"
|
|
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.
|
|
- "openrouter_code" : programmation, Minecraft/KubeJS, scripts, mathématiques.
|
|
- "openrouter_droit" : droit français, CGV, CGU, RGPD.
|
|
- "local" : salutations basiques, questions simples.
|
|
Tu dois aussi déterminer si la requête nécessite de lire la documentation interne (RAG). Thèmes : "minecraft", "droit", "serveur", "regles" ou "aucun".
|
|
Renvoie STRICTEMENT un JSON : {{"intention": "Résumé...", "fournisseur": "gemini|openrouter_chat|openrouter_code|openrouter_droit|local", "theme_rag": "minecraft|droit|serveur|regles|aucun"}}
|
|
Requête : {prompt}"""
|
|
|
|
try:
|
|
texte_routeur, _, _, _ = interroger_local_ollama(sec_prompt, "qwen2.5-coder:3b")
|
|
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"}
|
|
|
|
intention = decision.get("intention", "Analyse")
|
|
fournisseur = decision.get("fournisseur", "openrouter_chat").lower()
|
|
thematique_rag = decision.get("theme_rag", "aucun").lower()
|
|
|
|
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)
|
|
|
|
urls = re.findall(r"https?://[^\s]+", prompt)
|
|
mots_cles = ["cherche", "recherche", "sur internet", "sur le web", "sur le net", "trouve", "site"]
|
|
if any(m in prompt.lower() for m in mots_cles):
|
|
urls.extend(rechercher_sur_google(prompt))
|
|
|
|
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)
|
|
|
|
# Exécution rapide
|
|
reponse, couleur, fournisseur_nom, modele = executer_requete_ia(
|
|
fournisseur, full_prompt, mode_payant=mode_payant, multi_ia=mode_multi_ia
|
|
)
|
|
|
|
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.*"
|
|
|
|
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
|
|
})
|
|
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:
|
|
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;") |