klydekushy commited on
Commit
2b040fa
·
verified ·
1 Parent(s): fc5e86b

Update src/modules/jasmine_agent.py

Browse files
Files changed (1) hide show
  1. src/modules/jasmine_agent.py +44 -47
src/modules/jasmine_agent.py CHANGED
@@ -1,8 +1,8 @@
1
  """
2
- MODULE JASMINE AGENT - V29.5 OPTIMIZED (TOKEN SAVER)
3
- ====================================================
4
- Optimisation drastique des tokens pour passer dans les limites de Groq/Llama.
5
- Groupe les propriétés par classe au lieu de lister ligne par ligne.
6
  """
7
  import google.generativeai as genai
8
  from groq import Groq
@@ -29,11 +29,8 @@ class JasmineAgent:
29
  self.rdf_store = rdf_store
30
  self.ontology_rules = ontology_rules
31
 
32
- # ORDRE DE PRIORITÉ : Gemini supporte bcp plus de tokens.
33
- # Si Gemini échoue, on passe à Llama 70b (plus robuste que 8b)
34
- # Cascade de modèles
35
  self.MODEL_CASCADE = [
36
-
37
  "gemini-2.0-flash-exp",
38
  "llama-3.3-70b-versatile",
39
  "gemini-2.0-flash-lite",
@@ -41,7 +38,7 @@ class JasmineAgent:
41
  ]
42
 
43
  def _generate_dynamic_schema_prompt(self):
44
- """Version Compressée : Groupe par Classe pour réduire les tokens"""
45
  schema_map = defaultdict(lambda: {"relations": [], "props": []})
46
 
47
  for rule in self.ontology_rules:
@@ -50,52 +47,53 @@ class JasmineAgent:
50
  obj = rule.get('ObjectColOrConcept', 'unknown')
51
  obj_type = rule.get('ObjectType', 'data_property')
52
 
53
- # Nettoyage
54
  pred_clean = predicate if ":" in predicate else f"vortex:{predicate}"
55
 
56
  if obj_type == 'relation':
57
- schema_map[subject]["relations"].append(f"-> {pred_clean} (vers {obj})")
58
  else:
59
  schema_map[subject]["props"].append(f"{pred_clean}")
60
 
61
- # Construction du texte compact
62
- compact_text = []
63
  for cls, data in schema_map.items():
64
- lines = [f"📦 CLASSE: {cls}"]
65
- if data["relations"]:
66
- lines.append(f" 🔗 LIENS: {', '.join(data['relations'])}")
67
- if data["props"]:
68
- lines.append(f" 📝 PROPS: {', '.join(data['props'])}")
69
- compact_text.append("\n".join(lines))
70
 
71
- return "\n".join(compact_text)
72
 
73
  def build_system_prompt(self):
74
  dynamic_schema = self._generate_dynamic_schema_prompt()
75
 
76
  return f"""
77
- Tu es JASMINE, Expert SPARQL.
78
- CONTEXTE RDF (Ontologie Compressée) :
79
- PREFIX: vortex: <http://vortex.ai/ontology#>
80
 
 
 
81
  {dynamic_schema}
82
 
83
- ⚠️ RÈGLES SPARQL :
84
- 1. Les noms de prédicats ci-dessus sont EXACTS.
85
- 2. Pour les Relations (LIENS), utilise `FILTER(CONTAINS(STR(?o), 'Valeur'))`.
86
- 3. Pour les Propriétés (PROPS), utilise `FILTER(?o = 'Valeur')` ou `CONTAINS`.
 
 
 
 
87
 
88
- 🛠️ OUTILS :
89
- 1️⃣ {{ "tool": "search_semantic", "args": {{ "query": "..." }} }}
90
- 2️⃣ {{ "tool": "execute_sparql", "args": {{ "query": "SELECT..." }} }}
91
 
92
- ACTION UNIQUE. STOP APRÈS JSON.
93
  """
94
 
95
  def _format_messages_for_groq(self, system_prompt, chat_history, user_message):
96
  msgs = [{"role": "system", "content": system_prompt}]
97
- # Réduction de l'historique à 4 messages max pour économiser les tokens
98
- for m in chat_history[-4:]:
99
  role = "assistant" if m["role"] in ["model", "assistant"] else "user"
100
  content = str(m.get("content", ""))
101
  if content.strip():
@@ -105,7 +103,7 @@ ACTION UNIQUE. STOP APRÈS JSON.
105
 
106
  def _format_messages_for_gemini(self, system_prompt, chat_history, user_message):
107
  msgs = [{"role": "user", "parts": [system_prompt]}]
108
- for m in chat_history[-6:]:
109
  role = "user" if m["role"] == "user" else "model"
110
  content = str(m.get("content", ""))
111
  if content.strip():
@@ -121,13 +119,13 @@ ACTION UNIQUE. STOP APRÈS JSON.
121
  try:
122
  response_text = ""
123
  if "gemini" in model_name:
124
- if not self.google_key: continue # Skip si pas de clé
125
  formatted_msgs = self._format_messages_for_gemini(system_prompt, chat_history, user_message)
126
  model = genai.GenerativeModel(model_name)
127
  res = model.generate_content(formatted_msgs)
128
  response_text = res.text
129
  else:
130
- if not self.groq_client: continue # Skip si pas de clé
131
  formatted_msgs = self._format_messages_for_groq(system_prompt, chat_history, user_message)
132
  completion = self.groq_client.chat.completions.create(
133
  model=model_name,
@@ -136,24 +134,23 @@ ACTION UNIQUE. STOP APRÈS JSON.
136
  )
137
  response_text = completion.choices[0].message.content
138
 
139
- clean_text = re.sub(r"```json", "", response_text, flags=re.IGNORECASE)
140
- clean_text = re.sub(r"```", "", clean_text).strip()
 
141
 
142
  action = None
143
- if "{" in clean_text and "}" in clean_text:
144
  try:
145
- json_start = clean_text.find("{")
146
- json_end = clean_text.rfind("}") + 1
147
- json_str = clean_text[json_start:json_end]
148
  action = json.loads(json_str)
149
- clean_text = clean_text[:json_start] + clean_text[json_end:]
150
- clean_text = clean_text.strip()
151
  except: pass
152
 
153
- return clean_text, action
 
154
 
155
  except Exception as e:
156
  last_error = str(e)
157
- continue # Essayer le modèle suivant
158
 
159
- return f"⚠️ ERREUR TOUS MODÈLES : {last_error}", None
 
1
  """
2
+ MODULE JASMINE AGENT - V30 SILENT EXECUTOR
3
+ ==========================================
4
+ Objectif : Forcer le modèle à agir (JSON) sans discuter.
5
+ Modif : Prompt "Headless", Compression Tokens, SPARQL Safe Mode.
6
  """
7
  import google.generativeai as genai
8
  from groq import Groq
 
29
  self.rdf_store = rdf_store
30
  self.ontology_rules = ontology_rules
31
 
32
+ # PRIORITÉ MODÈLE : Gemini (Context Window large) > Llama
 
 
33
  self.MODEL_CASCADE = [
 
34
  "gemini-2.0-flash-exp",
35
  "llama-3.3-70b-versatile",
36
  "gemini-2.0-flash-lite",
 
38
  ]
39
 
40
  def _generate_dynamic_schema_prompt(self):
41
+ """Compression du schéma pour économiser les tokens"""
42
  schema_map = defaultdict(lambda: {"relations": [], "props": []})
43
 
44
  for rule in self.ontology_rules:
 
47
  obj = rule.get('ObjectColOrConcept', 'unknown')
48
  obj_type = rule.get('ObjectType', 'data_property')
49
 
50
+ # Nettoyage nom prédicat
51
  pred_clean = predicate if ":" in predicate else f"vortex:{predicate}"
52
 
53
  if obj_type == 'relation':
54
+ schema_map[subject]["relations"].append(f"-> {pred_clean}")
55
  else:
56
  schema_map[subject]["props"].append(f"{pred_clean}")
57
 
58
+ # Texte compact
59
+ lines = []
60
  for cls, data in schema_map.items():
61
+ lines.append(f"📦 {cls}")
62
+ if data["relations"]: lines.append(f" 🔗 {', '.join(data['relations'])}")
63
+ if data["props"]: lines.append(f" 📝 {', '.join(data['props'])}")
 
 
 
64
 
65
+ return "\n".join(lines)
66
 
67
  def build_system_prompt(self):
68
  dynamic_schema = self._generate_dynamic_schema_prompt()
69
 
70
  return f"""
71
+ ROLE: MOTEUR D'EXÉCUTION HEADLESS (JSON ONLY).
72
+ TU N'ES PAS UN CHATBOT. TU NE PARLES PAS. TU EXÉCUTES.
 
73
 
74
+ CONTEXTE RDF (ONTOLOGIE) :
75
+ PREFIX: vortex: <http://vortex.ai/ontology#>
76
  {dynamic_schema}
77
 
78
+ ⚠️ RÈGLES SPARQL DE SURVIE :
79
+ 1. **FILTER SAFE** : Utilise TOUJOURS `FILTER(CONTAINS(STR(?var), 'Valeur'))` au lieu de `?var = 'Valeur'`. Cela évite les erreurs de type (URI vs String).
80
+ 2. **NOMS EXACTS** : Utilise uniquement les prédicats listés ci-dessus (ex: `vortex:habite_ville`).
81
+
82
+ PROTOCOLE DE RÉPONSE :
83
+ 1. Si tu dois chercher une info -> Renvoie JSON `execute_sparql`.
84
+ 2. Si tu dois trouver une URI -> Renvoie JSON `search_semantic`.
85
+ 3. Si tu as fini -> Réponds en texte simple (courtememnt).
86
 
87
+ FORMAT ATTENDU (Sortie STRICTE) :
88
+ {{"tool": "execute_sparql", "args": {{"query": "SELECT ?s WHERE {{ ?s vortex:habite_ville ?v . FILTER(CONTAINS(STR(?v), 'Dakar')) }}"}}}}
 
89
 
90
+ INTERDIT D'ÉCRIRE DU TEXTE AVANT OU APRÈS LE JSON.
91
  """
92
 
93
  def _format_messages_for_groq(self, system_prompt, chat_history, user_message):
94
  msgs = [{"role": "system", "content": system_prompt}]
95
+ # On garde peu d'historique pour éviter le débordement de tokens
96
+ for m in chat_history[-3:]:
97
  role = "assistant" if m["role"] in ["model", "assistant"] else "user"
98
  content = str(m.get("content", ""))
99
  if content.strip():
 
103
 
104
  def _format_messages_for_gemini(self, system_prompt, chat_history, user_message):
105
  msgs = [{"role": "user", "parts": [system_prompt]}]
106
+ for m in chat_history[-5:]:
107
  role = "user" if m["role"] == "user" else "model"
108
  content = str(m.get("content", ""))
109
  if content.strip():
 
119
  try:
120
  response_text = ""
121
  if "gemini" in model_name:
122
+ if not self.google_key: continue
123
  formatted_msgs = self._format_messages_for_gemini(system_prompt, chat_history, user_message)
124
  model = genai.GenerativeModel(model_name)
125
  res = model.generate_content(formatted_msgs)
126
  response_text = res.text
127
  else:
128
+ if not self.groq_client: continue
129
  formatted_msgs = self._format_messages_for_groq(system_prompt, chat_history, user_message)
130
  completion = self.groq_client.chat.completions.create(
131
  model=model_name,
 
134
  )
135
  response_text = completion.choices[0].message.content
136
 
137
+ # Nettoyage agressif pour trouver le JSON
138
+ clean_text = response_text.strip()
139
+ match = re.search(r'(\{.*"tool":.*\})', clean_text, re.DOTALL)
140
 
141
  action = None
142
+ if match:
143
  try:
144
+ json_str = match.group(1)
 
 
145
  action = json.loads(json_str)
146
+ return "", action # On retourne une chaîne vide pour le texte, car c'est une action pure
 
147
  except: pass
148
 
149
+ # Si pas de JSON trouvé, c'est une réponse texte
150
+ return clean_text, None
151
 
152
  except Exception as e:
153
  last_error = str(e)
154
+ continue
155
 
156
+ return f"⚠️ ERREUR SYSTEME : {last_error}", None