From 2693816d06ab0c2dbfaba307a6a2a0181be26036 Mon Sep 17 00:00:00 2001 From: Arno37 Date: Thu, 12 Mar 2026 16:06:50 +0100 Subject: [PATCH 1/3] new commit --- agent_memory.py | 36 +++++++++++++++++++++ api_auth.py | 71 ++++++++++++++++++++++++++++++++++++++++ cookies.txt | 5 +++ cookies_mistral.txt | 5 +++ cookies_test.txt | 5 +++ db_session.py | 63 ++++++++++++++++++++++++++++++++++++ frontend/auth.js | 29 +++++++++++++++++ frontend/index.html | 15 +++++++++ frontend/script.js | 35 ++++++++++++++++++++ frontend/style.css | 79 +++++++++++++++++++++++++++++++++++++++++++++ init_and_run.py | 10 ++++++ runIA.py | 8 ++--- 12 files changed, 357 insertions(+), 4 deletions(-) create mode 100644 agent_memory.py create mode 100644 api_auth.py create mode 100644 cookies.txt create mode 100644 cookies_mistral.txt create mode 100644 cookies_test.txt create mode 100644 db_session.py create mode 100644 frontend/auth.js create mode 100644 init_and_run.py diff --git a/agent_memory.py b/agent_memory.py new file mode 100644 index 0000000..0c74250 --- /dev/null +++ b/agent_memory.py @@ -0,0 +1,36 @@ +# Fichier : agent_memory.py +import os +import sqlite3 +from dotenv import load_dotenv +from langgraph.checkpoint.sqlite import SqliteSaver +from langgraph.prebuilt import create_react_agent +from langchain_mistralai import ChatMistralAI +from runIA import outil_dvf_historique, outil_dvf_estimation, outil_infos_ville, PROMPT_CENTRAL + +load_dotenv() + +model = ChatMistralAI(model="mistral-large-latest", api_key=os.getenv("MISTRAL_API_KEY"), temperature=0) + +# On ajoute la règle de rappel +PROMPT_MEMOIRE = PROMPT_CENTRAL + ( + "\n\n6. RÈGLE DE MÉMOIRE : Tu as accès à l'historique de la discussion. " + "Si l'utilisateur pose une question répétée, mentionne poliment que c'est un rappel." +) + +conn = sqlite3.connect("data/memory.db", check_same_thread=False) +memory = SqliteSaver(conn) + +dvf_tools = [outil_dvf_historique, outil_dvf_estimation, outil_infos_ville] + +# 💡 CORRECTION : Utilisation de l'argument 'prompt' (version actuelle de langgraph) +agent_executor = create_react_agent( + model, + tools=dvf_tools, + checkpointer=memory, + prompt=PROMPT_MEMOIRE +) + +def interroger_agent(message_utilisateur: str, thread_id: str): + config = {"configurable": {"thread_id": thread_id}} + resultat = agent_executor.invoke({"messages": [("user", message_utilisateur)]}, config=config) + return resultat["messages"][-1].content diff --git a/api_auth.py b/api_auth.py new file mode 100644 index 0000000..673f8e3 --- /dev/null +++ b/api_auth.py @@ -0,0 +1,71 @@ +from fastapi import FastAPI, Response, Request, Cookie, HTTPException +from pydantic import BaseModel +import db_session +# On importe la fonction de chat de ton projet +from agent_memory import interroger_agent + +from fastapi.middleware.cors import CORSMiddleware + +app = FastAPI() + +# Configuration CORS pour éviter les erreurs réseau dans le navigateur +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +class LoginRequest(BaseModel): + email: str + +class ChatRequest(BaseModel): + message: str + +# 1. Route de Connexion : Génère le cookie +@app.post("/api/login") +async def login(req: LoginRequest, response: Response): + user_id = db_session.get_or_create_user(req.email) + session_id = db_session.create_session(user_id) + + # On place le badge (cookie) dans le navigateur + response.set_cookie( + key="session_id", + value=session_id, + httponly=True, # Sécurité : empêche le JS de lire le cookie + max_age=30*24*60*60 # 30 jours + ) + return {"status": "success", "message": "Connecté"} + +# 2. Route de Chat : Utilise le cookie automatiquement +@app.post("/api/chat") +async def chat(req: ChatRequest, session_id: str = Cookie(None)): + if not session_id: + raise HTTPException(status_code=401, detail="Non connecté") + + # On vérifie qui est l'utilisateur derrière ce cookie + user_id = db_session.verify_session(session_id) + if not user_id: + raise HTTPException(status_code=401, detail="Session invalide ou expirée") + + # On utilise l'id utilisateur comme thread_id pour garder la mémoire + reponse = interroger_agent(req.message, user_id) + return {"reply": reponse} + +# 3. Servir le Frontend +import os +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse + +# S'assurer que le dossier static existe +os.makedirs("frontend", exist_ok=True) +app.mount("/static", StaticFiles(directory="frontend"), name="static") + +@app.get("/") +async def root(): + return FileResponse("frontend/index.html") + +if __name__ == "__main__": + import uvicorn + uvicorn.run("api_auth:app", host="0.0.0.0", port=8000, reload=True) diff --git a/cookies.txt b/cookies.txt new file mode 100644 index 0000000..5c85344 --- /dev/null +++ b/cookies.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +#HttpOnly_127.0.0.1 FALSE / FALSE 1775917241 session_id 24a446b5-499c-4555-b94a-d94de98e481e diff --git a/cookies_mistral.txt b/cookies_mistral.txt new file mode 100644 index 0000000..6b96416 --- /dev/null +++ b/cookies_mistral.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +#HttpOnly_127.0.0.1 FALSE / FALSE 1775919227 session_id 083b53d2-fea0-4f89-ab62-676809399e3e diff --git a/cookies_test.txt b/cookies_test.txt new file mode 100644 index 0000000..8fb70d0 --- /dev/null +++ b/cookies_test.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +#HttpOnly_127.0.0.1 FALSE / FALSE 1775917150 session_id 0c68b8a4-c0ba-42f5-b6d8-7bd8252ede43 diff --git a/db_session.py b/db_session.py new file mode 100644 index 0000000..ccc40c2 --- /dev/null +++ b/db_session.py @@ -0,0 +1,63 @@ +import sqlite3 +import uuid +from datetime import datetime, timedelta + +DB_PATH = "data/auth_system.db" + +def init_auth_db(): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + # Table des utilisateurs + cursor.execute('''CREATE TABLE IF NOT EXISTS users ( + id TEXT PRIMARY KEY, + email TEXT UNIQUE + )''') + + # Table des sessions (le lien entre le cookie et l'utilisateur) + cursor.execute('''CREATE TABLE IF NOT EXISTS sessions ( + session_id TEXT PRIMARY KEY, + user_id TEXT, + expires_at TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) + )''') + + conn.commit() + conn.close() + +def get_or_create_user(email): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("SELECT id FROM users WHERE email = ?", (email,)) + user = cursor.fetchone() + + if user: + user_id = user[0] + else: + user_id = str(uuid.uuid4()) + cursor.execute("INSERT INTO users (id, email) VALUES (?, ?)", (user_id, email)) + conn.commit() + + conn.close() + return user_id + +def create_session(user_id): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + session_id = str(uuid.uuid4()) + # Session valide 30 jours + expires_at = datetime.now() + timedelta(days=30) + cursor.execute("INSERT INTO sessions (session_id, user_id, expires_at) VALUES (?, ?, ?)", + (session_id, user_id, expires_at)) + conn.commit() + conn.close() + return session_id + +def verify_session(session_id): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute("SELECT user_id FROM sessions WHERE session_id = ? AND expires_at > ?", + (session_id, datetime.now())) + result = cursor.fetchone() + conn.close() + return result[0] if result else None diff --git a/frontend/auth.js b/frontend/auth.js new file mode 100644 index 0000000..f50c7f0 --- /dev/null +++ b/frontend/auth.js @@ -0,0 +1,29 @@ +// Fonction pour se connecter +async function login(email) { + const response = await fetch('/api/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: email }) + }); + const data = await response.json(); + if(data.status === "success") { + console.log("Connecté ! Le cookie est maintenant dans le navigateur."); + } +} + +// Fonction d'envoi de message (Le navigateur enverra le cookie automatiquement) +async function sendMessage(text) { + const response = await fetch('/api/chat', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ message: text }) // Pas besoin d'envoyer l'ID, il est dans le cookie ! + }); + + if (response.status === 401) { + alert("Veuillez vous connecter d'abord."); + return; + } + + const data = await response.json(); + return data.reply; +} diff --git a/frontend/index.html b/frontend/index.html index a0c22b9..46c586c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -13,6 +13,21 @@
+ +
+
+
+ 🔑 +

Connexion

+

Entrez votre email pour accéder à l'agent expert

+
+ +
+
+