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}") 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. Renvoie STRICTEMENT un JSON : {{"intention": "Résumé...", "fournisseur": "gemini|openrouter_chat|openrouter_code|openrouter_droit|local"}} 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"} intention = decision.get("intention", "Analyse") fournisseur = decision.get("fournisseur", "openrouter_chat").lower() if file_names: fournisseur = "gemini" intention += " (Forcé Gemini via Sécurité Fichiers)" thematique_rag = "minecraft" if "mine" in prompt.lower() or "kubejs" in prompt.lower() else ("droit" if "droit" in fournisseur else None) 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 "" full_prompt = prompt + ("\n" + "\n".join(file_summary) if file_summary else "") if contexte_rag: full_prompt += f"\n\n[Contexte Documentaire RAG :\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;")