django-lucy-assist 1.2.0__py3-none-any.whl → 1.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-lucy-assist
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Assistant IA intelligent Revolucy pour outil métier
5
5
  Author-email: Revolucy <hello@revolucy.fr>
6
6
  Maintainer-email: Maxence <hello@revolucy.fr>
@@ -1,18 +1,19 @@
1
- lucy_assist/__init__.py,sha256=klW0laX0ly7M6Uvq0FsrMZXn1znyOe2ige3H2CD_LaI,325
2
- lucy_assist/admin.py,sha256=DHdcvkCYNkVBZoWrZ58AQxOEN2EmsN4E09qRnqYyARI,2919
1
+ lucy_assist/__init__.py,sha256=eg52UR1U2IqaSiZSe-_3OPuY4KhzK-eBk5NuZDboUtU,325
2
+ lucy_assist/admin.py,sha256=pDpxwrrSh-bY7vQhf5ZA3HKIugjP1OdthGDI-y5AVsk,3178
3
3
  lucy_assist/apps.py,sha256=zHZtlBXs5ML4CKtGg7xDyptSWzLfB1ks2VvbXF50hdo,264
4
- lucy_assist/conf.py,sha256=filh-SAWuFt69hu0MmpvX6VUhe7BvZqDTlqQ2riRmKY,3846
5
- lucy_assist/constantes.py,sha256=izuIYppBa9mhLE4lzaNBj6erWuto0ZzB7_Iwn3fmA28,5005
4
+ lucy_assist/conf.py,sha256=RgtZSrUwHZjjbEGZqQvifvC4GNdICmhty2XetfvAbls,3992
5
+ lucy_assist/constantes.py,sha256=9L2VGBK0HorJoNDGdwQlhEfT75W-xNPAhtS0-qgawBs,3947
6
6
  lucy_assist/context_processors.py,sha256=tnzil4CEJwtExuQGLyRzIEuwpQfnwJ-eMgkkR6zNgSg,2200
7
- lucy_assist/signals.py,sha256=aQA84oe9JNL72eeV5kURTTV-9CcQpqakDle1Lv3dnFY,861
7
+ lucy_assist/signals.py,sha256=i3Uj9v-0zyAIROFl86fLlwaqRqQizL9_CpWmVjJbk8g,863
8
8
  lucy_assist/urls.py,sha256=Qr8jJjEyC_EFGAeiZnjhgTc-9P4Y-TqKDaYicWRp_GQ,1451
9
9
  lucy_assist/migrations/0001_initial.py,sha256=quQUw6is5clu3Q4B3P_0mUZVE33lSvdimH_OTCY3ycE,4977
10
10
  lucy_assist/migrations/0002_configurationlucyassist_prompt_complementaire.py,sha256=yWE4-RPHQtPAqYHlAhr4EsXgk35kI8zRQArlAP_JwDc,418
11
11
  lucy_assist/migrations/0003_configurationlucyassist_crud_views_mapping.py,sha256=1vnu1VwVBouskhQBgLz-Lp71p4a9Cb9PjrT-RLVGY7s,429
12
+ lucy_assist/migrations/0004_configurationlucyassist_system_prompt.py,sha256=0a78xO15oiLcCSlO8BicMac_QE8IfiNSt9WlGptVNNo,568
12
13
  lucy_assist/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
14
  lucy_assist/models/__init__.py,sha256=JQMr50P94sBrKwwcNArRE_qnk7dLnknMKCabeRLskJc,415
14
15
  lucy_assist/models/base.py,sha256=Ql2AY7bzvcxZhxsPdCpbVEKvtWD-NBkHTLYZX4TjT3s,1532
15
- lucy_assist/models/configuration.py,sha256=9MjEI2gG2lx1H2UyHo9zCnDOoUC_1hVOKOYwzWuYIHs,8596
16
+ lucy_assist/models/configuration.py,sha256=EdCulH59rnjHM-c307a9dyjiRCEwdyN64Vwa8Vb6tuQ,12487
16
17
  lucy_assist/models/conversation.py,sha256=psx2AQtQ5SFP-AJD7wbGabpXXLIGYngdVZfeoTlgKtQ,1849
17
18
  lucy_assist/models/message.py,sha256=kf-ffMtLYNFhXYUrB3QSL97KKDJUOMrKaPjKeOcJa_o,1492
18
19
  lucy_assist/models/project_context_cache.py,sha256=Bnb0VU7pv7QEvjOI6JSLEPvL4BxskCQ0ojWGxO7YDSM,6530
@@ -21,8 +22,8 @@ lucy_assist/services/bug_notification_service.py,sha256=OyowCvAs-QDlsGQ_WTFoc4lR
21
22
  lucy_assist/services/context_service.py,sha256=Vx2tR6W1jmqr06pGn924uzWTF2SV7RAO_JIScxYbCy0,17087
22
23
  lucy_assist/services/crud_service.py,sha256=W50K02ZkaScyzO-yRaBd9Cjva9PGH7OjNTipSruErcA,41495
23
24
  lucy_assist/services/gitlab_service.py,sha256=uH83fwRSCwiRItznENpYQG4aPckjafYIV9z6OChUrZg,8056
24
- lucy_assist/services/mistral_service.py,sha256=NZTEQacsoNgDVXkgmyRcxENaq6toNcDtRu_dKE4PDY0,18578
25
- lucy_assist/services/project_context_service.py,sha256=bIuqTanc59gP_BLod3oQgWplxpiCgByg-kbUMe_57CQ,14053
25
+ lucy_assist/services/mistral_service.py,sha256=mmoI2ixBFTeKFOId-2goe-NGpvjd_cMW9PMuV2C-P2k,18624
26
+ lucy_assist/services/project_context_service.py,sha256=6QNVL-K2D-8whIPfyAJhi_xAifqTXxR09461YNBwg4o,14826
26
27
  lucy_assist/services/tool_executor_service.py,sha256=nTeJbj2JUDMk_bmLVgGxtWqFbSgywq-tNRBK-_3Yayw,15155
27
28
  lucy_assist/services/tools_definition.py,sha256=qIiIoB5_QMqmkD03OOEk_3A5Nc0g0avAjDCj3Uq3_r8,11735
28
29
  lucy_assist/services/view_discovery_service.py,sha256=J9LkHXUOzlGS3cyft2_jA1X27TWOd3xViN4M7GOROVw,11872
@@ -41,7 +42,7 @@ lucy_assist/utils/message_utils.py,sha256=YzcLHnl1ig4d5_utHCJwgxS7tKmd49Q-tuo78e
41
42
  lucy_assist/utils/token_utils.py,sha256=aBzyKVqpU67rAetO_Ee3QIfPtbNyjyWSe6qPfzIRJF8,2608
42
43
  lucy_assist/views/__init__.py,sha256=uUPYpuHlBC8j7zKS_DDoWjwpCpRnOIXETY-S2-Ss0cY,288
43
44
  lucy_assist/views/api_views.py,sha256=sJwKNC9-mi1zMbgyKZTVvT3pz4hMmw4jR0rrS7iJR-g,23619
44
- django_lucy_assist-1.2.0.dist-info/METADATA,sha256=XGoB6psj3RT8EkuP7Ip8ZKmjHwcBDG6s7zTY8ZNB1Gk,5899
45
- django_lucy_assist-1.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
46
- django_lucy_assist-1.2.0.dist-info/top_level.txt,sha256=T-UCiwpn5yF3Oem3234TUpSVnEgbkrM2rGz9Tz5N-QA,12
47
- django_lucy_assist-1.2.0.dist-info/RECORD,,
45
+ django_lucy_assist-1.2.2.dist-info/METADATA,sha256=t_hJ2rRU16S5X_o_Mj66IwNG4Nl8PQjrJi837ZGSjjs,5899
46
+ django_lucy_assist-1.2.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
47
+ django_lucy_assist-1.2.2.dist-info/top_level.txt,sha256=T-UCiwpn5yF3Oem3234TUpSVnEgbkrM2rGz9Tz5N-QA,12
48
+ django_lucy_assist-1.2.2.dist-info/RECORD,,
lucy_assist/__init__.py CHANGED
@@ -5,7 +5,7 @@ Un chatbot IA base sur Mistral AI, integrable dans n'importe quelle
5
5
  application Django pour fournir une assistance contextuelle aux utilisateurs.
6
6
  """
7
7
 
8
- __version__ = '1.2.0'
8
+ __version__ = '1.2.2'
9
9
  __author__ = 'Revolucy'
10
10
 
11
11
  default_app_config = 'lucy_assist.apps.LucyAssistConfig'
lucy_assist/admin.py CHANGED
@@ -26,7 +26,12 @@ class ConfigurationLucyAssistAdmin(admin.ModelAdmin):
26
26
  'fields': ('tokens_disponibles', 'prix_par_million_tokens', 'actif')
27
27
  }),
28
28
  ('Personnalisation', {
29
- 'fields': ('avatar', 'questions_frequentes', 'prompt_complementaire')
29
+ 'fields': ('avatar', 'questions_frequentes')
30
+ }),
31
+ ('Prompt Système', {
32
+ 'fields': ('system_prompt', 'prompt_complementaire'),
33
+ 'classes': ('collapse',),
34
+ 'description': 'Le prompt système est initialisé automatiquement si vide. Le prompt complémentaire est ajouté à la fin.'
30
35
  }),
31
36
  ('Mapping Automatique (lecture seule)', {
32
37
  'fields': ('crud_views_mapping_display', 'model_app_mapping_display'),
lucy_assist/conf.py CHANGED
@@ -17,6 +17,10 @@ class LucyAssistSettings:
17
17
  # Peut etre une string "mon_app.models.MonModele" ou une classe directement
18
18
  'BASE_MODEL': None,
19
19
 
20
+ # Nom de l'application (affiche dans le prompt systeme)
21
+ # Ex: 'Mon Application'
22
+ 'APPLICATION_NAME': 'cette application',
23
+
20
24
  # Cle API Mistral (peut aussi etre definie via MISTRAL_LUCY_API_KEY)
21
25
  'MISTRAL_LUCY_API_KEY': None,
22
26
 
lucy_assist/constantes.py CHANGED
@@ -53,27 +53,16 @@ class LucyAssistConstantes:
53
53
 
54
54
  # Prompts systeme pour Mistral
55
55
  SYSTEM_PROMPTS = {
56
- 'default': """Tu es Lucy, un assistant IA intégré dans un CRM métier (UNAF-CRM).
56
+ 'default': """Tu es Lucy, un assistant IA intégré dans l'application {application_name}.
57
57
  Tu as la capacité d'EXÉCUTER des actions directement dans l'application grâce aux tools disponibles.
58
58
 
59
59
  ## COMPORTEMENT PRIORITAIRE - ACTION FIRST
60
60
 
61
- RÈGLE FONDAMENTALE : Quand l'utilisateur te demande de faire quelque chose (créer, modifier, rechercher, supprimer),
62
- tu dois EXÉCUTER L'ACTION IMMÉDIATEMENT en utilisant les tools disponibles.
61
+ REGLE FONDAMENTALE : Quand l'utilisateur te demande de faire quelque chose (creer, modifier, rechercher, supprimer),
62
+ tu dois EXECUTER L'ACTION IMMEDIATEMENT en utilisant les tools disponibles.
63
63
 
64
- NE FAIS PAS : Expliquer comment faire, rediriger vers une page, dire que tu ne peux pas
65
- FAIS : Utiliser le tool approprié pour exécuter l'action demandée
66
-
67
- ## Exemples de comportement attendu
68
-
69
- Utilisateur : "Crée un membre Maxence Dupont"
70
- → Tu utilises le tool create_object avec les données {{"nom": "Dupont", "prenom": "Maxence"}}
71
-
72
- Utilisateur : "Cherche les membres de Paris"
73
- → Tu utilises le tool search_objects avec query="Paris" et model_name="Membre"
74
-
75
- Utilisateur : "Modifie l'adresse du membre 42"
76
- → Tu utilises le tool update_object avec l'object_id et les nouvelles données
64
+ NE FAIS PAS : Expliquer comment faire, rediriger vers une page, dire que tu ne peux pas
65
+ FAIS : Utiliser le tool approprie pour executer l'action demandee
77
66
 
78
67
  ## Quand NE PAS agir directement
79
68
 
@@ -93,32 +82,19 @@ Pour TOUTE demande de suppression, tu DOIS suivre cette procédure :
93
82
  3. Demande une confirmation EXPLICITE à l'utilisateur ("Confirmez-vous la suppression ?")
94
83
  4. SEULEMENT si l'utilisateur confirme explicitement (oui, ok, confirme, etc.), utilise `delete_object` avec `confirmed: true`
95
84
 
96
- Exemple de réponse après get_deletion_impact :
97
- "Voici les conséquences de la suppression du Client #42 :
98
- - 5 Réservations seront supprimées en cascade
99
- - 3 Paiements seront supprimés en cascade
100
- - 2 Documents auront leur champ 'client' mis à NULL
101
-
102
- **Confirmez-vous vouloir supprimer ce client et tous les éléments associés ?**"
103
-
104
85
  ## Contexte de la page actuelle
105
86
  {page_context}
106
87
 
107
88
  ## Permissions de l'utilisateur
108
89
  {user_permissions}
109
90
 
110
- ## Modèles principaux disponibles
111
- - Membre (membre) : nom, prenom, email, telephone, adresse, structure
112
- - Structure (structure) : nom, adresse, type, siret
113
- - Adhesion (adhesion) : membre, structure, date_debut, date_fin, montant
114
- - Paiement (paiement) : membre, montant, date, mode_paiement
115
-
116
- ## Format de réponse après action
91
+ ## Modèles disponibles dans l'application
92
+ {available_models}
117
93
 
118
- Après avoir exécuté une action, confirme brièvement :
119
- "✅ [Action effectuée] - [Détail bref]"
94
+ ## Format de reponse apres action
120
95
 
121
- Exemple : "✅ Membre créé - Maxence Dupont (ID: 123)"
96
+ Apres avoir execute une action, confirme brievement :
97
+ "[OK] [Action effectuee] - [Detail bref]"
122
98
  """,
123
99
  'crud': """Tu dois aider l'utilisateur à créer ou modifier un objet.
124
100
  Formulaire disponible : {form_info}
@@ -132,9 +108,9 @@ Erreur rapportée : {error_message}
132
108
 
133
109
  # Questions fréquentes par défaut (utilisées si non configurées dans le modèle)
134
110
  QUESTIONS_FREQUENTES_DEFAULT = [
135
- "Comment créer un nouveau membre ?",
136
- "Comment effectuer un paiement ?",
111
+ "Comment créer un nouvel enregistrement ?",
112
+ "Comment effectuer une recherche ?",
137
113
  "Comment exporter des données ?",
138
114
  "Comment modifier mon profil ?",
139
- " trouver la liste des adhésions ?",
115
+ "Quelles sont les fonctionnalités disponibles ?",
140
116
  ]
@@ -0,0 +1,22 @@
1
+ # Generated by Django
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('lucy_assist', '0003_configurationlucyassist_crud_views_mapping'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='configurationlucyassist',
15
+ name='system_prompt',
16
+ field=models.TextField(
17
+ blank=True,
18
+ default='',
19
+ help_text="Prompt système pour l'assistant IA. Initialisé automatiquement si vide."
20
+ ),
21
+ ),
22
+ ]
@@ -20,8 +20,16 @@ class ConfigurationLucyAssist(LucyAssistBaseModel):
20
20
  actif = models.BooleanField(default=True)
21
21
  avatar = models.ImageField(null=True, blank=True)
22
22
 
23
- # Instructions complémentaires pour le prompt
24
- prompt_complementaire = models.TextField(blank=True,default='')
23
+ # Prompt système principal pour l'assistant
24
+ # Initialisé automatiquement via signal si vide
25
+ system_prompt = models.TextField(
26
+ blank=True,
27
+ default='',
28
+ help_text="Prompt système pour l'assistant IA. Initialisé automatiquement si vide."
29
+ )
30
+
31
+ # Instructions complémentaires pour le prompt (ajoutées après le system_prompt)
32
+ prompt_complementaire = models.TextField(blank=True, default='')
25
33
 
26
34
  # Mapping des modèles vers leurs applications Django
27
35
  model_app_mapping = models.JSONField(blank=True, default=dict)
@@ -49,12 +57,20 @@ class ConfigurationLucyAssist(LucyAssistBaseModel):
49
57
  """
50
58
  Retourne la configuration singleton.
51
59
  Utilise le cache pour optimiser les performances.
60
+ Initialise le system_prompt si vide.
52
61
  """
53
62
  cache_key = 'lucy_assist_config'
54
63
  config = cache.get(cache_key)
55
64
 
56
65
  if config is None:
57
- config, _ = cls.objects.get_or_create(pk=1)
66
+ config, created = cls.objects.get_or_create(pk=1)
67
+
68
+ # Initialiser le system_prompt si vide (première utilisation ou reset)
69
+ if not config.system_prompt:
70
+ from lucy_assist.constantes import LucyAssistConstantes
71
+ config.system_prompt = LucyAssistConstantes.SYSTEM_PROMPTS['default']
72
+ config.save(update_fields=['system_prompt'])
73
+
58
74
  cache.set(cache_key, config, timeout=300) # Cache 5 minutes
59
75
 
60
76
  return config
@@ -222,6 +238,89 @@ class ConfigurationLucyAssist(LucyAssistBaseModel):
222
238
  config = cls.get_config()
223
239
  return config.get_app_for_model(model_name)
224
240
 
241
+ def get_system_prompt(
242
+ self,
243
+ page_context: str = "",
244
+ user_permissions: str = "",
245
+ available_models: str = ""
246
+ ) -> str:
247
+ """
248
+ Retourne le prompt système formaté avec les variables de contexte.
249
+
250
+ Args:
251
+ page_context: Contexte JSON de la page actuelle
252
+ user_permissions: Liste des permissions de l'utilisateur
253
+ available_models: Liste des modèles disponibles
254
+
255
+ Returns:
256
+ Prompt système complet avec le prompt_complementaire ajouté
257
+ """
258
+ prompt = self.system_prompt
259
+
260
+ # Remplacer les placeholders
261
+ prompt = prompt.format(
262
+ application_name=self._get_application_name(),
263
+ page_context=page_context,
264
+ user_permissions=user_permissions,
265
+ available_models=available_models
266
+ )
267
+
268
+ # Ajouter les instructions complémentaires si configurées
269
+ if self.prompt_complementaire:
270
+ prompt += f"\n\n## Instructions complémentaires\n{self.prompt_complementaire}"
271
+
272
+ return prompt
273
+
274
+ def _get_application_name(self) -> str:
275
+ """
276
+ Retourne le nom de l'application depuis les settings.
277
+ """
278
+ return getattr(lucy_assist_settings, 'APPLICATION_NAME', 'cette application')
279
+
280
+ def get_available_models_description(self) -> str:
281
+ """
282
+ Génère dynamiquement la description des modèles disponibles
283
+ à partir des modèles Django enregistrés.
284
+
285
+ Returns:
286
+ Description formatée des modèles avec leurs champs principaux
287
+ """
288
+ from django.apps import apps
289
+
290
+ descriptions = []
291
+ apps_prefix = lucy_assist_settings.PROJECT_APPS_PREFIX
292
+
293
+ for app_config in apps.get_app_configs():
294
+ # Filtrer les apps si un préfixe est configuré
295
+ if apps_prefix and not app_config.name.startswith(apps_prefix):
296
+ continue
297
+
298
+ # Ignorer les apps Django internes et lucy_assist
299
+ if app_config.name.startswith('django.') or app_config.name == 'lucy_assist':
300
+ continue
301
+
302
+ for model in app_config.get_models():
303
+ model_name = model.__name__
304
+ model_name_lower = model_name.lower()
305
+
306
+ # Récupérer les champs principaux (exclure les champs auto)
307
+ fields = []
308
+ for field in model._meta.get_fields():
309
+ if hasattr(field, 'name') and not field.name.startswith('_'):
310
+ if hasattr(field, 'auto_created') and field.auto_created:
311
+ continue
312
+ if field.name in ('id', 'pk', 'created_at', 'updated_at'):
313
+ continue
314
+ fields.append(field.name)
315
+
316
+ if fields:
317
+ fields_str = ", ".join(fields[:8]) # Limiter à 8 champs
318
+ if len(fields) > 8:
319
+ fields_str += ", ..."
320
+ descriptions.append(f"- {model_name} ({model_name_lower}) : {fields_str}")
321
+
322
+ return "\n".join(descriptions) if descriptions else "Aucun modèle configuré"
323
+
225
324
 
226
325
  def get_default_model_app_mapping() -> dict:
227
326
  """
@@ -57,8 +57,8 @@ class MistralService:
57
57
  """
58
58
  Construit le prompt systeme avec le contexte optimise.
59
59
 
60
- Utilise le cache pour reduire la redondance des informations
61
- sur le projet.
60
+ Utilise le prompt stocke en base de donnees et le cache
61
+ pour reduire la redondance des informations sur le projet.
62
62
  """
63
63
  # Recuperer les permissions utilisateur (compressees)
64
64
  user_permissions = []
@@ -81,17 +81,19 @@ class MistralService:
81
81
  'cache_stats': optimized_context.get('stats', {})
82
82
  }
83
83
 
84
- # Construire le prompt avec contexte compact
85
- prompt = LucyAssistConstantes.SYSTEM_PROMPTS['default'].format(
86
- page_context=json.dumps(enriched_context, ensure_ascii=False, indent=2),
87
- user_permissions=', '.join(user_permissions)
88
- )
89
-
90
- # Ajouter les instructions complementaires si configurees
84
+ # Recuperer la configuration avec le prompt systeme
91
85
  from lucy_assist.models import ConfigurationLucyAssist
92
86
  config = ConfigurationLucyAssist.get_config()
93
- if config.prompt_complementaire:
94
- prompt += f"\n\n## Instructions complementaires\n{config.prompt_complementaire}"
87
+
88
+ # Generer la description des modeles disponibles
89
+ available_models = config.get_available_models_description()
90
+
91
+ # Construire le prompt depuis la configuration
92
+ prompt = config.get_system_prompt(
93
+ page_context=json.dumps(enriched_context, ensure_ascii=False, indent=2),
94
+ user_permissions=', '.join(user_permissions),
95
+ available_models=available_models
96
+ )
95
97
 
96
98
  return prompt
97
99
 
@@ -332,23 +332,40 @@ class ProjectContextService:
332
332
  return None
333
333
 
334
334
  def _detect_model_from_question(self, question: str) -> Optional[str]:
335
- """Détecte si la question mentionne un modèle."""
336
- # Mots-clés courants et leurs modèles associés
337
- model_keywords = {
338
- 'membre': 'Membre',
339
- 'adhésion': 'Adhesion',
340
- 'cotisation': 'Cotisation',
341
- 'utilisateur': 'Utilisateur',
342
- 'user': 'Utilisateur',
343
- 'paiement': 'Paiement',
344
- 'facture': 'Facture',
345
- 'structure': 'Structure',
346
- }
335
+ """
336
+ Détecte si la question mentionne un modèle.
337
+
338
+ Utilise la liste dynamique des modèles Django enregistrés
339
+ au lieu d'une liste hardcodée.
340
+ """
341
+ from django.apps import apps
342
+ from lucy_assist.conf import lucy_assist_settings
347
343
 
348
344
  question_lower = question.lower()
349
- for keyword, model in model_keywords.items():
350
- if keyword in question_lower:
351
- return model
345
+ apps_prefix = lucy_assist_settings.PROJECT_APPS_PREFIX
346
+
347
+ for app_config in apps.get_app_configs():
348
+ # Filtrer les apps si un préfixe est configuré
349
+ if apps_prefix and not app_config.name.startswith(apps_prefix):
350
+ continue
351
+
352
+ # Ignorer les apps Django internes et lucy_assist
353
+ if app_config.name.startswith('django.') or app_config.name == 'lucy_assist':
354
+ continue
355
+
356
+ for model in app_config.get_models():
357
+ model_name = model.__name__
358
+ model_name_lower = model_name.lower()
359
+
360
+ # Vérifier si le nom du modèle apparaît dans la question
361
+ if model_name_lower in question_lower:
362
+ return model_name
363
+
364
+ # Vérifier aussi le verbose_name si disponible
365
+ if hasattr(model._meta, 'verbose_name'):
366
+ verbose_name = str(model._meta.verbose_name).lower()
367
+ if verbose_name in question_lower:
368
+ return model_name
352
369
 
353
370
  return None
354
371
 
lucy_assist/signals.py CHANGED
@@ -23,3 +23,5 @@ def message_post_save(sender, instance, created, **kwargs):
23
23
  config.tokens_disponibles = max(0, config.tokens_disponibles - instance.tokens_utilises)
24
24
  config._skip_signals = True
25
25
  config.save(update_fields=['tokens_disponibles'])
26
+
27
+