django-lucy-assist 0.1.0__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-0.1.0.dist-info/METADATA +206 -0
- django_lucy_assist-0.1.0.dist-info/RECORD +44 -0
- django_lucy_assist-0.1.0.dist-info/WHEEL +5 -0
- django_lucy_assist-0.1.0.dist-info/top_level.txt +1 -0
- lucy_assist/__init__.py +11 -0
- lucy_assist/admin.py +22 -0
- lucy_assist/apps.py +10 -0
- lucy_assist/conf.py +103 -0
- lucy_assist/constantes.py +120 -0
- lucy_assist/context_processors.py +65 -0
- lucy_assist/migrations/0001_initial.py +92 -0
- lucy_assist/migrations/__init__.py +0 -0
- lucy_assist/models/__init__.py +14 -0
- lucy_assist/models/base.py +54 -0
- lucy_assist/models/configuration.py +175 -0
- lucy_assist/models/conversation.py +54 -0
- lucy_assist/models/message.py +45 -0
- lucy_assist/models/project_context_cache.py +213 -0
- lucy_assist/services/__init__.py +21 -0
- lucy_assist/services/bug_notification_service.py +183 -0
- lucy_assist/services/claude_service.py +417 -0
- lucy_assist/services/context_service.py +350 -0
- lucy_assist/services/crud_service.py +364 -0
- lucy_assist/services/gitlab_service.py +248 -0
- lucy_assist/services/project_context_service.py +412 -0
- lucy_assist/services/tool_executor_service.py +343 -0
- lucy_assist/services/tools_definition.py +229 -0
- lucy_assist/signals.py +25 -0
- lucy_assist/static/lucy_assist/css/lucy-assist.css +160 -0
- lucy_assist/static/lucy_assist/image/icon-lucy.png +0 -0
- lucy_assist/static/lucy_assist/js/lucy-assist.js +824 -0
- lucy_assist/templates/lucy_assist/chatbot_sidebar.html +419 -0
- lucy_assist/templates/lucy_assist/partials/documentation_content.html +107 -0
- lucy_assist/tests/__init__.py +0 -0
- lucy_assist/tests/factories/__init__.py +15 -0
- lucy_assist/tests/factories/lucy_assist_factories.py +109 -0
- lucy_assist/tests/test_lucy_assist.py +186 -0
- lucy_assist/urls.py +36 -0
- lucy_assist/utils/__init__.py +7 -0
- lucy_assist/utils/log_utils.py +59 -0
- lucy_assist/utils/message_utils.py +130 -0
- lucy_assist/utils/token_utils.py +87 -0
- lucy_assist/views/__init__.py +13 -0
- lucy_assist/views/api_views.py +595 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
|
|
3
|
+
{# Composant Lucy Assist - Sidebar Chatbot #}
|
|
4
|
+
{# Ce template est autonome et inclut ses propres ressources CSS/JS #}
|
|
5
|
+
|
|
6
|
+
{% if lucy_assist_enabled %}
|
|
7
|
+
{# CSS Lucy Assist #}
|
|
8
|
+
<link href="{% static 'lucy_assist/css/lucy-assist.css' %}" rel="stylesheet">
|
|
9
|
+
|
|
10
|
+
{# Script Lucy Assist - doit être chargé avant d'utiliser le composant Alpine #}
|
|
11
|
+
<script src="{% static 'lucy_assist/js/lucy-assist.js' %}"></script>
|
|
12
|
+
|
|
13
|
+
<div x-data="lucyAssist()" x-init="init()" @keydown.window="handleKeydown($event)">
|
|
14
|
+
{# Bouton flottant - masqué quand le chat est ouvert #}
|
|
15
|
+
<button
|
|
16
|
+
x-show="!isOpen"
|
|
17
|
+
@click="toggleSidebar()"
|
|
18
|
+
class="fixed right-4 bottom-4 z-50 btn btn-circle btn-lg btn-primary shadow-lg hover:scale-110 transition-transform"
|
|
19
|
+
:class="{ 'animate-pulse': hasNewMessage }"
|
|
20
|
+
title="Ouvrir Lucy (Ctrl+K)"
|
|
21
|
+
>
|
|
22
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
23
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" />
|
|
24
|
+
</svg>
|
|
25
|
+
</button>
|
|
26
|
+
|
|
27
|
+
{# Sidebar #}
|
|
28
|
+
<div
|
|
29
|
+
x-show="isOpen"
|
|
30
|
+
x-transition:enter="transition ease-out duration-300"
|
|
31
|
+
x-transition:enter-start="translate-x-full"
|
|
32
|
+
x-transition:enter-end="translate-x-0"
|
|
33
|
+
x-transition:leave="transition ease-in duration-200"
|
|
34
|
+
x-transition:leave-start="translate-x-0"
|
|
35
|
+
x-transition:leave-end="translate-x-full"
|
|
36
|
+
class="fixed right-0 top-0 h-full w-96 bg-base-100 shadow-2xl z-999 flex flex-col border-l border-base-300"
|
|
37
|
+
>
|
|
38
|
+
{# Header #}
|
|
39
|
+
<div class="navbar bg-primary text-primary-content px-4">
|
|
40
|
+
<div class="flex-1">
|
|
41
|
+
<span class="text-lg font-bold">
|
|
42
|
+
Lucy Assist
|
|
43
|
+
</span>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="flex-none gap-1">
|
|
46
|
+
{# Bouton Historique #}
|
|
47
|
+
<button
|
|
48
|
+
@click="showHistory = !showHistory; showDoc = false"
|
|
49
|
+
class="btn btn-ghost btn-sm btn-circle"
|
|
50
|
+
:class="{ 'bg-primary-focus': showHistory }"
|
|
51
|
+
title="Historique des conversations"
|
|
52
|
+
>
|
|
53
|
+
<i class="fa-solid fa-clock-rotate-left"></i>
|
|
54
|
+
</button>
|
|
55
|
+
|
|
56
|
+
{# Bouton Nouvelle conversation #}
|
|
57
|
+
<button
|
|
58
|
+
@click="newConversation()"
|
|
59
|
+
class="btn btn-ghost btn-sm btn-circle"
|
|
60
|
+
title="Nouvelle conversation"
|
|
61
|
+
>
|
|
62
|
+
<i class="fa-solid fa-plus"></i>
|
|
63
|
+
</button>
|
|
64
|
+
|
|
65
|
+
{# Bouton Documentation #}
|
|
66
|
+
<button
|
|
67
|
+
@click="showDoc = !showDoc; showHistory = false"
|
|
68
|
+
class="btn btn-ghost btn-sm btn-circle"
|
|
69
|
+
:class="{ 'bg-primary-focus': showDoc }"
|
|
70
|
+
title="Documentation"
|
|
71
|
+
>
|
|
72
|
+
<i class="fa-solid fa-book"></i>
|
|
73
|
+
</button>
|
|
74
|
+
|
|
75
|
+
{# Bouton Fermer #}
|
|
76
|
+
<button
|
|
77
|
+
@click="closeSidebar()"
|
|
78
|
+
class="btn btn-ghost btn-sm btn-circle"
|
|
79
|
+
title="Fermer (Echap)"
|
|
80
|
+
>
|
|
81
|
+
<i class="fa-solid fa-xmark"></i>
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
{# Indicateur de crédits #}
|
|
87
|
+
<div class="px-4 py-2 bg-base-200 text-sm flex items-center justify-between">
|
|
88
|
+
<span class="text-base-content/70">
|
|
89
|
+
<i class="fa-solid fa-coins mr-1"></i>
|
|
90
|
+
<span x-text="formatTokens(tokensDisponibles)"></span> crédits
|
|
91
|
+
</span>
|
|
92
|
+
<button
|
|
93
|
+
x-show="tokensDisponibles < 100000"
|
|
94
|
+
@click="showBuyCredits = true"
|
|
95
|
+
class="btn btn-xs btn-warning"
|
|
96
|
+
>
|
|
97
|
+
<i class="fa-solid fa-cart-plus mr-1"></i>
|
|
98
|
+
Acheter
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{# Contenu principal #}
|
|
103
|
+
<div class="flex-1 overflow-hidden flex flex-col">
|
|
104
|
+
{# Vue Historique #}
|
|
105
|
+
<template x-if="showHistory">
|
|
106
|
+
<div class="flex-1 overflow-y-auto p-4">
|
|
107
|
+
<h3 class="font-semibold mb-3">Historique des conversations</h3>
|
|
108
|
+
<template x-if="conversations.length === 0">
|
|
109
|
+
<p class="text-base-content/60 text-sm">Aucune conversation</p>
|
|
110
|
+
</template>
|
|
111
|
+
<div class="space-y-2">
|
|
112
|
+
<template x-for="conv in conversations" :key="conv.id">
|
|
113
|
+
<div
|
|
114
|
+
@click="loadConversation(conv.id)"
|
|
115
|
+
class="p-3 rounded-lg bg-base-200 hover:bg-base-300 cursor-pointer transition-colors"
|
|
116
|
+
:class="{ 'ring-2 ring-primary': currentConversationId === conv.id }"
|
|
117
|
+
>
|
|
118
|
+
<div class="font-medium text-sm truncate" x-text="conv.titre"></div>
|
|
119
|
+
<div class="text-xs text-base-content/60 mt-1">
|
|
120
|
+
<span x-text="formatDate(conv.created_date)"></span>
|
|
121
|
+
<span class="ml-2">
|
|
122
|
+
<i class="fa-solid fa-coins"></i>
|
|
123
|
+
<span x-text="conv.total_tokens"></span> tokens | <span x-text="formatTokenCost(conv.total_tokens)"></span>
|
|
124
|
+
</span>
|
|
125
|
+
</div>
|
|
126
|
+
<button
|
|
127
|
+
@click.stop="deleteConversation(conv.id)"
|
|
128
|
+
class="btn btn-ghost btn-xs text-error mt-1"
|
|
129
|
+
>
|
|
130
|
+
<i class="fa-solid fa-trash"></i>
|
|
131
|
+
</button>
|
|
132
|
+
</div>
|
|
133
|
+
</template>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</template>
|
|
137
|
+
|
|
138
|
+
{# Vue Documentation #}
|
|
139
|
+
<template x-if="showDoc">
|
|
140
|
+
<div class="flex-1 overflow-y-auto p-4">
|
|
141
|
+
{% include "lucy_assist/partials/documentation_content.html" %}
|
|
142
|
+
</div>
|
|
143
|
+
</template>
|
|
144
|
+
|
|
145
|
+
{# Vue Chat #}
|
|
146
|
+
<template x-if="!showHistory && !showDoc">
|
|
147
|
+
<div class="flex-1 flex flex-col overflow-hidden">
|
|
148
|
+
{# Zone de messages #}
|
|
149
|
+
<div
|
|
150
|
+
x-ref="messagesContainer"
|
|
151
|
+
class="flex-1 overflow-y-auto p-4 space-y-4"
|
|
152
|
+
>
|
|
153
|
+
{# Message de bienvenue / Suggestions #}
|
|
154
|
+
<template x-if="messages.length === 0">
|
|
155
|
+
<div class="text-center py-8">
|
|
156
|
+
<div class="text-4xl mb-4">
|
|
157
|
+
<img class="mx-auto w-20" src="{% static 'lucy_assist/image/icon-lucy.png' %}" alt="Lucy">
|
|
158
|
+
</div>
|
|
159
|
+
<h3 class="font-semibold text-lg mb-2">Bonjour ! Je suis Lucy</h3>
|
|
160
|
+
<p class="text-base-content/70 text-sm mb-4">
|
|
161
|
+
Comment puis-je vous aider aujourd'hui ?
|
|
162
|
+
</p>
|
|
163
|
+
|
|
164
|
+
{# Suggestions #}
|
|
165
|
+
<div class="space-y-2">
|
|
166
|
+
<template x-for="suggestion in suggestions" :key="suggestion">
|
|
167
|
+
<button
|
|
168
|
+
@click="sendMessage(suggestion)"
|
|
169
|
+
class="btn btn-sm btn-outline btn-block justify-start text-left"
|
|
170
|
+
>
|
|
171
|
+
<i class="fa-solid fa-lightbulb mr-2 text-warning"></i>
|
|
172
|
+
<span x-text="suggestion" class="truncate"></span>
|
|
173
|
+
</button>
|
|
174
|
+
</template>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
179
|
+
{# Messages #}
|
|
180
|
+
<template x-for="(msg, index) in messages" :key="msg.id || index">
|
|
181
|
+
<div
|
|
182
|
+
class="chat"
|
|
183
|
+
:class="msg.repondant === 'UTILISATEUR' ? 'chat-end' : 'chat-start'"
|
|
184
|
+
>
|
|
185
|
+
<div class="chat-image avatar">
|
|
186
|
+
<div class="w-8 rounded-full flex items-center justify-center overflow-hidden">
|
|
187
|
+
<template x-if="msg.repondant === 'UTILISATEUR'">
|
|
188
|
+
{% if lucy_assist_config.avatar_url %}
|
|
189
|
+
<img src="{{ lucy_assist_config.avatar_url }}" alt="Utilisateur" class="w-full h-full object-cover">
|
|
190
|
+
{% else %}
|
|
191
|
+
<i class="fa-solid fa-user" style="font-size: 0.875rem;"></i>
|
|
192
|
+
{% endif %}
|
|
193
|
+
</template>
|
|
194
|
+
<template x-if="msg.repondant !== 'UTILISATEUR'">
|
|
195
|
+
<img src="{% static 'lucy_assist/image/icon-lucy.png' %}" alt="Lucy" class="w-full h-full object-cover">
|
|
196
|
+
</template>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
<div
|
|
200
|
+
class="chat-bubble"
|
|
201
|
+
:class="msg.repondant === 'UTILISATEUR' ? 'chat-bubble-primary' : 'chat-bubble-neutral'"
|
|
202
|
+
x-html="msg.contenu ? formatMessage(msg.contenu) : '<span class=\'loading loading-dots loading-sm\'></span>'"
|
|
203
|
+
>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="chat-footer opacity-50 text-xs">
|
|
206
|
+
<span x-text="formatTime(msg.created_date)"></span>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</template>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
{# Bouton Feedback - Lucy n'a pas compris #}
|
|
213
|
+
<template x-if="lucyDidNotUnderstand && messages.length > 0">
|
|
214
|
+
<div class="px-4 py-2 bg-info/10 border-t border-info/30">
|
|
215
|
+
<button
|
|
216
|
+
@click="openFeedback()"
|
|
217
|
+
class="btn btn-sm btn-info btn-outline w-full"
|
|
218
|
+
>
|
|
219
|
+
<i class="fa-solid fa-headset mr-2"></i>
|
|
220
|
+
Contacter le chef de projet Revolucy
|
|
221
|
+
</button>
|
|
222
|
+
</div>
|
|
223
|
+
</template>
|
|
224
|
+
|
|
225
|
+
{# Bouton Feedback - Erreur technique #}
|
|
226
|
+
<template x-if="hasError && !lucyDidNotUnderstand && messages.length > 0">
|
|
227
|
+
<div class="px-4 py-2 bg-warning/10 border-t border-warning/30">
|
|
228
|
+
<button
|
|
229
|
+
@click="openFeedback()"
|
|
230
|
+
class="btn btn-sm btn-warning btn-outline w-full"
|
|
231
|
+
>
|
|
232
|
+
<i class="fa-solid fa-comment-medical mr-2"></i>
|
|
233
|
+
Souhaites-tu que j'informe Revolucy ?
|
|
234
|
+
</button>
|
|
235
|
+
</div>
|
|
236
|
+
</template>
|
|
237
|
+
|
|
238
|
+
{# Zone de saisie #}
|
|
239
|
+
<div class="p-4 border-t border-base-300 bg-base-100">
|
|
240
|
+
<div class="flex gap-2 items-end">
|
|
241
|
+
<textarea
|
|
242
|
+
x-model="currentMessage"
|
|
243
|
+
x-ref="messageInput"
|
|
244
|
+
:disabled="isLoading"
|
|
245
|
+
@keydown.ctrl.enter.prevent="sendCurrentMessage()"
|
|
246
|
+
@keydown.meta.enter.prevent="sendCurrentMessage()"
|
|
247
|
+
placeholder="Tapez votre message..."
|
|
248
|
+
class="textarea textarea-bordered flex-1 textarea-sm min-h-[2.5rem] max-h-32 resize-none"
|
|
249
|
+
rows="1"
|
|
250
|
+
@input="$el.style.height = 'auto'; $el.style.height = Math.min($el.scrollHeight, 128) + 'px'"
|
|
251
|
+
></textarea>
|
|
252
|
+
<button
|
|
253
|
+
type="button"
|
|
254
|
+
@click="sendCurrentMessage()"
|
|
255
|
+
:disabled="isLoading || !currentMessage.trim()"
|
|
256
|
+
class="btn btn-primary btn-sm"
|
|
257
|
+
>
|
|
258
|
+
<i class="fa-solid fa-paper-plane"></i>
|
|
259
|
+
</button>
|
|
260
|
+
</div>
|
|
261
|
+
<p class="text-xs text-base-content/50 mt-2 text-center">
|
|
262
|
+
Ctrl+Entrée pour envoyer
|
|
263
|
+
</p>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</template>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
{# Modal Achat de crédits #}
|
|
271
|
+
<div
|
|
272
|
+
x-show="showBuyCredits"
|
|
273
|
+
class="modal modal-open"
|
|
274
|
+
@keydown.escape="showBuyCredits = false"
|
|
275
|
+
>
|
|
276
|
+
<div class="modal-box">
|
|
277
|
+
<h3 class="font-bold text-lg mb-4">
|
|
278
|
+
<i class="fa-solid fa-cart-plus mr-2"></i>
|
|
279
|
+
Acheter des crédits
|
|
280
|
+
</h3>
|
|
281
|
+
|
|
282
|
+
<div class="form-control mb-4">
|
|
283
|
+
<label class="label">
|
|
284
|
+
<span class="label-text">Montant (EUR HT)</span>
|
|
285
|
+
</label>
|
|
286
|
+
<input
|
|
287
|
+
type="number"
|
|
288
|
+
x-model.number="buyAmount"
|
|
289
|
+
min="10"
|
|
290
|
+
step="10"
|
|
291
|
+
class="input input-bordered"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div class="bg-base-200 rounded-lg p-4 mb-4">
|
|
296
|
+
<div class="flex justify-between mb-2">
|
|
297
|
+
<span>Tokens à recevoir</span>
|
|
298
|
+
<span class="font-bold" x-text="formatTokens(calculateTokens(buyAmount))"></span>
|
|
299
|
+
</div>
|
|
300
|
+
<div class="flex justify-between text-sm text-base-content/70">
|
|
301
|
+
<span>Conversations estimées</span>
|
|
302
|
+
<span x-text="Math.floor(calculateTokens(buyAmount) / 2000)"></span>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div class="alert alert-info text-sm mb-4">
|
|
307
|
+
<i class="fa-solid fa-info-circle"></i>
|
|
308
|
+
<span>10 EUR = 1 million de tokens = ~500 conversations</span>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<div class="modal-action">
|
|
312
|
+
<button @click="showBuyCredits = false" class="btn btn-ghost">
|
|
313
|
+
Annuler
|
|
314
|
+
</button>
|
|
315
|
+
<button @click="buyCredits()" class="btn btn-primary" :disabled="buyAmount < 10">
|
|
316
|
+
<i class="fa-solid fa-credit-card mr-2"></i>
|
|
317
|
+
Acheter
|
|
318
|
+
</button>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="modal-backdrop" @click="showBuyCredits = false"></div>
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
{# Modal Feedback #}
|
|
325
|
+
<div
|
|
326
|
+
x-show="showFeedback"
|
|
327
|
+
class="modal modal-open"
|
|
328
|
+
@keydown.escape="closeFeedback()"
|
|
329
|
+
>
|
|
330
|
+
<div class="modal-box">
|
|
331
|
+
<h3 class="font-bold text-lg mb-4">
|
|
332
|
+
<i class="fa-solid fa-comment-medical mr-2 text-warning"></i>
|
|
333
|
+
Signaler un problème
|
|
334
|
+
</h3>
|
|
335
|
+
|
|
336
|
+
<div class="alert alert-info text-sm mb-4">
|
|
337
|
+
<i class="fa-solid fa-info-circle"></i>
|
|
338
|
+
<span>Votre conversation sera envoyée à l'équipe Revolucy pour analyse et amélioration de Lucy.</span>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<div class="form-control mb-4">
|
|
342
|
+
<label class="label">
|
|
343
|
+
<span class="label-text">Description du problème (optionnel)</span>
|
|
344
|
+
</label>
|
|
345
|
+
<textarea
|
|
346
|
+
x-model="feedbackDescription"
|
|
347
|
+
class="textarea textarea-bordered h-24" style="width: 100%;margin-top: 10px;"
|
|
348
|
+
placeholder="Décrivez ce qui n'a pas fonctionné ou ce que vous attendiez..."
|
|
349
|
+
></textarea>
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<div class="modal-action">
|
|
353
|
+
<button @click="closeFeedback()" class="btn btn-ghost" :disabled="feedbackSending">
|
|
354
|
+
Annuler
|
|
355
|
+
</button>
|
|
356
|
+
<button
|
|
357
|
+
@click="sendFeedback()"
|
|
358
|
+
class="btn btn-warning"
|
|
359
|
+
:disabled="feedbackSending"
|
|
360
|
+
>
|
|
361
|
+
<span x-show="!feedbackSending">
|
|
362
|
+
<i class="fa-solid fa-paper-plane mr-2"></i>
|
|
363
|
+
Envoyer le feedback
|
|
364
|
+
</span>
|
|
365
|
+
<span x-show="feedbackSending" class="loading loading-spinner loading-sm"></span>
|
|
366
|
+
</button>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
<div class="modal-backdrop" @click="closeFeedback()"></div>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
{# Guide interactif (premier lancement) #}
|
|
373
|
+
<template x-if="showGuide">
|
|
374
|
+
<div class="fixed inset-0 z-50 bg-black/50">
|
|
375
|
+
<div
|
|
376
|
+
x-show="guideStep === 1"
|
|
377
|
+
class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-base-100 rounded-xl p-6 max-w-md shadow-2xl"
|
|
378
|
+
>
|
|
379
|
+
<h3 class="text-xl font-bold mb-4">
|
|
380
|
+
<i class="fa-solid fa-hand-wave text-warning mr-2"></i>
|
|
381
|
+
Bienvenue sur Lucy Assist !
|
|
382
|
+
</h3>
|
|
383
|
+
<p class="mb-4">
|
|
384
|
+
Je suis votre assistant intelligent. Je peux vous aider à :
|
|
385
|
+
</p>
|
|
386
|
+
<ul class="list-disc list-inside mb-4 space-y-1 text-sm">
|
|
387
|
+
<li>Naviguer dans l'application</li>
|
|
388
|
+
<li>Rechercher des informations</li>
|
|
389
|
+
<li>Créer et modifier des éléments</li>
|
|
390
|
+
<li>Comprendre les fonctionnalités</li>
|
|
391
|
+
</ul>
|
|
392
|
+
<div class="flex justify-end gap-2">
|
|
393
|
+
<button @click="skipGuide()" class="btn btn-ghost btn-sm">
|
|
394
|
+
Passer
|
|
395
|
+
</button>
|
|
396
|
+
<button @click="nextGuideStep()" class="btn btn-primary btn-sm">
|
|
397
|
+
Suivant
|
|
398
|
+
</button>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
|
|
402
|
+
<div
|
|
403
|
+
x-show="guideStep === 2"
|
|
404
|
+
class="absolute bottom-20 right-4 bg-base-100 rounded-xl p-4 max-w-xs shadow-2xl"
|
|
405
|
+
>
|
|
406
|
+
<div class="absolute -bottom-2 right-8 w-4 h-4 bg-base-100 transform rotate-45"></div>
|
|
407
|
+
<p class="text-sm mb-3">
|
|
408
|
+
Cliquez sur ce bouton ou utilisez <kbd class="kbd kbd-sm">Ctrl+K</kbd> pour ouvrir Lucy Assist à tout moment.
|
|
409
|
+
</p>
|
|
410
|
+
<div class="flex justify-end">
|
|
411
|
+
<button @click="finishGuide()" class="btn btn-primary btn-sm">
|
|
412
|
+
Compris !
|
|
413
|
+
</button>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
</template>
|
|
418
|
+
</div>
|
|
419
|
+
{% endif %}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{# Contenu de la documentation Lucy Assist #}
|
|
2
|
+
|
|
3
|
+
<div class="prose prose-sm max-w-none">
|
|
4
|
+
<h3 class="text-lg font-bold mb-4">
|
|
5
|
+
<i class="fa-solid fa-book mr-2 text-primary"></i>
|
|
6
|
+
Guide Lucy
|
|
7
|
+
</h3>
|
|
8
|
+
|
|
9
|
+
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
10
|
+
<input type="checkbox" checked />
|
|
11
|
+
<div class="collapse-title font-medium">
|
|
12
|
+
<i class="fa-solid fa-compass mr-2"></i>
|
|
13
|
+
Aide contextuelle
|
|
14
|
+
</div>
|
|
15
|
+
<div class="collapse-content text-sm">
|
|
16
|
+
<p>Lucy détecte automatiquement la page où vous vous trouvez pour vous proposer une aide ciblée.</p>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
21
|
+
<input type="checkbox" />
|
|
22
|
+
<div class="collapse-title font-medium">
|
|
23
|
+
<i class="fa-solid fa-search mr-2"></i>
|
|
24
|
+
Recherche intelligente
|
|
25
|
+
</div>
|
|
26
|
+
<div class="collapse-content text-sm">
|
|
27
|
+
<p>Trouvez rapidement n'importe quel élément en posant simplement la question.</p>
|
|
28
|
+
<p class="mt-2"><strong>Exemple :</strong> "Trouve-moi le client Dupont"</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
33
|
+
<input type="checkbox" />
|
|
34
|
+
<div class="collapse-title font-medium">
|
|
35
|
+
<i class="fa-solid fa-plus-circle mr-2"></i>
|
|
36
|
+
Création assistée
|
|
37
|
+
</div>
|
|
38
|
+
<div class="collapse-content text-sm">
|
|
39
|
+
<p>Créez de nouveaux éléments en discutant avec Lucy qui vous guide étape par étape.</p>
|
|
40
|
+
<p class="mt-2"><strong>Exemple :</strong> "Crée un nouveau membre nommé Jean Martin"</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
45
|
+
<input type="checkbox" />
|
|
46
|
+
<div class="collapse-title font-medium">
|
|
47
|
+
<i class="fa-solid fa-bug mr-2"></i>
|
|
48
|
+
Analyse de bugs
|
|
49
|
+
</div>
|
|
50
|
+
<div class="collapse-content text-sm">
|
|
51
|
+
<p>Signalez un problème et Lucy analysera pour détecter d'éventuels bugs.</p>
|
|
52
|
+
<p class="mt-2"><strong>Exemple :</strong> "Je n'arrive pas à enregistrer le formulaire, j'ai une erreur"</p>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
57
|
+
<input type="checkbox" />
|
|
58
|
+
<div class="collapse-title font-medium">
|
|
59
|
+
<i class="fa-solid fa-keyboard mr-2"></i>
|
|
60
|
+
Raccourcis clavier
|
|
61
|
+
</div>
|
|
62
|
+
<div class="collapse-content text-sm">
|
|
63
|
+
<ul class="list-none space-y-2">
|
|
64
|
+
<li>
|
|
65
|
+
<kbd class="kbd kbd-sm">Ctrl + K</kbd>
|
|
66
|
+
<span class="ml-2">Ouvrir Lucy</span>
|
|
67
|
+
</li>
|
|
68
|
+
<li>
|
|
69
|
+
<kbd class="kbd kbd-sm">Echap</kbd>
|
|
70
|
+
<span class="ml-2">Fermer Lucy</span>
|
|
71
|
+
</li>
|
|
72
|
+
<li>
|
|
73
|
+
<kbd class="kbd kbd-sm">Entrée</kbd>
|
|
74
|
+
<span class="ml-2">Envoyer le message</span>
|
|
75
|
+
</li>
|
|
76
|
+
</ul>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="divider"></div>
|
|
81
|
+
|
|
82
|
+
<h4 class="font-bold mb-2">
|
|
83
|
+
<i class="fa-solid fa-coins mr-2 text-warning"></i>
|
|
84
|
+
Tarification
|
|
85
|
+
</h4>
|
|
86
|
+
|
|
87
|
+
<div class="stats stats-vertical shadow w-full bg-base-200">
|
|
88
|
+
<div class="stat">
|
|
89
|
+
<div class="stat-title">Prix par million de tokens</div>
|
|
90
|
+
<div class="stat-value text-primary">10 EUR</div>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="stat">
|
|
93
|
+
<div class="stat-title">Tokens moyens par conversation</div>
|
|
94
|
+
<div class="stat-value text-secondary">2 000</div>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="stat">
|
|
97
|
+
<div class="stat-title">Conversations pour 10 EUR</div>
|
|
98
|
+
<div class="stat-value text-accent">~500</div>
|
|
99
|
+
<div class="stat-desc">Estimation basée sur l'usage moyen</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="alert alert-info mt-4 text-xs">
|
|
104
|
+
<i class="fa-solid fa-info-circle"></i>
|
|
105
|
+
<span>Le nombre de tokens utilisés dépend de la complexité de vos questions et des réponses générées.</span>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .lucy_assist_factories import (
|
|
2
|
+
ConfigurationLucyAssistFactory,
|
|
3
|
+
ConversationFactory,
|
|
4
|
+
MessageUtilisateurFactory,
|
|
5
|
+
MessageChatbotFactory,
|
|
6
|
+
ConversationAvecMessagesFactory,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
'ConfigurationLucyAssistFactory',
|
|
11
|
+
'ConversationFactory',
|
|
12
|
+
'MessageUtilisateurFactory',
|
|
13
|
+
'MessageChatbotFactory',
|
|
14
|
+
'ConversationAvecMessagesFactory',
|
|
15
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Factories pour les tests Lucy Assist.
|
|
3
|
+
Utilise factory_boy avec faker pour générer des données réalistes.
|
|
4
|
+
"""
|
|
5
|
+
import factory
|
|
6
|
+
from factory.django import DjangoModelFactory
|
|
7
|
+
from faker import Faker
|
|
8
|
+
|
|
9
|
+
from lucy_assist.models import Conversation, Message, ConfigurationLucyAssist
|
|
10
|
+
from lucy_assist.constantes import LucyAssistConstantes
|
|
11
|
+
|
|
12
|
+
fake = Faker('fr_FR')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConfigurationLucyAssistFactory(DjangoModelFactory):
|
|
16
|
+
"""Factory pour créer une configuration Lucy Assist."""
|
|
17
|
+
|
|
18
|
+
class Meta:
|
|
19
|
+
model = ConfigurationLucyAssist
|
|
20
|
+
django_get_or_create = ('id',)
|
|
21
|
+
|
|
22
|
+
id = 1 # Singleton
|
|
23
|
+
tokens_disponibles = factory.LazyFunction(lambda: fake.random_int(min=100000, max=10000000))
|
|
24
|
+
prix_par_million_tokens = 10.0
|
|
25
|
+
questions_frequentes = factory.LazyFunction(lambda: [
|
|
26
|
+
"Comment créer un nouveau membre ?",
|
|
27
|
+
"Comment effectuer un paiement ?",
|
|
28
|
+
"Comment exporter des données ?",
|
|
29
|
+
])
|
|
30
|
+
actif = True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ConversationFactory(DjangoModelFactory):
|
|
34
|
+
"""Factory pour créer des conversations."""
|
|
35
|
+
|
|
36
|
+
class Meta:
|
|
37
|
+
model = Conversation
|
|
38
|
+
|
|
39
|
+
utilisateur = factory.SubFactory('apps.utilisateur.tests.factories.UtilisateurFactory')
|
|
40
|
+
titre = factory.LazyFunction(lambda: fake.sentence(nb_words=5))
|
|
41
|
+
page_contexte = factory.LazyFunction(lambda: f"/{fake.word()}/{fake.word()}/list")
|
|
42
|
+
is_active = True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MessageUtilisateurFactory(DjangoModelFactory):
|
|
46
|
+
"""Factory pour créer des messages utilisateur."""
|
|
47
|
+
|
|
48
|
+
class Meta:
|
|
49
|
+
model = Message
|
|
50
|
+
|
|
51
|
+
conversation = factory.SubFactory(ConversationFactory)
|
|
52
|
+
repondant = LucyAssistConstantes.Repondant.UTILISATEUR
|
|
53
|
+
contenu = factory.LazyFunction(lambda: fake.text(max_nb_chars=200))
|
|
54
|
+
tokens_utilises = 0
|
|
55
|
+
type_action = None
|
|
56
|
+
metadata = factory.LazyFunction(dict)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class MessageChatbotFactory(DjangoModelFactory):
|
|
60
|
+
"""Factory pour créer des messages chatbot."""
|
|
61
|
+
|
|
62
|
+
class Meta:
|
|
63
|
+
model = Message
|
|
64
|
+
|
|
65
|
+
conversation = factory.SubFactory(ConversationFactory)
|
|
66
|
+
repondant = LucyAssistConstantes.Repondant.CHATBOT
|
|
67
|
+
contenu = factory.LazyFunction(lambda: fake.text(max_nb_chars=500))
|
|
68
|
+
tokens_utilises = factory.LazyFunction(lambda: fake.random_int(min=100, max=2000))
|
|
69
|
+
type_action = factory.LazyFunction(
|
|
70
|
+
lambda: fake.random_element([
|
|
71
|
+
LucyAssistConstantes.TypeAction.AIDE_NAVIGATION,
|
|
72
|
+
LucyAssistConstantes.TypeAction.RECHERCHE,
|
|
73
|
+
LucyAssistConstantes.TypeAction.EXPLICATION,
|
|
74
|
+
])
|
|
75
|
+
)
|
|
76
|
+
metadata = factory.LazyFunction(dict)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ConversationAvecMessagesFactory(ConversationFactory):
|
|
80
|
+
"""Factory pour créer une conversation avec plusieurs messages."""
|
|
81
|
+
|
|
82
|
+
@factory.post_generation
|
|
83
|
+
def messages(self, create, extracted, **kwargs):
|
|
84
|
+
if not create:
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
if extracted:
|
|
88
|
+
# Si des messages sont passés, les utiliser
|
|
89
|
+
for message in extracted:
|
|
90
|
+
message.conversation = self
|
|
91
|
+
message.save()
|
|
92
|
+
else:
|
|
93
|
+
# Créer une conversation type avec échanges
|
|
94
|
+
MessageUtilisateurFactory(
|
|
95
|
+
conversation=self,
|
|
96
|
+
contenu="Comment créer un nouveau membre ?"
|
|
97
|
+
)
|
|
98
|
+
MessageChatbotFactory(
|
|
99
|
+
conversation=self,
|
|
100
|
+
contenu="Pour créer un nouveau membre, suivez ces étapes..."
|
|
101
|
+
)
|
|
102
|
+
MessageUtilisateurFactory(
|
|
103
|
+
conversation=self,
|
|
104
|
+
contenu="Merci, et comment lui ajouter une adhésion ?"
|
|
105
|
+
)
|
|
106
|
+
MessageChatbotFactory(
|
|
107
|
+
conversation=self,
|
|
108
|
+
contenu="Pour ajouter une adhésion, rendez-vous sur..."
|
|
109
|
+
)
|