django-lucy-assist 1.0.4__py3-none-any.whl → 1.0.5__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.0.4
3
+ Version: 1.0.5
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>
@@ -183,14 +183,6 @@ Lucy Assist expose plusieurs endpoints API :
183
183
 
184
184
  [Revolucy](https://www.revolucy.fr)
185
185
 
186
- ## Versionning
187
-
188
- - V1.0.0 | Création du module Lucy
189
- - V1.0.1 | Correction de bugs
190
- - V1.0.2 | Correction de bugs
191
- - V1.0.3 | Correction de bugs
192
- - V1.0.4 | Ajout Prompt Custom dans configuration
193
-
194
186
  ## Déploiement Pypi
195
187
 
196
188
  1. `docker-compose exec django pip install build twine`
@@ -1,12 +1,13 @@
1
- lucy_assist/__init__.py,sha256=Tr_o2S-OSLV_yuIm237gYrKYpvFeySwoHgui9qV1xW0,335
1
+ lucy_assist/__init__.py,sha256=kJG9Bgp37TXe9Ph2DsiuyDMalCvaJkAXRDPGYqASiyk,335
2
2
  lucy_assist/admin.py,sha256=-hNfuwuMfxgZVFQc_ODy6WcyZPxrM_8TfKsRMd0fj38,694
3
3
  lucy_assist/apps.py,sha256=zHZtlBXs5ML4CKtGg7xDyptSWzLfB1ks2VvbXF50hdo,264
4
- lucy_assist/conf.py,sha256=xp5t-CRG4L7R-NOkb2sY6-XkmGee7M7Eb-jAVr-CAzk,3500
4
+ lucy_assist/conf.py,sha256=sWcAdJTSE3Hn_guTifZaRCLKIuJMvR9RwJk2GEKCFOI,3520
5
5
  lucy_assist/constantes.py,sha256=YppDWi1DQueMwJk3jmeGPDi-UTmSUUDSwR6sW7QzBn4,4083
6
6
  lucy_assist/context_processors.py,sha256=mDrr9G5XztDfJLGq_75X1rkJbVI5De08ys_pW3y12Dw,2210
7
7
  lucy_assist/signals.py,sha256=aQA84oe9JNL72eeV5kURTTV-9CcQpqakDle1Lv3dnFY,861
8
8
  lucy_assist/urls.py,sha256=Qr8jJjEyC_EFGAeiZnjhgTc-9P4Y-TqKDaYicWRp_GQ,1451
9
9
  lucy_assist/migrations/0001_initial.py,sha256=Z4chOo4Ok_5VqQyHvfPS00ikyuefdwHF3LaH4SKpzoQ,4977
10
+ lucy_assist/migrations/0002_configurationlucyassist_prompt_complementaire.py,sha256=yWE4-RPHQtPAqYHlAhr4EsXgk35kI8zRQArlAP_JwDc,418
10
11
  lucy_assist/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
12
  lucy_assist/models/__init__.py,sha256=JQMr50P94sBrKwwcNArRE_qnk7dLnknMKCabeRLskJc,415
12
13
  lucy_assist/models/base.py,sha256=Ql2AY7bzvcxZhxsPdCpbVEKvtWD-NBkHTLYZX4TjT3s,1532
@@ -17,12 +18,12 @@ lucy_assist/models/project_context_cache.py,sha256=Bnb0VU7pv7QEvjOI6JSLEPvL4Bxsk
17
18
  lucy_assist/services/__init__.py,sha256=I0brW674WNIKkGHj2lj4sGEDD7HUAr5Z254dsbirdLk,691
18
19
  lucy_assist/services/bug_notification_service.py,sha256=OyowCvAs-QDlsGQ_WTFoc4lRe9detD7r6ZyYK0JD2Sc,7217
19
20
  lucy_assist/services/claude_service.py,sha256=vYeotZKwFghbWNmN_VM0uggnFQgtNNK0SP3e9QPQzgc,16218
20
- lucy_assist/services/context_service.py,sha256=RakjdAV74GlJlLsLYiTRluTQVmnkKC92xrXgWhnYJHQ,12232
21
- lucy_assist/services/crud_service.py,sha256=E8-xRxPXXbUuPakGXPsbZfImEJWZx4BZq5DhM5q2I08,11436
21
+ lucy_assist/services/context_service.py,sha256=aNPvo8b9pUjqnGpd5p6zUt4QJAYdJwjOw2V7URe1ANE,13230
22
+ lucy_assist/services/crud_service.py,sha256=wpFFFpApQfycjY5I2AWYk_sZEXUsMI0HiQ69LKeRz4U,13051
22
23
  lucy_assist/services/gitlab_service.py,sha256=uH83fwRSCwiRItznENpYQG4aPckjafYIV9z6OChUrZg,8056
23
24
  lucy_assist/services/project_context_service.py,sha256=bIuqTanc59gP_BLod3oQgWplxpiCgByg-kbUMe_57CQ,14053
24
25
  lucy_assist/services/tool_executor_service.py,sha256=fXLH4Aaip-HPX1nNRs8UQ1N77rGloBahSwOR9gPmjfU,13583
25
- lucy_assist/services/tools_definition.py,sha256=xUJCtFZ1uROxB7jNHY0X75fs5nzFAKr4VpSHtKgepxU,8974
26
+ lucy_assist/services/tools_definition.py,sha256=_3wfZOtzPtN44H8ITjEPzGn1LVxAu0ipcWLXsvzLyHE,8923
26
27
  lucy_assist/static/lucy_assist/css/lucy-assist.css,sha256=gUfj4OUTz_aFiXWau1iXtHEmfUCkUI2zGMfwkLk2nXs,18190
27
28
  lucy_assist/static/lucy_assist/image/icon-lucy.png,sha256=FOYlwXAt40Gr9jsWFmhgPivYOBFWKeYW0lxJI5Up-GM,6710
28
29
  lucy_assist/static/lucy_assist/js/lucy-assist.js,sha256=dmXtSPQ38LisoRWZd3R1Ms6OvVaEk3OrYKcVM0OMb_Q,28207
@@ -38,7 +39,7 @@ lucy_assist/utils/message_utils.py,sha256=YzcLHnl1ig4d5_utHCJwgxS7tKmd49Q-tuo78e
38
39
  lucy_assist/utils/token_utils.py,sha256=rxe9jHjcRJcaIlcw0QuVmYXOjscTsUsxnhhI6RMBzDM,2608
39
40
  lucy_assist/views/__init__.py,sha256=uUPYpuHlBC8j7zKS_DDoWjwpCpRnOIXETY-S2-Ss0cY,288
40
41
  lucy_assist/views/api_views.py,sha256=iCvdTTTJ73r3jfyZVjcEDi3Of2wP_N24G_QsXwc-Euk,23617
41
- django_lucy_assist-1.0.4.dist-info/METADATA,sha256=FIcrAyAOj5EVwJ4JSLieSDoZ3cv5xTd9aTVUVwupwl8,5736
42
- django_lucy_assist-1.0.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
43
- django_lucy_assist-1.0.4.dist-info/top_level.txt,sha256=T-UCiwpn5yF3Oem3234TUpSVnEgbkrM2rGz9Tz5N-QA,12
44
- django_lucy_assist-1.0.4.dist-info/RECORD,,
42
+ django_lucy_assist-1.0.5.dist-info/METADATA,sha256=anJPOWh-GwZl2vhMz5er7zGGSSR9SAzw76KFbANystc,5543
43
+ django_lucy_assist-1.0.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
44
+ django_lucy_assist-1.0.5.dist-info/top_level.txt,sha256=T-UCiwpn5yF3Oem3234TUpSVnEgbkrM2rGz9Tz5N-QA,12
45
+ django_lucy_assist-1.0.5.dist-info/RECORD,,
lucy_assist/__init__.py CHANGED
@@ -5,7 +5,7 @@ Un chatbot IA basé sur Claude d'Anthropic, intégrable dans n'importe quelle
5
5
  application Django pour fournir une assistance contextuelle aux utilisateurs.
6
6
  """
7
7
 
8
- __version__ = '1.0.4'
8
+ __version__ = '1.0.5'
9
9
  __author__ = 'Revolucy'
10
10
 
11
11
  default_app_config = 'lucy_assist.apps.LucyAssistConfig'
lucy_assist/conf.py CHANGED
@@ -47,13 +47,13 @@ class LucyAssistSettings:
47
47
  # Nombre moyen de tokens par conversation
48
48
  'TOKENS_MOYENS_PAR_CONVERSATION': 2000,
49
49
 
50
- # Questions fréquentes par défaut
50
+ # Questions fréquentes par défaut (génériques)
51
51
  'QUESTIONS_FREQUENTES_DEFAULT': [
52
- "Comment créer un nouveau membre ?",
53
- "Comment effectuer un paiement ?",
52
+ "Comment créer un nouveau client ?",
53
+ "Comment effectuer une recherche ?",
54
54
  "Comment exporter des données ?",
55
55
  "Comment modifier mon profil ?",
56
- "Où trouver la liste des adhésions ?",
56
+ "Où trouver la liste des réservations ?",
57
57
  ],
58
58
  }
59
59
 
@@ -0,0 +1,18 @@
1
+ # Generated by Django 6.0.1 on 2026-01-27 09:51
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('lucy_assist', '0001_initial'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='configurationlucyassist',
15
+ name='prompt_complementaire',
16
+ field=models.TextField(blank=True, default=''),
17
+ ),
18
+ ]
@@ -10,6 +10,7 @@ from django.urls import resolve, Resolver404
10
10
  from django.apps import apps
11
11
 
12
12
  from lucy_assist.utils.log_utils import LogUtils
13
+ from lucy_assist.conf import lucy_assist_settings
13
14
 
14
15
 
15
16
  class ContextService:
@@ -269,16 +270,28 @@ class ContextService:
269
270
  except LookupError:
270
271
  continue
271
272
  else:
272
- # Chercher dans les principaux modèles métier
273
- model_names = ['Membre', 'Structure', 'Paiement', 'Adhesion']
274
- for name in model_names:
275
- for app_config in apps.get_app_configs():
276
- try:
277
- model = app_config.get_model(name)
278
- models_to_search.append(model)
279
- break
280
- except LookupError:
281
- continue
273
+ # Découvrir dynamiquement les modèles du projet
274
+ apps_prefix = lucy_assist_settings.PROJECT_APPS_PREFIX
275
+
276
+ for app_config in apps.get_app_configs():
277
+ # Filtrer par préfixe si configuré
278
+ if apps_prefix and not app_config.name.startswith(apps_prefix):
279
+ continue
280
+
281
+ # Exclure les apps système et lucy_assist
282
+ if app_config.name.startswith('django.') or app_config.name == 'lucy_assist':
283
+ continue
284
+
285
+ # Ajouter les modèles de cette app (limiter à 3 par app)
286
+ app_models = list(app_config.get_models())[:3]
287
+ models_to_search.extend(app_models)
288
+
289
+ # Limiter le nombre total de modèles à rechercher
290
+ if len(models_to_search) >= 10:
291
+ break
292
+
293
+ # Log des modèles découverts pour debug
294
+ LogUtils.info(f"[search_objects] Recherche '{query}' dans {len(models_to_search)} modèles: {[m.__name__ for m in models_to_search]}")
282
295
 
283
296
  # Rechercher dans chaque modèle
284
297
  for model in models_to_search:
@@ -291,6 +304,7 @@ class ContextService:
291
304
  search_fields.append(field.name)
292
305
 
293
306
  if not search_fields:
307
+ LogUtils.info(f"[search_objects] {model.__name__}: aucun champ texte trouvé")
294
308
  continue
295
309
 
296
310
  # Construire la requête
@@ -304,9 +318,12 @@ class ContextService:
304
318
  # dépendent de ThreadLocal/middleware pour l'utilisateur courant
305
319
  try:
306
320
  queryset = model.objects.all()
321
+ count = queryset.count()
322
+ LogUtils.info(f"[search_objects] {model.__name__}: {count} objets en base")
307
323
  objects = queryset.filter(q_objects)[:limit]
308
- except AttributeError:
324
+ except AttributeError as e:
309
325
  # Si le manager a besoin d'un utilisateur non disponible, skip ce modèle
326
+ LogUtils.info(f"[search_objects] {model.__name__}: skip (AttributeError: {e})")
310
327
  continue
311
328
 
312
329
  for obj in objects:
@@ -322,7 +339,7 @@ class ContextService:
322
339
  return results
323
340
 
324
341
  except Exception as e:
325
- LogUtils.info(f"Erreur recherche dans {model.__name__}: {e}")
342
+ LogUtils.info(f"[search_objects] Erreur recherche dans {model.__name__}: {e}")
326
343
  continue
327
344
 
328
345
  return results
@@ -1,7 +1,6 @@
1
1
  """
2
2
  Service pour exécuter des opérations CRUD via Lucy Assist.
3
3
  """
4
- import logging
5
4
  from typing import Dict, List, Optional, Any
6
5
 
7
6
  from django.apps import apps
@@ -9,6 +8,7 @@ from django.db import transaction
9
8
  from django.forms import modelform_factory
10
9
 
11
10
  from lucy_assist.utils.log_utils import LogUtils
11
+ from lucy_assist.conf import lucy_assist_settings
12
12
 
13
13
 
14
14
  class CRUDService:
@@ -37,35 +37,67 @@ class CRUDService:
37
37
 
38
38
  def get_model(self, app_name: str, model_name: str):
39
39
  """Récupère une classe de modèle Django."""
40
+ # Essayer d'abord avec app_name fourni
40
41
  try:
41
- return apps.get_model(app_name, model_name)
42
+ model = apps.get_model(app_name, model_name)
43
+ LogUtils.info(f"[CRUD] Modèle trouvé: {app_name}.{model_name}")
44
+ return model
42
45
  except LookupError:
43
- # Essayer de trouver dans toutes les apps
44
- for app_config in apps.get_app_configs():
45
- try:
46
- return app_config.get_model(model_name)
47
- except LookupError:
48
- continue
49
- return None
46
+ pass
47
+
48
+ # Essayer avec le préfixe PROJECT_APPS_PREFIX
49
+ apps_prefix = lucy_assist_settings.PROJECT_APPS_PREFIX or ''
50
+ if apps_prefix and not app_name.startswith(apps_prefix):
51
+ try:
52
+ full_app_name = f"{apps_prefix}{app_name}"
53
+ model = apps.get_model(full_app_name, model_name)
54
+ LogUtils.info(f"[CRUD] Modèle trouvé avec préfixe: {full_app_name}.{model_name}")
55
+ return model
56
+ except LookupError:
57
+ pass
58
+
59
+ # Chercher dans toutes les apps du projet
60
+ for app_config in apps.get_app_configs():
61
+ # Filtrer par préfixe si configuré
62
+ if apps_prefix and not app_config.name.startswith(apps_prefix):
63
+ continue
64
+
65
+ try:
66
+ model = app_config.get_model(model_name)
67
+ LogUtils.info(f"[CRUD] Modèle trouvé par recherche: {app_config.label}.{model_name}")
68
+ return model
69
+ except LookupError:
70
+ continue
71
+
72
+ LogUtils.warning(f"[CRUD] Modèle non trouvé: {app_name}.{model_name}")
73
+ return None
50
74
 
51
75
  def get_form_class(self, model):
52
76
  """Récupère ou crée une classe de formulaire pour le modèle."""
53
- # Essayer de trouver un formulaire existant
54
77
  app_label = model._meta.app_label
78
+ model_name = model.__name__
55
79
 
56
- # Chercher le formulaire dans l'app
57
- try:
58
- forms_module = __import__(
59
- f'apps.{app_label}.forms',
60
- fromlist=[f'{model.__name__}Form']
61
- )
62
- form_class = getattr(forms_module, f'{model.__name__}Form', None)
63
- if form_class:
64
- return form_class
65
- except (ImportError, AttributeError):
66
- pass
80
+ # Liste des chemins d'import possibles pour les formulaires
81
+ apps_prefix = lucy_assist_settings.PROJECT_APPS_PREFIX or ''
82
+ possible_paths = [
83
+ f'{apps_prefix}{app_label}.forms', # apps.client.forms
84
+ f'{app_label}.forms', # client.forms
85
+ f'apps.{app_label}.forms', # apps.client.forms (legacy)
86
+ ]
87
+
88
+ # Essayer de trouver un formulaire existant
89
+ for path in possible_paths:
90
+ try:
91
+ forms_module = __import__(path, fromlist=[f'{model_name}Form'])
92
+ form_class = getattr(forms_module, f'{model_name}Form', None)
93
+ if form_class:
94
+ LogUtils.info(f"[CRUD] Formulaire trouvé: {path}.{model_name}Form")
95
+ return form_class
96
+ except (ImportError, AttributeError, ModuleNotFoundError):
97
+ continue
67
98
 
68
99
  # Créer un formulaire automatique
100
+ LogUtils.info(f"[CRUD] Formulaire auto-généré pour {model_name}")
69
101
  return modelform_factory(model, fields='__all__')
70
102
 
71
103
  def get_required_fields(self, model) -> List[Dict]:
@@ -10,8 +10,7 @@ LUCY_ASSIST_TOOLS = [
10
10
  {
11
11
  "name": "create_object",
12
12
  "description": """Crée un nouvel objet dans la base de données.
13
- Utilise ce tool quand l'utilisateur demande de créer un membre, une structure,
14
- un paiement, une adhésion ou tout autre objet métier.
13
+ Utilise ce tool quand l'utilisateur demande de créer un objet métier (client, réservation, etc.).
15
14
  IMPORTANT: Utilise ce tool en priorité quand l'utilisateur demande une création.
16
15
  Ne demande pas de confirmation, exécute directement l'action.""",
17
16
  "input_schema": {
@@ -19,11 +18,11 @@ LUCY_ASSIST_TOOLS = [
19
18
  "properties": {
20
19
  "app_name": {
21
20
  "type": "string",
22
- "description": "Nom de l'application Django (ex: membre, structure, paiement, adhesion)"
21
+ "description": "Nom de l'application Django (ex: client, reservation, franchise)"
23
22
  },
24
23
  "model_name": {
25
24
  "type": "string",
26
- "description": "Nom du modèle Django (ex: Membre, Structure, Paiement, Adhesion)"
25
+ "description": "Nom du modèle Django (ex: Client, Reservation, Franchise)"
27
26
  },
28
27
  "data": {
29
28
  "type": "object",
@@ -37,7 +36,7 @@ LUCY_ASSIST_TOOLS = [
37
36
  {
38
37
  "name": "update_object",
39
38
  "description": """Met à jour un objet existant dans la base de données.
40
- Utilise ce tool quand l'utilisateur demande de modifier un membre, une structure, etc.
39
+ Utilise ce tool quand l'utilisateur demande de modifier un objet existant.
41
40
  IMPORTANT: Exécute directement l'action sans demander de confirmation.""",
42
41
  "input_schema": {
43
42
  "type": "object",
@@ -90,7 +89,7 @@ LUCY_ASSIST_TOOLS = [
90
89
  {
91
90
  "name": "search_objects",
92
91
  "description": """Recherche des objets dans la base de données.
93
- Utilise ce tool pour trouver des membres, structures, paiements, etc.""",
92
+ Utilise ce tool pour trouver des clients, réservations, ou tout autre objet métier.""",
94
93
  "input_schema": {
95
94
  "type": "object",
96
95
  "properties": {
@@ -206,7 +205,7 @@ LUCY_ASSIST_TOOLS = [
206
205
  },
207
206
  "model_name": {
208
207
  "type": "string",
209
- "description": "Nom du modèle Django concerné (si identifiable, ex: Membre, Structure)"
208
+ "description": "Nom du modèle Django concerné (si identifiable, ex: Client, Reservation)"
210
209
  },
211
210
  "action_type": {
212
211
  "type": "string",