jaimalklyde commited on
Commit
bfa9769
·
verified ·
1 Parent(s): f5f749a

Update src/module/AIP.py

Browse files
Files changed (1) hide show
  1. src/module/AIP.py +175 -78
src/module/AIP.py CHANGED
@@ -1,85 +1,182 @@
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
- import time
 
 
3
 
4
- # ==============================================================================
5
- # 1. STYLE (THÈME GOTHAM)
6
- # ==============================================================================
7
- def apply_gotham_theme():
8
- st.markdown("""
9
- <style>
10
- .stApp { background-color: #0d1117 !important; color: #c9d1d9; }
11
- .ontology-title { font-family: 'Space Grotesk', sans-serif; font-size: 1.5rem; font-weight: 700; color: #ffffff; }
12
-
13
- /* Chat Styling */
14
- .user-msg {
15
- background: rgba(30, 136, 229, 0.1);
16
- border-left: 3px solid #1E88E5;
17
- padding: 12px; margin: 10px 0;
18
- border-radius: 0 4px 4px 0;
19
- font-family: 'Inter', sans-serif;
20
- }
21
-
22
- .bot-msg {
23
- background: rgba(48, 54, 61, 0.3);
24
- border-left: 3px solid #00E676;
25
- padding: 12px; margin: 10px 0;
26
- border-radius: 0 4px 4px 0;
27
- font-family: 'Inter', sans-serif;
28
- }
29
-
30
- .debug-block {
31
- font-family: 'JetBrains Mono', monospace;
32
- font-size: 0.8rem;
33
- color: #8b949e;
34
- background: #0d1117;
35
- border: 1px solid #30363d;
36
- padding: 10px;
37
- margin-top: 5px;
38
- border-radius: 4px;
39
- }
40
- </style>
41
- """, unsafe_allow_html=True)
42
 
43
- # ==============================================================================
44
- # 2. FONCTION MODULE JASMINE
45
- # ==============================================================================
46
- def show_jasmine_terminal():
47
- apply_gotham_theme()
48
-
49
- # --- INITIALISATION DU STATE ---
50
- if "chat_history" not in st.session_state:
51
- st.session_state["chat_history"] = []
52
- if "terminal_active" not in st.session_state:
53
- st.session_state["terminal_active"] = False
54
 
55
- # --- HEADER ---
56
- c1, c2 = st.columns([5, 1])
57
- with c1:
58
- st.markdown('<h1 class="ontology-title">VORTEX FLUX - JASMINE AI</h1>', unsafe_allow_html=True)
59
- with c2:
60
- st.session_state["terminal_active"] = st.toggle("TERMINAL", value=st.session_state["terminal_active"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- # --- INTERFACE TERMINAL ---
63
- if st.session_state["terminal_active"]:
64
- st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- # Zone d'affichage des messages
67
- with st.container(height=550):
68
- for msg in st.session_state["chat_history"]:
69
- if msg["role"] == "user":
70
- st.markdown(f'<div class="user-msg">{msg["content"]}</div>', unsafe_allow_html=True)
71
- else:
72
- st.markdown(f'<div class="bot-msg">{msg["content"]}</div>', unsafe_allow_html=True)
 
 
73
 
74
- # Entrée utilisateur
75
- if prompt := st.chat_input("En attente d'instruction..."):
76
- # Ajout du message utilisateur
77
- st.session_state["chat_history"].append({"role": "user", "content": prompt})
78
-
79
- # Simulation de la réponse IA Jasmine
80
- with st.spinner("Jasmine analyse..."):
81
- time.sleep(0.8)
82
- response = f"Requête '{prompt}' reçue. Le terminal Jasmine est opérationnel."
83
- st.session_state["chat_history"].append({"role": "assistant", "content": response})
84
-
85
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MODULE JASMINE AGENT - V FINAL (CASCADE MULTI-MODÈLES + FUNCTIONS)
3
+ ==================================================================
4
+ Responsabilité : Traduire le langage naturel en actions + appeler des fonctions métier.
5
+ """
6
+ import google.generativeai as genai
7
+ from groq import Groq
8
+ import json
9
  import streamlit as st
10
+ import os
11
+ import re
12
+ import sys
13
 
14
+ # Import des fonctions métier
15
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')))
16
+ from src.Function.AIP_Function import (
17
+ calculate_occupancy_rate,
18
+ audit_data_quality,
19
+ get_stock_alerts,
20
+ get_patient_stats
21
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ class JasmineAgent:
24
+ def __init__(self, rdf_store, ontology_rules, dataframes=None):
25
+ self.google_key = os.environ.get("GOOGLE_API_KEY") or st.secrets.get("GOOGLE_API_KEY")
26
+ if self.google_key:
27
+ genai.configure(api_key=self.google_key)
 
 
 
 
 
 
28
 
29
+ self.groq_key = os.environ.get("GROQ_API_KEY") or st.secrets.get("GROQ_API_KEY")
30
+ self.groq_client = Groq(api_key=self.groq_key) if self.groq_key else None
31
+
32
+ self.rdf_store = rdf_store
33
+ self.ontology_rules = ontology_rules
34
+ self.dataframes = dataframes or {} # Dictionnaire des DataFrames disponibles
35
+
36
+ self.MODEL_CASCADE = [
37
+ "gemini-2.0-flash-exp",
38
+ "llama-3.3-70b-versatile",
39
+ "gemini-2.0-flash-lite",
40
+ "llama-3.1-8b-instant"
41
+ ]
42
+
43
+ # Mapping des fonctions disponibles
44
+ self.available_functions = {
45
+ "calculate_occupancy_rate": calculate_occupancy_rate,
46
+ "audit_data_quality": audit_data_quality,
47
+ "get_stock_alerts": get_stock_alerts,
48
+ "get_patient_stats": get_patient_stats
49
+ }
50
+
51
+ def build_base_system_prompt(self):
52
+ return """
53
+ ROLE: Tu es un agent intelligent qui analyse les requêtes utilisateur.
54
+ MISSION: Comprendre l'intention et générer les actions appropriées.
55
+
56
+ --- FONCTIONS DISPONIBLES ---
57
+ 1. calculate_occupancy_rate : Calcule le taux d'occupation (nécessite df_ressources)
58
+ 2. audit_data_quality : Analyse la qualité des données (nécessite un DataFrame avec colonnes dq_)
59
+ 3. get_stock_alerts : Identifie les ruptures de stock (nécessite df_stock)
60
+ 4. get_patient_stats : Statistiques démographiques patients (nécessite df_patients)
61
 
62
+ CONTRAINTES DE SORTIE (JSON STRICT) :
63
+ {
64
+ "thought_trace": "1. ANALYSE: ... 2. EXTRACTION: ... 3. STRATÉGIE: ... 4. ACTION: ...",
65
+ "tool": "calculate_occupancy_rate" | "audit_data_quality" | "get_stock_alerts" | "get_patient_stats" | "none",
66
+ "args": { "dataframe_name": "df_ressources" }
67
+ }
68
+
69
+ IMPORTANT: Pour les fonctions de calcul, tu DOIS spécifier le nom du DataFrame dans args.
70
+ """
71
+
72
+ def execute_function(self, function_name, args):
73
+ """Exécute une fonction métier avec les arguments fournis"""
74
+ if function_name not in self.available_functions:
75
+ return {"error": f"Fonction {function_name} inconnue"}
76
+
77
+ # Récupération du DataFrame demandé
78
+ df_name = args.get("dataframe_name")
79
+ if not df_name or df_name not in self.dataframes:
80
+ return {"error": f"DataFrame {df_name} non disponible. DataFrames disponibles: {list(self.dataframes.keys())}"}
81
 
82
+ df = self.dataframes[df_name]
83
+
84
+ # Exécution de la fonction
85
+ try:
86
+ func = self.available_functions[function_name]
87
+ result = func(df)
88
+ return {"success": True, "result": result}
89
+ except Exception as e:
90
+ return {"error": f"Erreur lors de l'exécution: {str(e)}"}
91
 
92
+ def ask(self, user_message, chat_history):
93
+ """Essaie les modèles en cascade jusqu'à obtenir une réponse valide"""
94
+
95
+ system_prompt = self.build_base_system_prompt()
96
+ last_error = None
97
+
98
+ for model_name in self.MODEL_CASCADE:
99
+ try:
100
+ response_text = ""
101
+
102
+ if "gemini" in model_name:
103
+ if not self.google_key:
104
+ continue
105
+
106
+ msgs = self._format_messages_for_gemini(system_prompt, chat_history, user_message)
107
+ model = genai.GenerativeModel(model_name)
108
+ res = model.generate_content(
109
+ msgs,
110
+ generation_config={"response_mime_type": "application/json"}
111
+ )
112
+ response_text = res.text
113
+
114
+ else: # Groq (Llama)
115
+ if not self.groq_client:
116
+ continue
117
+
118
+ msgs = self._format_messages_for_groq(system_prompt, chat_history, user_message)
119
+ completion = self.groq_client.chat.completions.create(
120
+ model=model_name,
121
+ messages=msgs,
122
+ temperature=0.0,
123
+ response_format={"type": "json_object"}
124
+ )
125
+ response_text = completion.choices[0].message.content
126
+
127
+ # Nettoyage de la réponse
128
+ clean_text = response_text.strip()
129
+ clean_text = re.sub(r'```json\s*|\s*```', '', clean_text)
130
+
131
+ # Tentative de parsing JSON
132
+ try:
133
+ action = json.loads(clean_text)
134
+
135
+ # Récupération du Chain of Thought
136
+ thought_trace = action.get("thought_trace", "")
137
+
138
+ # Exécution de la fonction si demandée
139
+ tool = action.get("tool")
140
+ if tool and tool != "none":
141
+ args = action.get("args", {})
142
+ function_result = self.execute_function(tool, args)
143
+
144
+ return "", action, thought_trace, function_result
145
+
146
+ return "", action, thought_trace, None
147
+
148
+ except json.JSONDecodeError:
149
+ continue
150
+
151
+ except Exception as e:
152
+ last_error = str(e)
153
+ continue
154
+
155
+ # Si tous les modèles ont échoué
156
+ return f"⚠️ ERREUR SYSTEME : {last_error}", None, "", None
157
+
158
+ def _format_messages_for_groq(self, system_prompt, chat_history, user_message):
159
+ """Formate les messages pour Groq (OpenAI format)"""
160
+ msgs = [{"role": "system", "content": system_prompt}]
161
+
162
+ for m in chat_history[-4:]:
163
+ role = "assistant" if m["role"] in ["model", "assistant"] else "user"
164
+ content = str(m.get("content", ""))
165
+ if "🛠️" not in content and content.strip():
166
+ msgs.append({"role": role, "content": content})
167
+
168
+ msgs.append({"role": "user", "content": user_message})
169
+ return msgs
170
+
171
+ def _format_messages_for_gemini(self, system_prompt, chat_history, user_message):
172
+ """Formate les messages pour Gemini"""
173
+ msgs = [{"role": "user", "parts": [system_prompt]}]
174
+
175
+ for m in chat_history[-4:]:
176
+ role = "user" if m["role"] == "user" else "model"
177
+ content = str(m.get("content", ""))
178
+ if "🛠️" not in content and content.strip():
179
+ msgs.append({"role": role, "parts": [content]})
180
+
181
+ msgs.append({"role": "user", "parts": [user_message]})
182
+ return msgs