import os import pandas as pd from langchain_core.tools import tool from langchain_community.tools import DuckDuckGoSearchRun import matplotlib.pyplot as plt import seaborn as sns import matplotlib matplotlib.use('Agg') # Force Matplotlib à ne pas ouvrir de fenêtre graphique @tool def convert_csv_to_excel(csv_path: str): """Convertit un fichier CSV en Excel (.xlsx).""" if not os.path.exists(csv_path): return f"Désolé, je ne trouve pas le fichier '{csv_path}'. Vérifiez qu'il est bien présent dans le dossier du projet." try: df = pd.read_csv(csv_path) new_path = csv_path.replace(".csv", ".xlsx") df.to_excel(new_path, index=False) return f"Succès : Le fichier a été converti en {new_path}" except Exception as e: return f"Erreur lors de la lecture du CSV : {str(e)}" BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @tool def inspect_data(file_name: str): """ Explore le fichier situé dans le dossier 'data/' pour retourner les colonnes et un aperçu. Passer uniquement le nom du fichier (ex: 'data.csv'). """ # 1. On nettoie le nom du fichier au cas où l'IA ajoute des guillemets clean_name = file_name.strip().replace("'", "").replace('"', "") # 2. On construit le chemin ABSOLU vers le dossier data # On suppose que ton script est à la racine du projet data_path = os.path.join(os.getcwd(), "data", clean_name) try: # 3. Vérification de l'existence du fichier if not os.path.exists(data_path): return f"Erreur : Le fichier '{clean_name}' est introuvable dans le dossier data/." # 4. Lecture selon l'extension (Note : correction de 'endiwith' en 'endswith') if data_path.lower().endswith(".csv"): df = pd.read_csv(data_path) # Utilisation de la variable data_path, PAS de la chaîne "file_path" elif data_path.lower().endswith((".xlsx", ".xls")): df = pd.read_excel(data_path) else: return "Format de fichier non supporté. Utilisez .csv ou .xlsx" # 5. Extraction des infos columns_names = df.columns.tolist() preview = df.head(3).to_string() return f"Colonnes trouvées : {columns_names}\n\nAperçu des données :\n{preview}" except Exception as e: return f"Erreur lors de l'inspection : {str(e)}" @tool def excel_code_interpreter(file_path: str, code: str): """Exécute du code Python sur le fichier (CSV ou Excel) chargé dans 'df'.""" # Nettoyage du nom de fichier file_name = os.path.basename(file_path.strip().replace("'", "").replace('"', "")) # 1. Définition de la priorité : Dossier 'data' d'abord data_folder_path = os.path.join(BASE_DIR, "data", file_name) root_path = os.path.join(BASE_DIR, file_name) # Choix du chemin if os.path.exists(data_folder_path): full_path = data_folder_path elif os.path.exists(root_path): full_path = root_path else: # Si rien n'est trouvé, on aide l'agent avec un message clair files_in_data = os.listdir(os.path.join(BASE_DIR, "data")) if os.path.exists(os.path.join(BASE_DIR, "data")) else "Dossier data absent" return f"ERREUR : Fichier '{file_name}' introuvable. Contenu de 'data/': {files_in_data}" try: # Lecture selon l'extension if file_name.endswith('.csv'): df = pd.read_csv(full_path) else: df = pd.read_excel(full_path) safe_globals={"__builtins__" :{}} # Injection des bibliothèques pour l'IA local_vars = {"df": df, "pd": pd, "plt": plt, "result": None} exec(code, safe_globals , local_vars) return str(local_vars.get("result", "Exécution terminée.")) except Exception as e: return f"ERREUR PYTHON : {str(e)}" @tool def generate_excel_chart(file_path: str, code: str, output_image: str = "chart.png"): """ Exécute du code Python pour générer un graphique Matplotlib/Seaborn à partir du fichier Excel. Le DataFrame est chargé dans 'df'. IMPORTANT : Le code doit finir par plt.savefig(output_image). """ # Force le mode sans interface graphique (important pour les scripts automatisés) plt.switch_backend('Agg') plt.clf() # Nettoie les anciens graphiques en mémoire full_path = os.path.join(BASE_DIR, file_path) try: df = pd.read_excel(full_path) # Environnement d'exécution pour l'IA local_vars = { "df": df, "plt": plt, "sns": sns, "output_image": output_image } exec(code, {}, local_vars) if os.path.exists(output_image): return f"Succès : Graphique généré et enregistré sous '{output_image}'." else: return "Erreur : Le code a été exécuté mais aucun fichier image n'a été créé." except Exception as e: return f"Erreur lors de la génération du graphique : {str(e)}" ddg = DuckDuckGoSearchRun() @tool def search_tool(query : str) : """Recherche sur le web. Limité pour économiser les tokens.""" try: results = ddg.run(query) if not results: return "Aucun résultat trouvé." # 1. On nettoie les espaces superflus pour gagner des tokens clean_results = " ".join(results.split()) # 2. On limite intelligemment (ex: 1200 chars pour plus de contexte) # Mais on s'assure de ne pas couper un mot au milieu limit = 1200 if len(clean_results) <= limit: return clean_results return clean_results[:limit] + "... [Résultat tronqué pour économie]" except Exception as e: return f"Erreur lors de la recherche : {str(e)}"