Files changed (1) hide show
  1. app.py +300 -66
app.py CHANGED
@@ -4,7 +4,7 @@ import uuid
4
  import requests
5
  import os
6
  os.environ["USER_AGENT"] = "RAG-App/1.0"
7
- from typing import Dict, List, Any
8
  from dotenv import load_dotenv
9
 
10
  from bs4 import BeautifulSoup
@@ -18,7 +18,7 @@ from langchain_huggingface import HuggingFaceEmbeddings
18
  from langchain_community.vectorstores import Weaviate
19
  from langchain_community.vectorstores import FAISS
20
  from langchain_groq import ChatGroq
21
- from langchain_core.prompts import ChatPromptTemplate,MessagesPlaceholder
22
 
23
  from langchain_classic.chains.combine_documents import create_stuff_documents_chain
24
  from langchain_classic.chains import create_retrieval_chain
@@ -27,57 +27,58 @@ from langchain_core.runnables.history import RunnableWithMessageHistory
27
  from langchain_community.chat_message_histories import ChatMessageHistory
28
  from langchain_core.chat_history import BaseChatMessageHistory
29
 
 
 
30
  #================== CONFIG==================
31
 
32
  load_dotenv()
33
 
34
  set_llm_cache(InMemoryCache())
35
- api_key=os.environ["GROQ_API_KEY"]
36
- #os.environ["HF_API_KEY"]
37
  print("api chargée:" if api_key else "y'a probleme!!")
38
 
39
  #========== charger et decouper documents=================
40
 
41
- urls=[
42
  "https://fr.wikipedia.org/wiki/%C3%89levage",
43
  "https://fr.wikipedia.org/wiki/La_P%C3%AAche"
44
  ]
45
 
46
  loader = WebBaseLoader(urls,
47
  requests_kwargs={
48
- "headers":{
49
- "User-Agent":"RAG-App/1.0"
50
  }
51
  }
52
  )
53
- docs =loader.load()
54
 
55
- splitter = RecursiveCharacterTextSplitter(chunk_size= 1000, chunk_overlap=200)
56
- chunks= splitter.split_documents(docs)
57
 
58
  #============embeding et indexation vers faiss_db================
59
 
60
- embeddings= HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
61
 
62
- faiss_db=FAISS.from_documents(
63
  documents=chunks,
64
  embedding=embeddings
65
- )
66
 
67
- retriever=faiss_db.as_retriever(search_type="similarity", search_kwargs={"k":3})
68
 
69
  #=============== LLM et Prompt=================
70
 
71
  llm = ChatGroq(
72
  model="llama-3.3-70b-versatile",
73
  temperature=0.0,
74
- max_tokens=1200
75
- )
 
76
 
77
  prompt = ChatPromptTemplate.from_messages([
78
  ("system", """Tu es un assistant expert en dans le domaine de l'elevage et la pêche. Réponds clairement.
79
  Si tu ne connais pas, n'invente pas. Garde un ton amical.
80
-
81
  Contexte :
82
  {context}"""),
83
  MessagesPlaceholder(variable_name="chat_history"),
@@ -86,14 +87,14 @@ Contexte :
86
 
87
  #============= CHAINE DE RECUPERATION=======
88
 
89
- stuff_chain= create_stuff_documents_chain(llm, prompt)
90
- rag_chain=create_retrieval_chain(retriever, stuff_chain)
91
 
92
- import gradio as gr
93
 
94
  store = {}
95
 
96
- def get_session_history(session_id:str)->BaseChatMessageHistory:
97
  if session_id not in store:
98
  store[session_id] = ChatMessageHistory()
99
  return store[session_id]
@@ -108,55 +109,288 @@ convers_chain = RunnableWithMessageHistory(
108
  output_messages_key="answer"
109
  )
110
 
111
- # =============FONCTION CHAT ================
112
-
113
  SESSION_ID = str(uuid.uuid4()) # session globale
114
 
115
- def chat_fn(message, history):
116
- result = convers_chain.invoke(
117
- {"input": message},
118
- config={"configurable": {"session_id": SESSION_ID}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  )
120
- return result.get("answer", str(result))
121
-
122
- # def chat_fn(message, history, request: gr.Request):
123
- # session_id = request.session_hash
124
 
125
- # result = convers_chain.invoke(
126
- # {"input": message},
127
- # config={"configurable": {"session_id": session_id}}
128
- # )
129
- # return result.get("answer", str(result))
130
-
131
- #session_id_state = gr.State(value=None)
132
-
133
- # def chat_fn(message, history, session_id_state):
134
- # if session_id_state is None:
135
- # session_id_state = str(uuid.uuid4())
136
-
137
- # result = convers_chain.invoke(
138
- # {"input": message},
139
- # config={"configurable": {"session_id": session_id_state}}
140
- # )
141
-
142
- # response = result.get("answer", str(result))
143
- # return response, session_id_state
144
-
145
-
146
- # ================= GRADIO ====================
147
-
148
- demo = gr.ChatInterface(
149
- fn=chat_fn,
150
- title="🤖 RAG:Specialist en Science Animale 👌",
151
- description="Posez vos questions sur l'élévage et la pêche",
152
- examples=[
153
- "C'est quoi la pêche ?",
154
- "Explique l'élévage",
155
- "Quelle est la différence entre l'élévage et pêche ?"
156
- ]
157
- )
158
-
 
 
 
159
 
160
  # ===================LANCEMENT ================
161
 
162
- demo.launch()
 
4
  import requests
5
  import os
6
  os.environ["USER_AGENT"] = "RAG-App/1.0"
7
+ from typing import Dict, List, Any, Generator
8
  from dotenv import load_dotenv
9
 
10
  from bs4 import BeautifulSoup
 
18
  from langchain_community.vectorstores import Weaviate
19
  from langchain_community.vectorstores import FAISS
20
  from langchain_groq import ChatGroq
21
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
22
 
23
  from langchain_classic.chains.combine_documents import create_stuff_documents_chain
24
  from langchain_classic.chains import create_retrieval_chain
 
27
  from langchain_community.chat_message_histories import ChatMessageHistory
28
  from langchain_core.chat_history import BaseChatMessageHistory
29
 
30
+ import gradio as gr
31
+
32
  #================== CONFIG==================
33
 
34
  load_dotenv()
35
 
36
  set_llm_cache(InMemoryCache())
37
+ api_key = os.environ["GROQ_API_KEY"]
 
38
  print("api chargée:" if api_key else "y'a probleme!!")
39
 
40
  #========== charger et decouper documents=================
41
 
42
+ urls = [
43
  "https://fr.wikipedia.org/wiki/%C3%89levage",
44
  "https://fr.wikipedia.org/wiki/La_P%C3%AAche"
45
  ]
46
 
47
  loader = WebBaseLoader(urls,
48
  requests_kwargs={
49
+ "headers": {
50
+ "User-Agent": "RAG-App/1.0"
51
  }
52
  }
53
  )
54
+ docs = loader.load()
55
 
56
+ splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
57
+ chunks = splitter.split_documents(docs)
58
 
59
  #============embeding et indexation vers faiss_db================
60
 
61
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
62
 
63
+ faiss_db = FAISS.from_documents(
64
  documents=chunks,
65
  embedding=embeddings
66
+ )
67
 
68
+ retriever = faiss_db.as_retriever(search_type="similarity", search_kwargs={"k": 3})
69
 
70
  #=============== LLM et Prompt=================
71
 
72
  llm = ChatGroq(
73
  model="llama-3.3-70b-versatile",
74
  temperature=0.0,
75
+ max_tokens=1200,
76
+ streaming=True # Activer le streaming pour une réponse en temps réel
77
+ )
78
 
79
  prompt = ChatPromptTemplate.from_messages([
80
  ("system", """Tu es un assistant expert en dans le domaine de l'elevage et la pêche. Réponds clairement.
81
  Si tu ne connais pas, n'invente pas. Garde un ton amical.
 
82
  Contexte :
83
  {context}"""),
84
  MessagesPlaceholder(variable_name="chat_history"),
 
87
 
88
  #============= CHAINE DE RECUPERATION=======
89
 
90
+ stuff_chain = create_stuff_documents_chain(llm, prompt)
91
+ rag_chain = create_retrieval_chain(retriever, stuff_chain)
92
 
93
+ # ====== GESTION DE L'HISTORIQUE ======
94
 
95
  store = {}
96
 
97
+ def get_session_history(session_id: str) -> BaseChatMessageHistory:
98
  if session_id not in store:
99
  store[session_id] = ChatMessageHistory()
100
  return store[session_id]
 
109
  output_messages_key="answer"
110
  )
111
 
 
 
112
  SESSION_ID = str(uuid.uuid4()) # session globale
113
 
114
+ # ================= CSS PERSONNALISÉ POUR LE STYLE CHATGPT ====================
115
+
116
+ custom_css = """
117
+ /* Style global */
118
+ .gradio-container {
119
+ max-width: 100% !important;
120
+ margin: 0 !important;
121
+ padding: 0 !important;
122
+ }
123
+
124
+ /* Style de la sidebar */
125
+ .sidebar {
126
+ background: #202123;
127
+ color: white;
128
+ height: 100vh;
129
+ overflow-y: auto;
130
+ }
131
+
132
+ .sidebar-header {
133
+ padding: 12px 16px;
134
+ border-bottom: 1px solid #4d4d4f;
135
+ margin-bottom: 8px;
136
+ }
137
+
138
+ .new-chat-btn {
139
+ width: 100%;
140
+ padding: 8px 16px;
141
+ background: transparent;
142
+ border: 1px solid #4d4d4f;
143
+ color: white;
144
+ border-radius: 8px;
145
+ cursor: pointer;
146
+ font-size: 14px;
147
+ text-align: left;
148
+ transition: background 0.3s;
149
+ }
150
+
151
+ .new-chat-btn:hover {
152
+ background: #2b2c2f;
153
+ }
154
+
155
+ .history-item {
156
+ padding: 12px 16px;
157
+ cursor: pointer;
158
+ border-radius: 8px;
159
+ margin: 4px 8px;
160
+ transition: background 0.3s;
161
+ word-wrap: break-word;
162
+ color: #ececec;
163
+ }
164
+
165
+ .history-item:hover {
166
+ background: #2b2c2f;
167
+ }
168
+
169
+ .history-item.active {
170
+ background: #343541;
171
+ }
172
+
173
+ .main-chat-area {
174
+ background: #343541;
175
+ height: 100vh;
176
+ }
177
+
178
+ .chatbot-container {
179
+ height: calc(100vh - 180px) !important;
180
+ }
181
+
182
+ .chatbot-container > div {
183
+ border: none !important;
184
+ background: #343541 !important;
185
+ }
186
+ """
187
+
188
+ # ================= INTERFACE GRADIO STYLE CHATGPT ====================
189
+
190
+ with gr.Blocks(css=custom_css, theme="soft") as demo:
191
+ with gr.Row(equal_height=True):
192
+ # ==================== SIDEBAR GAUCHE (Style ChatGPT) ====================
193
+ with gr.Column(scale=1, min_width=260, elem_classes="sidebar"):
194
+ gr.HTML("""
195
+ <div class="sidebar-header">
196
+ <h2 style="color: white; font-size: 16px; margin: 0;">🐟 RAG Assistant</h2>
197
+ <p style="color: #8e8ea0; font-size: 12px; margin: 4px 0 0 0;">Élevage & Pêche</p>
198
+ </div>
199
+ """)
200
+
201
+ # Bouton Nouvelle conversation
202
+ new_chat_btn = gr.Button("➕ Nouvelle conversation", elem_classes="new-chat-btn")
203
+
204
+ gr.HTML('<div style="padding: 8px 16px; color: #8e8ea0; font-size: 12px; margin-top: 16px;">HISTORIQUE</div>')
205
+
206
+ # Liste des conversations précédentes
207
+ history_radio = gr.Radio(
208
+ choices=[],
209
+ label=None,
210
+ interactive=True,
211
+ elem_classes="history-radio",
212
+ container=False
213
+ )
214
+
215
+ # Variables d'état pour stocker les conversations
216
+ conversations_state = gr.State([])
217
+ current_conversation_id = gr.State("")
218
+
219
+ # Message pour confirmer les actions
220
+ status_msg = gr.HTML(visible=False)
221
+
222
+ # ==================== ZONE PRINCIPALE DE CHAT (Style ChatGPT) ====================
223
+ with gr.Column(scale=4, elem_classes="main-chat-area"):
224
+ # Header
225
+ gr.HTML("""
226
+ <div style="padding: 16px; border-bottom: 1px solid #4d4d4f; background: #343541;">
227
+ <h2 style="color: white; font-size: 18px; margin: 0; text-align: center;">
228
+ Assistant RAG - Élevage & Pêche
229
+ </h2>
230
+ </div>
231
+ """)
232
+
233
+ # Chatbot
234
+ chatbot = gr.Chatbot(
235
+ label=None,
236
+ height=500,
237
+ elem_classes="chatbot-container",
238
+ show_label=False,
239
+ bubble_full_width=False,
240
+ avatar_images=(None, "🐟")
241
+ )
242
+
243
+ # Zone de saisie style ChatGPT
244
+ with gr.Row():
245
+ msg_input = gr.Textbox(
246
+ show_label=False,
247
+ placeholder="Envoyez un message...",
248
+ scale=9,
249
+ container=False,
250
+ lines=1,
251
+ max_lines=5
252
+ )
253
+ send_btn = gr.Button("↑", variant="primary", scale=1, min_width=40)
254
+
255
+ # Texte de copyright en bas
256
+ gr.HTML("""
257
+ <div style="text-align: center; padding: 8px; color: #8e8ea0; font-size: 11px;">
258
+ RAG Assistant peut faire des erreurs. Vérifiez les informations importantes.
259
+ </div>
260
+ """)
261
+
262
+ # ==================== FONCTIONS DE GESTION ====================
263
+
264
+ def create_new_conversation(conversations):
265
+ """Crée une nouvelle conversation et retourne l'ID."""
266
+ conversation_id = str(uuid.uuid4())
267
+ title = "Nouvelle conversation"
268
+ conversations.append({"id": conversation_id, "title": title, "messages": []})
269
+ return conversations, conversation_id, title, gr.update(choices=[c["title"] for c in conversations], value=None)
270
+
271
+ def load_conversation(selected_title, conversations):
272
+ """Charge une conversation existante."""
273
+ if not selected_title:
274
+ return [], "", ""
275
+
276
+ for conv in conversations:
277
+ if conv["title"] == selected_title:
278
+ chat_history = []
279
+ for msg in conv["messages"]:
280
+ if msg["role"] == "user":
281
+ chat_history.append((msg["content"], None))
282
+ else:
283
+ if chat_history:
284
+ chat_history[-1] = (chat_history[-1][0], msg["content"])
285
+ else:
286
+ chat_history.append((None, msg["content"]))
287
+ return chat_history, conv["id"], conv["title"]
288
+
289
+ return [], "", ""
290
+
291
+ def send_message(message, chat_history, conversations, current_conv_id, chat_state):
292
+ """Envoie un message et met à jour la conversation."""
293
+ if not message or not message.strip():
294
+ return "", chat_history, conversations, current_conv_id, chat_state
295
+
296
+ # Créer une nouvelle conversation si nécessaire
297
+ if not current_conv_id:
298
+ current_conv_id = str(uuid.uuid4())
299
+ # Utiliser les premiers mots comme titre
300
+ title = message[:50] + "..." if len(message) > 50 else message
301
+ conversations.append({
302
+ "id": current_conv_id,
303
+ "title": title,
304
+ "messages": [{"role": "user", "content": message}]
305
+ })
306
+ else:
307
+ # Ajouter le message à la conversation existante
308
+ for conv in conversations:
309
+ if conv["id"] == current_conv_id:
310
+ conv["messages"].append({"role": "user", "content": message})
311
+ if conv["title"] == "Nouvelle conversation":
312
+ conv["title"] = message[:50] + "..." if len(message) > 50 else message
313
+ break
314
+
315
+ # Ajouter le message utilisateur au chatbot
316
+ chat_history.append((message, None))
317
+
318
+ # Obtenir la réponse de l'assistant
319
+ result = convers_chain.invoke(
320
+ {"input": message},
321
+ config={"configurable": {"session_id": SESSION_ID}}
322
+ )
323
+
324
+ response = result.get("answer", str(result))
325
+
326
+ # Ajouter la réponse à la conversation
327
+ for conv in conversations:
328
+ if conv["id"] == current_conv_id:
329
+ conv["messages"].append({"role": "assistant", "content": response})
330
+ break
331
+
332
+ # Mettre à jour le chatbot
333
+ chat_history[-1] = (message, response)
334
+
335
+ # Mettre à jour les choix du radio
336
+ choices = [conv["title"] for conv in conversations]
337
+
338
+ return "", chat_history, conversations, current_conv_id, gr.update(choices=choices, value=conversations[-1]["title"] if conversations else None)
339
+
340
+ def clear_chat():
341
+ """Efface le chat et commence une nouvelle conversation."""
342
+ return [], "", None, gr.update(choices=[], value=None)
343
+
344
+ # ==================== GESTIONNAIRES D'ÉVÉNEMENTS ====================
345
+
346
+ # Envoi de message
347
+ msg_input.submit(
348
+ send_message,
349
+ inputs=[msg_input, chatbot, conversations_state, current_conversation_id, gr.State([])],
350
+ outputs=[msg_input, chatbot, conversations_state, current_conversation_id, history_radio]
351
+ ).then(
352
+ lambda: gr.update(value=""),
353
+ outputs=[msg_input]
354
  )
 
 
 
 
355
 
356
+ send_btn.click(
357
+ send_message,
358
+ inputs=[msg_input, chatbot, conversations_state, current_conversation_id, gr.State([])],
359
+ outputs=[msg_input, chatbot, conversations_state, current_conversation_id, history_radio]
360
+ ).then(
361
+ lambda: gr.update(value=""),
362
+ outputs=[msg_input]
363
+ )
364
+
365
+ # Nouvelle conversation
366
+ new_chat_btn.click(
367
+ create_new_conversation,
368
+ inputs=[conversations_state],
369
+ outputs=[conversations_state, current_conversation_id, gr.State(""), history_radio]
370
+ ).then(
371
+ clear_chat,
372
+ outputs=[chatbot, current_conversation_id, history_radio, history_radio]
373
+ )
374
+
375
+ # Charger une conversation depuis l'historique
376
+ history_radio.change(
377
+ load_conversation,
378
+ inputs=[history_radio, conversations_state],
379
+ outputs=[chatbot, current_conversation_id, gr.State("")]
380
+ )
381
+
382
+ # Support de la touche Entrée (Shift+Entrée pour nouvelle ligne)
383
+ def handle_enter_key(text, event: gr.EventData):
384
+ if event.key == "Enter" and not event.shift:
385
+ return text, gr.update()
386
+ return text, gr.update()
387
+
388
+ msg_input.key_up(
389
+ handle_enter_key,
390
+ inputs=[msg_input],
391
+ outputs=[msg_input, chatbot]
392
+ )
393
 
394
  # ===================LANCEMENT ================
395
 
396
+ demo.launch(share=False)