# utils/config.py import os from dotenv import load_dotenv # Charger les variables d'environnement du fichier .env load_dotenv() # --- Clé API LLM + Embedding --- MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY") if not MISTRAL_API_KEY: print("⚠️ Attention: La clé API Mistral (MISTRAL_API_KEY) n'est pas définie dans le fichier .env") # Vous pouvez choisir de lever une exception ici ou de continuer avec des fonctionnalités limitées # raise ValueError("Clé API Mistral manquante. Veuillez la définir dans le fichier .env") # --- Clé API Write Pydantic Logfire --- LOGFIRE_TOKEN = os.getenv("LOGFIRE_TOKEN") # Si vous souhaitez envoyer des logs à Logfire, sinon laissez vide ou ne pas utiliser # --- Configuration de l'Application --- APP_TITLE = "NBA Analyst AI" NAME = "NBA" # Nom à personnaliser dans l'interface # ====================================================== # LLM # ====================================================== # --- Modèles Mistral --- EMBEDDING_MODEL = "mistral-embed" MODEL_NAME = "mistral-small-latest" # mistral-small-latest mistral-small-2506 # --- Paramètres du LLM Mistral --- # Note : les paramètres 'Presence Penalty' et 'Frequency Penalty' ne sont pas définis dans ce projet car ils sont plus adaptés à des tâches de génération créative ou de conversation prolongée. TEMPERATURE = 0.03 # Température basse pour des réponses factuelles basées sur le contexte TOP_P = 0.9 # Nucleus sampling (Une température de 0 est déterministe, rendant ainsi la valeur Top P sans importance) # Note : Si on souhaite des réponses plus créatives, on peut augmenter la température et/ou réduire le top_p. # Mistral ne supporte pas Temperature et Top_p simultanément. C'est l'un ou l'autre. Décommenter top_p dans le code si nécessaire. LLM_CALL_DELAY = 0.7 # Délai en secondes entre les appels au LLM (rate limiting) # ====================================================== # Vectorisation + RAG # ====================================================== # --- Racine du projet (dossier parent de utils/) --- _PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # --- Configuration de l'Indexation --- # INPUT_DATA_URL = os.getenv("INPUT_DATA_URL") # Décommentez si vous utilisez une URL INPUT_DIR = os.path.join(_PROJECT_ROOT, "inputs") VECTOR_DB_DIR = os.path.join(_PROJECT_ROOT, "vector_db") FAISS_INDEX_FILE = os.path.join(VECTOR_DB_DIR, "faiss_index.idx") DOCUMENT_CHUNKS_FILE = os.path.join(VECTOR_DB_DIR, "document_chunks.pkl") CHUNK_SIZE = 1500 # Taille des chunks en caractères CHUNK_OVERLAP = 150 # Chevauchement en caractères EMBEDDING_BATCH_SIZE = 8 # Taille des lots pour l'API d'embedding (réduire pour éviter les erreurs brotli) # --- Configuration de la Recherche --- SEARCH_K = 5 # Nombre de documents à récupérer par défaut # --- Fichier source Excel à charger en Vector DB / Rag --- EXCEL_INPUTS_FOR_RAG = os.path.join(INPUT_DIR, "regular NBA_Corr_Rag.xlsx") # --- Prompt Système RAG --- RAG_SYSTEM_PROMT = """Tu es 'NBA Analyst AI', un assistant expert sur la ligue de basketball NBA. Ta mission est de répondre aux questions des entraîneurs, analystes et préparateurs physiques de manière structurée, mais pas trop verbeuse. N'invente jamais d'informations : si tu n'es pas sûr de la réponse, dis-le clairement et suggère de consulter une source externe. --- {context_str} --- QUESTION DE L'UTILISATEUR: {question} RÉPONSE DE L'ANALYSTE NBA:""" # ====================================================== # RAGAS # ====================================================== # --- Configuration des questions/réponses pour RAGAS --- QUESTIONS_TEST = [ "Quelle équipe à comme code MIA ?", "Combien de joueurs contient l’équipe des Philadelphia 76ers ?", "Quelle est la moyenne d’âge des joueurs d’Atlanta à un chiffre après la virgule ?", "Quelle est l’équipe qui a inscrit le plus de points ?", "Comment se rendre à San Antonio ?" ] GROUND_TRUTHS= [ "L’équipe ayant le code MIA est l’équipe des Miami Heat.", "L’équipe des Philadelphia 76ers contient 23 joueurs.", "La moyenne d’âge des joueurs d’Atlanta est de 25,6.", "L’équipe des Detroit Pistons a inscrit le plus de points avec un total de 10292.", "Le système ne peut pas guider l’utilisateur et suggère de consulter une source externe." ] # ====================================================== # SQL # ====================================================== # --- Configuration de la Base de Données PostgreSQL --- DATABASE_STATUS = 1 # Si 0, pas d'utilisation de la db SQL pour Ragas, si 1 utilisation du routeur Graph et donc du Rag + SQL PG_HOST = os.getenv("PG_HOST", "localhost") PG_PORT = int(os.getenv("PG_PORT", "5432")) PG_DB = "oc_mlops_projet_3" PG_ADMIN = "admin" POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD", "") PG_URL_ADMIN = f"postgresql+psycopg2://{PG_ADMIN}:{POSTGRES_PASSWORD}@{PG_HOST}:{PG_PORT}/{PG_DB}" # --- Utilisateur read-only utilisé par l'agent SQL (sql_tool.py) --- # Créé automatiquement par load_excel_to_db.py via le superutilisateur # Droits : USAGE ON SCHEMA public + SELECT ON ALL TABLES IN SCHEMA public PG_USER_1 = "user_1" PG_USER_1_PASSWORD = os.getenv("PG_USER_1_PASSWORD", "") PG_URL_READONLY = f"postgresql+psycopg2://{PG_USER_1}:{PG_USER_1_PASSWORD}@{PG_HOST}:{PG_PORT}/{PG_DB}" # --- Fichier source Excel à charger en db SQL --- EXCEL_INPUTS_FOR_SQL = os.path.join(INPUT_DIR, "regular NBA_Corr_Sql.xlsx") # --- Prompt Système Agent SQL --- SQL_SYSTEM_PROMPT = """ Tu es un agent SQL expert de la NBA. La base contient les tables suivantes : - teams : équipes NBA (id, name) - players : joueurs (id, team_id, name, age) - analyse_joueurs_une_equipe : stats défensives/offensives par joueur et équipe - analyse_nbr_joueurs_et_points_par_equipe : nombre de joueurs et total de points par équipe - analyse_top_15_joueurs_nombre_points : top 15 joueurs selon leurs points - stats_joueurs_saison_reguliere : statistiques détaillées de la saison régulière Pour toute question, suis impérativement ces étapes dans l'ordre : 1. TOUJOURS commencer par exécuter cette requête pour récupérer d'un seul coup les commentaires de TOUTES les colonnes de TOUTES les tables : SELECT c.relname AS table_name, a.attname AS colonne, col_description(c.oid, a.attnum) AS commentaire FROM pg_class c JOIN pg_attribute a ON a.attrelid = c.oid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind = 'r' AND n.nspname = 'public' AND a.attnum > 0 AND NOT a.attisdropped ORDER BY c.relname, a.attnum; Ces commentaires définissent la sémantique exacte de chaque colonne. C'est la base de toute ta réflexion : ne passe jamais cette étape. 2. En t'appuyant sur ces définitions, identifie la ou les tables les plus adaptées à la question (et les jointures éventuelles à effectuer). 3. Consulte le schéma détaillé des tables sélectionnées. 4. Écris une requête SQL valide en {dialect} en t'appuyant sur le schéma ET les commentaires. 5. Vérifie-la avant exécution. 6. N'exécute jamais de INSERT / UPDATE / DELETE / DROP. 7. Réponds en français de façon concise et factuelle. Exemples : Exemple 1 Question utilisateur : Quels sont les 3 joueurs des Los Angeles Lakers ayant capté le plus de rebonds offensifs ? SQL : SELECT t.name, p.name, a.sum_oreb FROM analyse_joueurs_une_equipe AS a INNER JOIN players AS p ON a.player_id = p.id INNER JOIN teams AS t ON a.team_id = t.id WHERE t.name = 'Los Angeles Lakers' ORDER BY a.sum_oreb DESC LIMIT 3; Réponse attendue : Les 3 joueurs des Los Angeles Lakers ayant capté le plus de rebonds offensifs sont : Dorian Finney-Smith (82), Jaxson Hayes (78) et Rui Hachimura (77). Exemple 2 Question utilisateur : Quelles sont les 3 équipes ayant marqué le moins de points ? SQL : SELECT t.name AS team_name, SUM(s.pts) AS total_pts FROM stats_joueurs_saison_reguliere AS s INNER JOIN teams AS t ON s.team_id = t.id GROUP BY t.name ORDER BY SUM(s.pts) ASC LIMIT 3; Réponse attendue : Les 3 équipes ayant marqué le moins de points sont : les Washington Wizards (7986), les Brooklyn Nets (7999) et les Charlotte Hornets (8278). """