Public Access
MAJ Phase 10 : Support KubeJS, bouton RAG UI et Modale d'upload admin
Build and Push Docker Image / build-and-push (push) Successful in 37s
Build and Push Docker Image / build-and-push (push) Successful in 37s
This commit is contained in:
1 parent
f17ef384f6
commit
a2261ab8dd
3 files changed
+79
-5
No files matched your search
@@ -3,6 +3,7 @@ from dotenv import load_dotenv
|
|||||||
load_dotenv() # Force la lecture du fichier .env en local
|
load_dotenv() # Force la lecture du fichier .env en local
|
||||||
import uuid
|
import uuid
|
||||||
import json
|
import json
|
||||||
|
import threading
|
||||||
from fasthtml.common import *
|
from fasthtml.common import *
|
||||||
from starlette.staticfiles import StaticFiles
|
from starlette.staticfiles import StaticFiles
|
||||||
from starlette.responses import FileResponse, Response, HTMLResponse, RedirectResponse
|
from starlette.responses import FileResponse, Response, HTMLResponse, RedirectResponse
|
||||||
@@ -10,13 +11,14 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
|||||||
|
|
||||||
from modules.theme import get_theme_hdrs
|
from modules.theme import get_theme_hdrs
|
||||||
from modules.database import nettoyer_vieilles_discussions, HISTORY_FILE, load_history, save_history
|
from modules.database import nettoyer_vieilles_discussions, HISTORY_FILE, load_history, save_history
|
||||||
from modules.ui_components import render_main_page, render_sidebar, render_login_page
|
from modules.ui_components import render_main_page, render_sidebar, render_login_page, render_rag_upload_modal
|
||||||
from modules.chat_handler import handle_chat_request
|
from modules.chat_handler import handle_chat_request
|
||||||
from modules.security import verifier_et_bloquer_vpn, journaliser_ip
|
from modules.security import verifier_et_bloquer_vpn, journaliser_ip
|
||||||
from modules.auth_manager import IDENTIFIANTS_ADMIN, verifier_mot_de_passe
|
from modules.auth_manager import IDENTIFIANTS_ADMIN, verifier_mot_de_passe
|
||||||
from modules.logger import log_event
|
from modules.logger import log_event
|
||||||
from modules.billing_manager import charger_soldes, sauvegarder_soldes
|
from modules.billing_manager import charger_soldes, sauvegarder_soldes
|
||||||
from modules.rag_manager import auto_apprendre_rag
|
from modules.rag_manager import auto_apprendre_rag
|
||||||
|
from modules.vector_rag import indexer_dossier_thematique
|
||||||
|
|
||||||
# Lancement du module d'auto-mise à jour en arrière-plan
|
# Lancement du module d'auto-mise à jour en arrière-plan
|
||||||
import modules.model_updater
|
import modules.model_updater
|
||||||
@@ -152,5 +154,42 @@ def post(prompt: str, reponse: str, theme: str):
|
|||||||
auto_apprendre_rag(prompt, reponse, theme)
|
auto_apprendre_rag(prompt, reponse, theme)
|
||||||
return Span("✅ Indexé", cls="msg-action-btn", style="background-color: #4caf50; color: white; border: none; cursor: default;")
|
return Span("✅ Indexé", cls="msg-action-btn", style="background-color: #4caf50; color: white; border: none; cursor: default;")
|
||||||
|
|
||||||
|
# --- NOUVELLES ROUTES POUR L'UPLOAD MODAL DU RAG ---
|
||||||
|
@rt('/admin/rag_modal')
|
||||||
|
def get(session):
|
||||||
|
if session.get('user', '') != 'xavier':
|
||||||
|
return HTMLResponse("Accès refusé.")
|
||||||
|
return render_rag_upload_modal()
|
||||||
|
|
||||||
|
@rt('/api/upload_rag')
|
||||||
|
async def post(request, session):
|
||||||
|
if session.get('user', '') != 'xavier':
|
||||||
|
return Span("❌ Accès refusé", style="color: red;")
|
||||||
|
|
||||||
|
form = await request.form()
|
||||||
|
theme = form.get("theme")
|
||||||
|
fichiers = form.getlist("fichiers_rag")
|
||||||
|
|
||||||
|
if not theme or not fichiers:
|
||||||
|
return Span("❌ Thème ou fichiers manquants", style="color: red;")
|
||||||
|
|
||||||
|
dossier_cible = f"/DATA/AppData/MULTI-IA-AETHAS38/RAG/{theme}"
|
||||||
|
os.makedirs(dossier_cible, exist_ok=True)
|
||||||
|
|
||||||
|
fichiers_sauves = 0
|
||||||
|
for f in fichiers:
|
||||||
|
if f.filename:
|
||||||
|
chemin_fichier = os.path.join(dossier_cible, f.filename)
|
||||||
|
contenu = await f.read()
|
||||||
|
with open(chemin_fichier, "wb") as out:
|
||||||
|
out.write(contenu)
|
||||||
|
fichiers_sauves += 1
|
||||||
|
|
||||||
|
if fichiers_sauves > 0:
|
||||||
|
threading.Thread(target=indexer_dossier_thematique, args=(theme,)).start()
|
||||||
|
return Span(f"✅ {fichiers_sauves} fichier(s) sauvé(s). Indexation FAISS démarrée en arrière-plan !", style="color: #4caf50;")
|
||||||
|
else:
|
||||||
|
return Span("❌ Aucun fichier valide détecté.", style="color: red;")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
serve(port=5001)
|
serve(port=5001)
|
||||||
@@ -17,7 +17,6 @@ def get_openrouter_stats():
|
|||||||
def render_user_msg(text):
|
def render_user_msg(text):
|
||||||
return Div(P(text, style="margin:0; font-weight:bold; white-space: pre-wrap;"), cls="user-bubble")
|
return Div(P(text, style="margin:0; font-weight:bold; white-space: pre-wrap;"), cls="user-bubble")
|
||||||
|
|
||||||
# --- LA FONCTION CORRIGÉE AVEC SES 10 ARGUMENTS ---
|
|
||||||
def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, execution_time=None, trace=None, prompt_associe="", thematique="general", msg_id=""):
|
def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, execution_time=None, trace=None, prompt_associe="", thematique="general", msg_id=""):
|
||||||
time_badge = Span(f"⏱️ {execution_time}s", style="margin-right: 10px; color:#aaa; font-size:0.8em;") if execution_time else ""
|
time_badge = Span(f"⏱️ {execution_time}s", style="margin-right: 10px; color:#aaa; font-size:0.8em;") if execution_time else ""
|
||||||
|
|
||||||
@@ -33,7 +32,6 @@ def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, executi
|
|||||||
time_badge, style="border-bottom: 1px solid #333; padding-bottom: 10px; margin-bottom: 10px; display: flex; flex-wrap: wrap; gap: 5px;"
|
time_badge, style="border-bottom: 1px solid #333; padding-bottom: 10px; margin-bottom: 10px; display: flex; flex-wrap: wrap; gap: 5px;"
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- BOUTON FORCER RAG (HTMX) ---
|
|
||||||
bouton_forcer = ""
|
bouton_forcer = ""
|
||||||
if msg_id and prompt_associe and thematique != "aucun":
|
if msg_id and prompt_associe and thematique != "aucun":
|
||||||
safe_prompt = prompt_associe.replace('"', '\\"').replace('\n', ' ')
|
safe_prompt = prompt_associe.replace('"', '\\"').replace('\n', ' ')
|
||||||
@@ -110,7 +108,6 @@ def render_main_page(chat_id, utilisateur_nom="xavier", role="Super Admin"):
|
|||||||
.catch(() => { serverDown = true; console.log("Serveur hors ligne..."); });
|
.catch(() => { serverDown = true; console.log("Serveur hors ligne..."); });
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
// Script pour gérer l'avertissement de gratuité sur les médias
|
|
||||||
function checkMediaPaymentWarning() {
|
function checkMediaPaymentWarning() {
|
||||||
const mediaMode = document.querySelector('input[name="media_mode"]:checked').value;
|
const mediaMode = document.querySelector('input[name="media_mode"]:checked').value;
|
||||||
const isPayant = document.getElementById('mode-payant-toggle').checked;
|
const isPayant = document.getElementById('mode-payant-toggle').checked;
|
||||||
@@ -155,6 +152,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"),
|
||||||
|
Button("📚 Gestion RAG", hx_get="/admin/rag_modal", hx_target="body", hx_swap="beforeend", cls="export-btn", style="background-color: #2196f3; color: white; border-color: #2196f3; margin-top: 5px; cursor: pointer;") if role.lower() in ["super admin", "admin"] else "",
|
||||||
A("🚪 Déconnexion", href="/logout", cls="export-btn", style="background-color: #8b0000; color: white; border-color: #8b0000; margin-top: 5px;"),
|
A("🚪 Déconnexion", href="/logout", cls="export-btn", style="background-color: #8b0000; color: white; border-color: #8b0000; margin-top: 5px;"),
|
||||||
cls="nav-buttons", style="display: flex; flex-direction: column; gap: 10px;"
|
cls="nav-buttons", style="display: flex; flex-direction: column; gap: 10px;"
|
||||||
), cls="sidebar-top"
|
), cls="sidebar-top"
|
||||||
@@ -252,4 +250,41 @@ def render_popup_paiement(cout_estime, prompt_sauvegarde, chat_id, multi_ia_stat
|
|||||||
),
|
),
|
||||||
id="popup-paiement",
|
id="popup-paiement",
|
||||||
style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 9999;"
|
style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 9999;"
|
||||||
|
)
|
||||||
|
|
||||||
|
def render_rag_upload_modal():
|
||||||
|
"""Génère la fenêtre pop-up pour téléverser des documents dans le RAG."""
|
||||||
|
themes = [
|
||||||
|
"Minecraft_KubeJS", "Juridique_Loi_Francaise", "Serveur_Debian",
|
||||||
|
"Bureautique_Excel", "Regles_Aethas38", "Education_Nationale", "Connaissances_Generales"
|
||||||
|
]
|
||||||
|
options = [Option(t.replace("_", " "), value=t) for t in themes]
|
||||||
|
|
||||||
|
return Div(
|
||||||
|
Div(
|
||||||
|
H3("📚 Administration RAG (Base Vectorielle)", style="color: #2196f3; margin-top: 0;"),
|
||||||
|
P("Formats supportés : PDF, MD, TXT, JS, JSON, YML, CSV", style="color: #ccc; font-size: 0.9em;"),
|
||||||
|
Form(
|
||||||
|
Div(
|
||||||
|
Label("1. Choisir la thématique :", style="font-weight: bold; color: white;"),
|
||||||
|
Select(*options, name="theme", cls="input-field", style="margin-bottom: 15px;")
|
||||||
|
),
|
||||||
|
Div(
|
||||||
|
Label("2. Sélectionner les fichiers :", style="font-weight: bold; color: white;"),
|
||||||
|
Input(type="file", name="fichiers_rag", multiple=True, required=True, cls="input-field", style="margin-bottom: 15px; background: #222; padding: 10px; border: 1px dashed #2196f3; cursor: pointer;")
|
||||||
|
),
|
||||||
|
Div(
|
||||||
|
Button("Annuler", type="button", onclick="document.getElementById('rag-modal').remove();", cls="btn-submit", style="background-color: #555; width: 48%;"),
|
||||||
|
Button("Téléverser & Indexer", type="submit", cls="btn-submit", style="background-color: #2196f3; color: white; width: 48%;"),
|
||||||
|
style="display: flex; justify-content: space-between;"
|
||||||
|
),
|
||||||
|
hx_post="/api/upload_rag",
|
||||||
|
hx_target="#rag-upload-result",
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
),
|
||||||
|
Div(id="rag-upload-result", style="margin-top: 15px; text-align: center; font-weight: bold;"),
|
||||||
|
style="background: #1a1a1a; padding: 25px; border-radius: 12px; border: 2px solid #2196f3; width: 500px; max-width: 90vw; box-shadow: 0 10px 30px rgba(0,0,0,0.8);"
|
||||||
|
),
|
||||||
|
id="rag-modal",
|
||||||
|
style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 9999;"
|
||||||
)
|
)
|
||||||
@@ -46,7 +46,7 @@ def indexer_dossier_thematique(thematique: str):
|
|||||||
reader = pypdf.PdfReader(chemin_complet)
|
reader = pypdf.PdfReader(chemin_complet)
|
||||||
for page in reader.pages:
|
for page in reader.pages:
|
||||||
texte += page.extract_text() or ""
|
texte += page.extract_text() or ""
|
||||||
elif fichier.lower().endswith(('.md', '.txt')):
|
elif fichier.lower().endswith(('.md', '.txt', '.yml', '.yaml', '.js', '.json', '.csv', '.toml', '.properties')):
|
||||||
with open(chemin_complet, 'r', encoding='utf-8') as f:
|
with open(chemin_complet, 'r', encoding='utf-8') as f:
|
||||||
texte = f.read()
|
texte = f.read()
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user