app.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import streamlit as st
  2. import os
  3. import glob
  4. import shutil
  5. from workflow_Agent import app # Ton graphe compilé
  6. from langchain_core.messages import HumanMessage
  7. # --- CONFIGURATION ET NETTOYAGE AU DÉMARRAGE ---
  8. st.set_page_config(page_title="Dataltist AI Assistant", layout="wide")
  9. st.title("📊 Dataltist BI Chat")
  10. from langfuse.langchain import CallbackHandler
  11. # Initialize Langfuse CallbackHandler for Langchain (tracing)
  12. langfuse_handler = CallbackHandler()
  13. # Initialisation du dossier de travail
  14. if "app_initialized" not in st.session_state:
  15. if os.path.exists("outputs"):
  16. shutil.rmtree("outputs")
  17. os.makedirs("outputs", exist_ok=True)
  18. if not os.path.exists("data"):
  19. os.makedirs("data", exist_ok=True)
  20. st.session_state.app_initialized = True
  21. st.session_state.query_count = 0
  22. st.session_state.messages = []
  23. # --- 1. SIDEBAR : GESTION DES DONNÉES ---
  24. file_path = None
  25. with st.sidebar:
  26. st.header("📁 Données")
  27. uploaded_file = st.file_uploader("Charge ton fichier (CSV/Excel)", type=["csv", "xlsx"])
  28. if uploaded_file:
  29. file_path = os.path.join("data", uploaded_file.name)
  30. with open(file_path, "wb") as f:
  31. f.write(uploaded_file.getbuffer())
  32. st.success(f"Fichier '{uploaded_file.name}' prêt !")
  33. st.write("---")
  34. if st.button("🗑️ Effacer la discussion & fichiers", use_container_width=True):
  35. st.session_state.messages = []
  36. st.session_state.query_count = 0
  37. if os.path.exists("outputs"):
  38. shutil.rmtree("outputs")
  39. os.makedirs("outputs", exist_ok=True)
  40. st.rerun()
  41. # --- 2. AFFICHAGE DE L'HISTORIQUE ---
  42. for message in st.session_state.messages:
  43. with st.chat_message(message["role"]):
  44. st.markdown(message["content"])
  45. # Réaffichage des graphiques archivés pour ce message
  46. if "charts" in message and message["charts"]:
  47. cols = st.columns(min(len(message["charts"]), 2))
  48. for idx, path in enumerate(message["charts"]):
  49. if os.path.exists(path):
  50. cols[idx % 2].image(path, use_container_width=True)
  51. # --- 3. INTERACTION AVEC LES AGENTS ---
  52. if prompt := st.chat_input("Pose ta question sur tes données..."):
  53. if not file_path:
  54. st.error("Veuillez d'abord charger un fichier dans la barre latérale.")
  55. st.stop()
  56. # Archivage : Création du dossier spécifique pour cette requête
  57. current_query_id = st.session_state.query_count
  58. query_folder = os.path.join("outputs", f"query_{current_query_id}")
  59. os.makedirs(query_folder, exist_ok=True)
  60. # Affichage message utilisateur
  61. st.session_state.messages.append({"role": "user", "content": prompt})
  62. with st.chat_message("user"):
  63. st.markdown(prompt)
  64. # Appel des agents
  65. with st.chat_message("assistant"):
  66. with st.status("Analyse Dataltist en cours...", expanded=True) as status:
  67. inputs = {
  68. "messages": [HumanMessage(content=prompt)],
  69. "current_df_path": file_path
  70. }
  71. # Exécution du graphe
  72. final_state = app.invoke(
  73. inputs,
  74. config={"callbacks": [langfuse_handler], "run_name": f"Query_{st.session_state.query_count}"}
  75. )
  76. # Récupération de la réponse finale (Reporter)
  77. response_text = final_state["messages"][-1].content
  78. st.markdown(response_text)
  79. # --- GESTION DES FICHIERS GÉNÉRÉS ---
  80. new_files_paths = []
  81. # On scanne la racine de 'outputs/' pour trouver ce que l'Exécuteur a créé
  82. raw_files = [f for f in glob.glob("outputs/*") if os.path.isfile(f)]
  83. if raw_files:
  84. st.write("---")
  85. st.subheader("📊 Résultats de l'analyse")
  86. # On déplace les fichiers vers le dossier de la query pour l'historique
  87. for f in raw_files:
  88. dest_path = os.path.join(query_folder, os.path.basename(f))
  89. shutil.move(f, dest_path)
  90. new_files_paths.append(dest_path)
  91. # Affichage des images et fichiers Excel
  92. images = [p for p in new_files_paths if p.lower().endswith(('.png', '.jpg'))]
  93. docs = [p for p in new_files_paths if p.lower().endswith(('.xlsx', '.csv'))]
  94. if images:
  95. cols = st.columns(min(len(images), 2))
  96. for idx, img_path in enumerate(images):
  97. cols[idx % 2].image(img_path, use_container_width=True, caption=f"Visuel {idx+1}")
  98. if docs:
  99. for doc_path in docs:
  100. with open(doc_path, "rb") as f:
  101. st.download_button(
  102. label=f"📥 Télécharger {os.path.basename(doc_path)}",
  103. data=f,
  104. file_name=os.path.basename(doc_path),
  105. mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  106. )
  107. status.update(label="Analyse terminée !", state="complete")
  108. # Mise à jour de la session et du compteur
  109. st.session_state.messages.append({
  110. "role": "assistant",
  111. "content": response_text,
  112. "charts": new_files_paths
  113. })
  114. st.session_state.query_count += 1