django-lucy-assist 1.2.2__tar.gz → 1.2.4__tar.gz

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.
Files changed (55) hide show
  1. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/PKG-INFO +1 -1
  2. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/django_lucy_assist.egg-info/PKG-INFO +1 -1
  3. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/django_lucy_assist.egg-info/SOURCES.txt +0 -3
  4. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/__init__.py +1 -1
  5. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/admin.py +13 -13
  6. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/conf.py +4 -0
  7. django_lucy_assist-1.2.4/lucy_assist/constantes.py +166 -0
  8. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/migrations/0001_initial.py +3 -1
  9. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/models/configuration.py +5 -13
  10. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/context_service.py +234 -0
  11. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/crud_service.py +97 -13
  12. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/gitlab_service.py +79 -6
  13. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/mistral_service.py +31 -11
  14. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/tool_executor_service.py +91 -0
  15. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/tools_definition.py +65 -0
  16. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/view_discovery_service.py +47 -13
  17. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/static/lucy_assist/css/lucy-assist.css +48 -1
  18. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/static/lucy_assist/js/lucy-assist.js +11 -0
  19. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/templates/lucy_assist/chatbot_sidebar.html +11 -1
  20. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/pyproject.toml +1 -1
  21. django_lucy_assist-1.2.2/lucy_assist/constantes.py +0 -116
  22. django_lucy_assist-1.2.2/lucy_assist/migrations/0002_configurationlucyassist_prompt_complementaire.py +0 -18
  23. django_lucy_assist-1.2.2/lucy_assist/migrations/0003_configurationlucyassist_crud_views_mapping.py +0 -18
  24. django_lucy_assist-1.2.2/lucy_assist/migrations/0004_configurationlucyassist_system_prompt.py +0 -22
  25. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/MANIFEST.in +0 -0
  26. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/README.md +0 -0
  27. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/django_lucy_assist.egg-info/dependency_links.txt +0 -0
  28. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/django_lucy_assist.egg-info/requires.txt +0 -0
  29. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/django_lucy_assist.egg-info/top_level.txt +0 -0
  30. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/apps.py +0 -0
  31. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/context_processors.py +0 -0
  32. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/migrations/__init__.py +0 -0
  33. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/models/__init__.py +0 -0
  34. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/models/base.py +0 -0
  35. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/models/conversation.py +0 -0
  36. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/models/message.py +0 -0
  37. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/models/project_context_cache.py +0 -0
  38. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/__init__.py +0 -0
  39. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/bug_notification_service.py +0 -0
  40. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/services/project_context_service.py +0 -0
  41. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/signals.py +0 -0
  42. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/static/lucy_assist/image/icon-lucy.png +0 -0
  43. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/templates/lucy_assist/partials/documentation_content.html +0 -0
  44. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/tests/__init__.py +0 -0
  45. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/tests/factories/__init__.py +0 -0
  46. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/tests/factories/lucy_assist_factories.py +0 -0
  47. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/tests/test_lucy_assist.py +0 -0
  48. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/urls.py +0 -0
  49. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/utils/__init__.py +0 -0
  50. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/utils/log_utils.py +0 -0
  51. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/utils/message_utils.py +0 -0
  52. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/utils/token_utils.py +0 -0
  53. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/views/__init__.py +0 -0
  54. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/lucy_assist/views/api_views.py +0 -0
  55. {django_lucy_assist-1.2.2 → django_lucy_assist-1.2.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-lucy-assist
3
- Version: 1.2.2
3
+ Version: 1.2.4
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-lucy-assist
3
- Version: 1.2.2
3
+ Version: 1.2.4
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>
@@ -15,9 +15,6 @@ lucy_assist/context_processors.py
15
15
  lucy_assist/signals.py
16
16
  lucy_assist/urls.py
17
17
  lucy_assist/migrations/0001_initial.py
18
- lucy_assist/migrations/0002_configurationlucyassist_prompt_complementaire.py
19
- lucy_assist/migrations/0003_configurationlucyassist_crud_views_mapping.py
20
- lucy_assist/migrations/0004_configurationlucyassist_system_prompt.py
21
18
  lucy_assist/migrations/__init__.py
22
19
  lucy_assist/models/__init__.py
23
20
  lucy_assist/models/base.py
@@ -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.2'
8
+ __version__ = '1.2.4'
9
9
  __author__ = 'Revolucy'
10
10
 
11
11
  default_app_config = 'lucy_assist.apps.LucyAssistConfig'
@@ -28,10 +28,10 @@ class ConfigurationLucyAssistAdmin(admin.ModelAdmin):
28
28
  ('Personnalisation', {
29
29
  'fields': ('avatar', 'questions_frequentes')
30
30
  }),
31
- ('Prompt Système', {
32
- 'fields': ('system_prompt', 'prompt_complementaire'),
31
+ ('Prompt Systeme', {
32
+ 'fields': ('system_prompt',),
33
33
  'classes': ('collapse',),
34
- 'description': 'Le prompt système est initialisé automatiquement si vide. Le prompt complémentaire est ajouté à la fin.'
34
+ 'description': 'Le prompt systeme est initialise automatiquement si vide.'
35
35
  }),
36
36
  ('Mapping Automatique (lecture seule)', {
37
37
  'fields': ('crud_views_mapping_display', 'model_app_mapping_display'),
@@ -41,36 +41,36 @@ class ConfigurationLucyAssistAdmin(admin.ModelAdmin):
41
41
  actions = ['refresh_crud_views']
42
42
 
43
43
  def nb_vues_crud(self, obj):
44
- """Affiche le nombre de modèles avec des vues CRUD découvertes."""
44
+ """Affiche le nombre de modeles avec des vues CRUD decouvertes."""
45
45
  if obj.crud_views_mapping:
46
46
  return len(obj.crud_views_mapping)
47
47
  return 0
48
- nb_vues_crud.short_description = "Modèles CRUD"
48
+ nb_vues_crud.short_description = "Modeles CRUD"
49
49
 
50
50
  def crud_views_mapping_display(self, obj):
51
- """Affiche le mapping des vues CRUD de manière lisible."""
51
+ """Affiche le mapping des vues CRUD de maniere lisible."""
52
52
  import json
53
53
  if obj.crud_views_mapping:
54
54
  return json.dumps(obj.crud_views_mapping, indent=2, ensure_ascii=False)
55
- return "Aucune vue découverte. Utilisez l'action 'Rafraîchir les vues CRUD'."
56
- crud_views_mapping_display.short_description = "Vues CRUD découvertes"
55
+ return "Aucune vue decouverte. Utilisez l'action 'Rafraichir les vues CRUD'."
56
+ crud_views_mapping_display.short_description = "Vues CRUD decouvertes"
57
57
 
58
58
  def model_app_mapping_display(self, obj):
59
- """Affiche le mapping modèle -> app de manière lisible."""
59
+ """Affiche le mapping modele -> app de maniere lisible."""
60
60
  import json
61
61
  if obj.model_app_mapping:
62
62
  return json.dumps(obj.model_app_mapping, indent=2, ensure_ascii=False)
63
63
  return "Aucun mapping. Sera construit automatiquement."
64
- model_app_mapping_display.short_description = "Mapping Modèle -> App"
64
+ model_app_mapping_display.short_description = "Mapping Modele -> App"
65
65
 
66
- @admin.action(description="Rafraîchir les vues CRUD découvertes")
66
+ @admin.action(description="Rafraichir les vues CRUD decouvertes")
67
67
  def refresh_crud_views(self, request, queryset):
68
- """Action admin pour rafraîchir le mapping des vues CRUD."""
68
+ """Action admin pour rafraichir le mapping des vues CRUD."""
69
69
  for config in queryset:
70
70
  mapping = config.refresh_crud_views_mapping()
71
71
  nb_models = len(mapping)
72
72
  nb_views = sum(len(actions) for actions in mapping.values())
73
73
  self.message_user(
74
74
  request,
75
- f"Mapping rafraîchi: {nb_models} modèles, {nb_views} vues découvertes."
75
+ f"Mapping rafraichi: {nb_models} modeles, {nb_views} vues decouvertes."
76
76
  )
@@ -67,6 +67,10 @@ class LucyAssistSettings:
67
67
  # Attributs de l'utilisateur a copier vers la requete
68
68
  # Ex: ['franchise', 'tenant', 'organization']
69
69
  'REQUEST_USER_ATTRS': [],
70
+
71
+ # Forcer l'utilisation des vues CRUD (pas de fallback ORM direct)
72
+ # Si True, Lucy refusera de creer/modifier/supprimer si aucune vue n'est trouvee
73
+ 'CRUD_VIEWS_ONLY': True,
70
74
  }
71
75
 
72
76
  def __init__(self):
@@ -0,0 +1,166 @@
1
+ """
2
+ Constantes pour Lucy Assist
3
+ """
4
+
5
+
6
+ class LucyAssistConstantes:
7
+ """Constantes générales de Lucy Assist"""
8
+
9
+ # Prix par million de tokens (en euros)
10
+ PRIX_PAR_MILLION_TOKENS = 50.0
11
+
12
+ # Nombre moyen de tokens par conversation
13
+ TOKENS_MOYENS_PAR_CONVERSATION = 2000
14
+
15
+ # Nombre de conversations estimées pour 1 million de tokens
16
+ CONVERSATIONS_PAR_MILLION = 500 # 1_000_000 / 2000
17
+
18
+ class Repondant:
19
+ """Types de répondants dans une conversation"""
20
+ UTILISATEUR = 'UTILISATEUR'
21
+ CHATBOT = 'CHATBOT'
22
+
23
+ tuples = [
24
+ (UTILISATEUR, 'Utilisateur'),
25
+ (CHATBOT, 'Lucy Assist'),
26
+ ]
27
+
28
+ class TypeAction:
29
+ """Types d'actions que Lucy Assist peut effectuer"""
30
+ AIDE_NAVIGATION = 'AIDE_NAVIGATION'
31
+ RECHERCHE = 'RECHERCHE'
32
+ CRUD = 'CRUD'
33
+ ANALYSE_BUG = 'ANALYSE_BUG'
34
+ EXPLICATION = 'EXPLICATION'
35
+
36
+ tuples = [
37
+ (AIDE_NAVIGATION, 'Aide à la navigation'),
38
+ (RECHERCHE, 'Recherche d\'objets'),
39
+ (CRUD, 'Création/Modification/Suppresion d\'objets'),
40
+ (ANALYSE_BUG, 'Analyse de bug'),
41
+ (EXPLICATION, 'Explication de fonctionnalité'),
42
+ ]
43
+
44
+ class StatutConversation:
45
+ """Statuts possibles d'une conversation"""
46
+ ACTIVE = 'ACTIVE'
47
+ ARCHIVEE = 'ARCHIVEE'
48
+
49
+ tuples = [
50
+ (ACTIVE, 'Active'),
51
+ (ARCHIVEE, 'Archivée'),
52
+ ]
53
+
54
+ # Prompts systeme pour Mistral
55
+ SYSTEM_PROMPTS = {
56
+ 'default': """Tu es Lucy, une assistante IA sympathique et efficace integree dans {application_name}.
57
+ Tu aides les utilisateurs a naviguer et effectuer des actions dans l'application.
58
+
59
+ ## TES CAPACITES
60
+
61
+ 1. **Aide contextuelle** : Tu vois la page ou se trouve l'utilisateur et tu proposes une aide adaptee
62
+ 2. **Recherche intelligente** : Tu peux chercher n'importe quel objet de base de données, suivant les permissions de l'utilisateur connectés
63
+ 3. **Creation assistee** : Tu peux creer des elements directement via la conversation, suivant les CRUD disponibles et les permissions de l'utilisateur connectés
64
+ 4. **Analyse de bugs** : Tu peux analyser les problemes signales par l'utilisateur en inspectant le projet GIT
65
+
66
+ ## COMPORTEMENT - ACTION FIRST
67
+
68
+ Quand l'utilisateur demande une action (creer, modifier, chercher, supprimer) :
69
+ -> EXECUTE L'ACTION IMMEDIATEMENT avec les tools disponibles, UNIQUEMENT si les vues de CRUD sont disponibles dans le projet
70
+ -> Passe par les vues CRUD pour qu'on puisse avoir les logs, et que si il y a des services connecté aux vues, ceux soit appelés.
71
+ -> Ne te contente pas d'expliquer comment faire
72
+
73
+ Quand l'utilisateur pose une question ou demande de l'aide :
74
+ -> Reponds de maniere claire et concise
75
+ -> Propose des actions concretes avec des liens
76
+
77
+ ## LIMITES STRICTES
78
+
79
+ Tu es EXCLUSIVEMENT dediee a l'aide sur {application_name}.
80
+
81
+ ### Regle absolue sur les modeles
82
+ Tu ne peux travailler QU'AVEC les modeles listes dans "MODELES DISPONIBLES" ci-dessous.
83
+ - Si un modele n'est PAS dans la liste -> il N'EXISTE PAS dans cette application
84
+ - Ne JAMAIS inventer, supposer ou suggerer des modeles/fonctionnalites absents de la liste
85
+ - Ne JAMAIS parler de "logements", "produits", "commandes" etc. si ces modeles ne sont pas listes
86
+
87
+ Avant toute action CRUD, VERIFIE que le modele demande est dans la liste.
88
+ Si le modele n'existe pas, reponds :
89
+ "Cette fonctionnalite n'est pas disponible dans {application_name}. Voici ce que je peux faire : [lister les modeles disponibles]"
90
+
91
+ ### Demandes hors sujet
92
+ Tu REFUSES POLIMENT :
93
+ - Questions sans rapport avec l'application
94
+ - Demandes de redaction, traduction, culture generale, recettes, etc.
95
+
96
+ Reponse type :
97
+ "Je suis Lucy, l'assistante de {application_name}. Je ne peux repondre qu'aux questions concernant l'application."
98
+
99
+ IMPORTANT : Ne donne JAMAIS la reponse meme partiellement. Refuse et redirige vers l'application.
100
+
101
+ ## COMMUNICATION
102
+
103
+ Tu parles a un UTILISATEUR FINAL (pas un developpeur) :
104
+ - Langage simple et accessible
105
+ - Reponses courtes et directes
106
+ - Liens cliquables vers les pages (utilise navigate_to_page)
107
+ - Ne JAMAIS mentionner : fichiers, code, GitLab, classes Python, chemins techniques
108
+
109
+ ## AIDE CONTEXTUELLE
110
+
111
+ Utilise le contexte de la page pour personnaliser ton aide :
112
+ - Sur une liste : propose de filtrer, chercher, ou creer un nouvel element
113
+ - Sur un formulaire : aide a remplir les champs ou explique leur utilite
114
+ - Sur un detail : propose de modifier, supprimer, ou voir les elements lies
115
+
116
+ ## EXEMPLES DE BONNES REPONSES
117
+
118
+ Recherche :
119
+ "J'ai trouve 3 clients correspondant a 'Dupont' : [Jean Dupont](/client/42/), [Marie Dupont](/client/87/), [Dupont SARL](/client/156/)"
120
+
121
+ Creation :
122
+ "J'ai cree le membre Jean Martin (ID: 234). [Voir sa fiche](/membre/234/)"
123
+
124
+ Aide :
125
+ "Sur cette page, vous pouvez :
126
+ - Modifier les informations en cliquant sur les champs
127
+ - [Voir l'historique](/client/42/historique/)
128
+ - [Supprimer ce client](/client/42/delete/)"
129
+
130
+ ## PROCEDURE DE SUPPRESSION
131
+
132
+ OBLIGATOIRE avant toute suppression :
133
+ 1. Utilise `get_deletion_impact` pour analyser les consequences
134
+ 2. Montre clairement ce qui sera supprime (objets en cascade, etc.)
135
+ 3. Demande confirmation explicite
136
+ 4. Supprime SEULEMENT apres confirmation avec `confirmed: true`
137
+
138
+ ## CONTEXTE ACTUEL
139
+
140
+ Page : {page_context}
141
+ Permissions : {user_permissions}
142
+
143
+ ## MODELES DISPONIBLES (LISTE EXHAUSTIVE)
144
+ Voici les SEULS modeles existants dans cette application. Tout autre modele N'EXISTE PAS.
145
+ {available_models}
146
+
147
+ Si "Aucun modele" est affiche, tu ne peux effectuer AUCUNE operation CRUD.
148
+ """,
149
+ 'crud': """Tu dois aider l'utilisateur à créer ou modifier un objet.
150
+ Formulaire disponible : {form_info}
151
+ Champs requis : {required_fields}
152
+ """,
153
+ 'bug_analysis': """Analyse le problème signalé par l'utilisateur.
154
+ Code source pertinent : {code_context}
155
+ Erreur rapportée : {error_message}
156
+ """,
157
+ }
158
+
159
+ # Questions fréquentes par défaut (utilisées si non configurées dans le modèle)
160
+ QUESTIONS_FREQUENTES_DEFAULT = [
161
+ "Comment créer un nouvel enregistrement ?",
162
+ "Comment effectuer une recherche ?",
163
+ "Comment exporter des données ?",
164
+ "Comment modifier mon profil ?",
165
+ "Quelles sont les fonctionnalités disponibles ?",
166
+ ]
@@ -1,4 +1,4 @@
1
- # Generated by Django 6.0.1 on 2026-01-26 17:03
1
+ # Generated by Django 6.0.2 on 2026-02-04 14:09
2
2
 
3
3
  import django.db.models.deletion
4
4
  from django.conf import settings
@@ -26,7 +26,9 @@ class Migration(migrations.Migration):
26
26
  ('questions_frequentes', models.JSONField(blank=True, default=list)),
27
27
  ('actif', models.BooleanField(default=True)),
28
28
  ('avatar', models.ImageField(blank=True, null=True, upload_to='')),
29
+ ('system_prompt', models.TextField(blank=True, default='')),
29
30
  ('model_app_mapping', models.JSONField(blank=True, default=dict)),
31
+ ('crud_views_mapping', models.JSONField(blank=True, default=dict)),
30
32
  ],
31
33
  options={
32
34
  'verbose_name': 'Configuration Lucy Assist',
@@ -21,16 +21,12 @@ class ConfigurationLucyAssist(LucyAssistBaseModel):
21
21
  avatar = models.ImageField(null=True, blank=True)
22
22
 
23
23
  # Prompt système principal pour l'assistant
24
- # Initialisé automatiquement via signal si vide
24
+ # Initialisé automatiquement si vide
25
25
  system_prompt = models.TextField(
26
26
  blank=True,
27
27
  default='',
28
- help_text="Prompt système pour l'assistant IA. Initialisé automatiquement si vide."
29
28
  )
30
29
 
31
- # Instructions complémentaires pour le prompt (ajoutées après le system_prompt)
32
- prompt_complementaire = models.TextField(blank=True, default='')
33
-
34
30
  # Mapping des modèles vers leurs applications Django
35
31
  model_app_mapping = models.JSONField(blank=True, default=dict)
36
32
 
@@ -245,15 +241,15 @@ class ConfigurationLucyAssist(LucyAssistBaseModel):
245
241
  available_models: str = ""
246
242
  ) -> str:
247
243
  """
248
- Retourne le prompt système formaté avec les variables de contexte.
244
+ Retourne le prompt systeme formate avec les variables de contexte.
249
245
 
250
246
  Args:
251
247
  page_context: Contexte JSON de la page actuelle
252
248
  user_permissions: Liste des permissions de l'utilisateur
253
- available_models: Liste des modèles disponibles
249
+ available_models: Liste des modeles disponibles
254
250
 
255
251
  Returns:
256
- Prompt système complet avec le prompt_complementaire ajouté
252
+ Prompt systeme complet avec le project_context
257
253
  """
258
254
  prompt = self.system_prompt
259
255
 
@@ -265,10 +261,6 @@ class ConfigurationLucyAssist(LucyAssistBaseModel):
265
261
  available_models=available_models
266
262
  )
267
263
 
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
264
  return prompt
273
265
 
274
266
  def _get_application_name(self) -> str:
@@ -319,7 +311,7 @@ class ConfigurationLucyAssist(LucyAssistBaseModel):
319
311
  fields_str += ", ..."
320
312
  descriptions.append(f"- {model_name} ({model_name_lower}) : {fields_str}")
321
313
 
322
- return "\n".join(descriptions) if descriptions else "Aucun modèle configuré"
314
+ return "\n".join(descriptions) if descriptions else "Aucun modele configure"
323
315
 
324
316
 
325
317
  def get_default_model_app_mapping() -> dict:
@@ -452,3 +452,237 @@ class ContextService:
452
452
 
453
453
  except Exception:
454
454
  return None
455
+
456
+ def get_page_help(self, url_path: str) -> Dict:
457
+ """
458
+ Retourne une aide contextuelle enrichie pour la page actuelle.
459
+
460
+ Args:
461
+ url_path: URL de la page
462
+
463
+ Returns:
464
+ Dict avec aide, actions suggerees, liens utiles
465
+ """
466
+ context = self.get_page_context(url_path)
467
+ action = context.get('action')
468
+ model_name = context.get('model_name', 'element')
469
+ app_name = context.get('app_name', '')
470
+
471
+ help_info = {
472
+ 'page_type': action,
473
+ 'current_model': model_name,
474
+ 'help_message': '',
475
+ 'suggested_actions': [],
476
+ 'useful_links': [],
477
+ 'tips': []
478
+ }
479
+
480
+ # Generer les URLs utiles
481
+ from django.urls import reverse
482
+ model_lower = model_name.lower() if model_name else ''
483
+ app_simple = app_name.split(':')[-1] if app_name else ''
484
+
485
+ def try_reverse(pattern, **kwargs):
486
+ try:
487
+ return reverse(pattern, kwargs=kwargs) if kwargs else reverse(pattern)
488
+ except Exception:
489
+ return None
490
+
491
+ # Aide selon le type de page
492
+ if action == 'list':
493
+ help_info['help_message'] = f"Vous consultez la liste des {model_name}s."
494
+ help_info['suggested_actions'] = [
495
+ "Rechercher un element specifique",
496
+ "Filtrer la liste",
497
+ "Creer un nouvel element"
498
+ ]
499
+ help_info['tips'] = [
500
+ "Utilisez la barre de recherche pour trouver rapidement",
501
+ "Cliquez sur une ligne pour voir les details"
502
+ ]
503
+ # Lien creation
504
+ create_url = try_reverse(f'{app_simple}:{model_lower}-form')
505
+ if create_url:
506
+ help_info['useful_links'].append({
507
+ 'label': f'Creer un nouveau {model_name}',
508
+ 'url': create_url
509
+ })
510
+
511
+ elif action == 'create_or_edit':
512
+ if context.get('object_id'):
513
+ help_info['help_message'] = f"Vous modifiez un {model_name} existant."
514
+ help_info['suggested_actions'] = [
515
+ "Modifier les informations",
516
+ "Enregistrer les modifications",
517
+ "Annuler et revenir a la liste"
518
+ ]
519
+ else:
520
+ help_info['help_message'] = f"Vous creez un nouveau {model_name}."
521
+ help_info['suggested_actions'] = [
522
+ "Remplir les champs obligatoires",
523
+ "Enregistrer le nouvel element"
524
+ ]
525
+ help_info['tips'] = [
526
+ "Les champs marques * sont obligatoires",
527
+ "Verifiez les informations avant d'enregistrer"
528
+ ]
529
+ # Lien liste
530
+ list_url = try_reverse(f'{app_simple}:{model_lower}-list')
531
+ if list_url:
532
+ help_info['useful_links'].append({
533
+ 'label': f'Retour a la liste des {model_name}s',
534
+ 'url': list_url
535
+ })
536
+
537
+ elif action == 'detail':
538
+ help_info['help_message'] = f"Vous consultez les details d'un {model_name}."
539
+ help_info['suggested_actions'] = [
540
+ "Modifier cet element",
541
+ "Supprimer cet element",
542
+ "Voir les elements lies"
543
+ ]
544
+ object_id = context.get('object_id')
545
+ if object_id:
546
+ edit_url = try_reverse(f'{app_simple}:{model_lower}-form', pk=object_id)
547
+ if edit_url:
548
+ help_info['useful_links'].append({
549
+ 'label': 'Modifier',
550
+ 'url': edit_url
551
+ })
552
+ list_url = try_reverse(f'{app_simple}:{model_lower}-list')
553
+ if list_url:
554
+ help_info['useful_links'].append({
555
+ 'label': f'Liste des {model_name}s',
556
+ 'url': list_url
557
+ })
558
+
559
+ elif action == 'delete':
560
+ help_info['help_message'] = f"Attention : vous allez supprimer ce {model_name}."
561
+ help_info['suggested_actions'] = [
562
+ "Confirmer la suppression",
563
+ "Annuler et revenir"
564
+ ]
565
+ help_info['tips'] = [
566
+ "Cette action est irreversible",
567
+ "Verifiez les elements lies qui seront aussi supprimes"
568
+ ]
569
+
570
+ else:
571
+ help_info['help_message'] = "Comment puis-je vous aider sur cette page ?"
572
+ help_info['suggested_actions'] = [
573
+ "Poser une question",
574
+ "Rechercher un element",
575
+ "Signaler un probleme"
576
+ ]
577
+
578
+ return help_info
579
+
580
+ def get_relevant_models(self, url_path: str) -> List[Dict]:
581
+ """
582
+ Retourne uniquement les modeles pertinents pour la page actuelle.
583
+ Optimise l'utilisation des tokens.
584
+
585
+ Args:
586
+ url_path: URL de la page
587
+
588
+ Returns:
589
+ Liste des modeles pertinents avec leurs champs
590
+ """
591
+ context = self.get_page_context(url_path)
592
+ app_name = context.get('app_name', '')
593
+ model_name = context.get('model_name')
594
+
595
+ relevant_models = []
596
+ apps_prefix = lucy_assist_settings.PROJECT_APPS_PREFIX
597
+ app_simple = app_name.split(':')[-1] if app_name else ''
598
+
599
+ # 1. Toujours inclure le modele de la page actuelle en premier
600
+ if model_name:
601
+ model_info = self._get_model_info(app_simple, model_name)
602
+ if model_info:
603
+ model_info['is_current'] = True
604
+ relevant_models.append(model_info)
605
+
606
+ # 2. Ajouter les modeles de la meme app (limiter a 5)
607
+ if app_simple:
608
+ try:
609
+ app_config = apps.get_app_config(app_simple)
610
+ for model in list(app_config.get_models())[:5]:
611
+ if model.__name__ != model_name:
612
+ model_info = self._get_model_info(app_simple, model.__name__)
613
+ if model_info:
614
+ model_info['is_current'] = False
615
+ relevant_models.append(model_info)
616
+ except LookupError:
617
+ pass
618
+
619
+ # 3. Si moins de 3 modeles, ajouter les modeles principaux du projet
620
+ if len(relevant_models) < 3:
621
+ for app_config in apps.get_app_configs():
622
+ if apps_prefix and not app_config.name.startswith(apps_prefix):
623
+ continue
624
+ if app_config.name.startswith('django.') or app_config.name == 'lucy_assist':
625
+ continue
626
+ if app_config.label == app_simple:
627
+ continue
628
+
629
+ for model in list(app_config.get_models())[:2]:
630
+ if len(relevant_models) >= 8:
631
+ break
632
+ model_info = self._get_model_info(app_config.label, model.__name__)
633
+ if model_info:
634
+ model_info['is_current'] = False
635
+ relevant_models.append(model_info)
636
+
637
+ if len(relevant_models) >= 8:
638
+ break
639
+
640
+ return relevant_models
641
+
642
+ def _get_model_info(self, app_label: str, model_name: str) -> Optional[Dict]:
643
+ """Retourne les informations d'un modele."""
644
+ try:
645
+ model = apps.get_model(app_label, model_name)
646
+ fields = []
647
+ for field in model._meta.get_fields():
648
+ if hasattr(field, 'name') and not field.name.startswith('_'):
649
+ if hasattr(field, 'auto_created') and field.auto_created:
650
+ continue
651
+ if field.name in ('id', 'pk', 'created_at', 'updated_at'):
652
+ continue
653
+ fields.append(field.name)
654
+
655
+ return {
656
+ 'app': app_label,
657
+ 'name': model_name,
658
+ 'name_lower': model_name.lower(),
659
+ 'fields': fields[:8],
660
+ 'verbose_name': str(model._meta.verbose_name) if hasattr(model._meta, 'verbose_name') else model_name
661
+ }
662
+ except LookupError:
663
+ return None
664
+
665
+ def get_all_model_names(self) -> List[str]:
666
+ """
667
+ Retourne la liste de TOUS les noms de modeles du projet.
668
+ Utilise pour informer Lucy de ce qui existe vs ce qui n'existe pas.
669
+
670
+ Returns:
671
+ Liste des noms de modeles (ex: ['Client', 'Reservation', 'Facture'])
672
+ """
673
+ model_names = []
674
+ apps_prefix = lucy_assist_settings.PROJECT_APPS_PREFIX
675
+
676
+ for app_config in apps.get_app_configs():
677
+ # Filtrer les apps si un préfixe est configuré
678
+ if apps_prefix and not app_config.name.startswith(apps_prefix):
679
+ continue
680
+
681
+ # Ignorer les apps Django internes et lucy_assist
682
+ if app_config.name.startswith('django.') or app_config.name == 'lucy_assist':
683
+ continue
684
+
685
+ for model in app_config.get_models():
686
+ model_names.append(model.__name__)
687
+
688
+ return sorted(set(model_names))