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.
- {django_lucy_assist-1.0.4.dist-info → django_lucy_assist-1.0.5.dist-info}/METADATA +1 -9
- {django_lucy_assist-1.0.4.dist-info → django_lucy_assist-1.0.5.dist-info}/RECORD +10 -9
- lucy_assist/__init__.py +1 -1
- lucy_assist/conf.py +4 -4
- lucy_assist/migrations/0002_configurationlucyassist_prompt_complementaire.py +18 -0
- lucy_assist/services/context_service.py +29 -12
- lucy_assist/services/crud_service.py +53 -21
- lucy_assist/services/tools_definition.py +6 -7
- {django_lucy_assist-1.0.4.dist-info → django_lucy_assist-1.0.5.dist-info}/WHEEL +0 -0
- {django_lucy_assist-1.0.4.dist-info → django_lucy_assist-1.0.5.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-lucy-assist
|
|
3
|
-
Version: 1.0.
|
|
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=
|
|
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=
|
|
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=
|
|
21
|
-
lucy_assist/services/crud_service.py,sha256=
|
|
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=
|
|
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.
|
|
42
|
-
django_lucy_assist-1.0.
|
|
43
|
-
django_lucy_assist-1.0.
|
|
44
|
-
django_lucy_assist-1.0.
|
|
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.
|
|
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
|
|
53
|
-
"Comment effectuer
|
|
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
|
|
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
|
-
#
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
208
|
+
"description": "Nom du modèle Django concerné (si identifiable, ex: Client, Reservation)"
|
|
210
209
|
},
|
|
211
210
|
"action_type": {
|
|
212
211
|
"type": "string",
|
|
File without changes
|
|
File without changes
|