AxL95 commited on
Commit
085905a
·
verified ·
1 Parent(s): 86c942e

Update chat.py

Browse files
Files changed (1) hide show
  1. chat.py +158 -245
chat.py CHANGED
@@ -1,12 +1,11 @@
1
  from fastapi import APIRouter, Request, HTTPException, Depends
2
- from fastapi.responses import JSONResponse, StreamingResponse
3
  from datetime import datetime
4
  from bson.objectid import ObjectId
5
  from huggingface_hub import InferenceClient
6
  from sentence_transformers import SentenceTransformer
7
  from sklearn.metrics.pairwise import cosine_similarity
8
  import re
9
- import json
10
 
11
  from auth import get_current_user
12
  from database import get_db
@@ -17,41 +16,6 @@ db=get_db()
17
  conversation_history = {}
18
  hf_client = InferenceClient(token=HF_TOKEN)
19
 
20
- def save_bot_response(conversation_id, current_user, text, current_tokens=0, message_tokens=0):
21
- """Fonction utilitaire pour sauvegarder toutes les réponses du bot"""
22
- if not conversation_id or not current_user:
23
- print("⚠️ Impossible de sauvegarder la réponse - conversation_id ou current_user manquant")
24
- return None
25
-
26
- try:
27
- # Sauvegarder le message
28
- message_id = db.messages.insert_one({
29
- "conversation_id": conversation_id,
30
- "user_id": str(current_user["_id"]),
31
- "sender": "bot",
32
- "text": text,
33
- "timestamp": datetime.utcnow()
34
- }).inserted_id
35
-
36
- # Mettre à jour les métadonnées de la conversation
37
- response_tokens = int(len(text.split()) * 1.3) if text else 0
38
- total_tokens = current_tokens + message_tokens + response_tokens
39
-
40
- db.conversations.update_one(
41
- {"_id": ObjectId(conversation_id)},
42
- {"$set": {
43
- "last_message": text[:100] + ("..." if len(text) > 100 else ""),
44
- "updated_at": datetime.utcnow(),
45
- "token_count": total_tokens
46
- }}
47
- )
48
-
49
- print(f"✅ Réponse du bot sauvegardée avec ID: {message_id}")
50
- return message_id
51
- except Exception as e:
52
- print(f"❌ Erreur lors de la sauvegarde: {str(e)}")
53
- return None
54
-
55
  try:
56
  from langchain_community.embeddings import HuggingFaceEmbeddings
57
  embedding_model = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL)
@@ -60,7 +24,7 @@ except Exception as e:
60
  print(f"Erreur chargement embedding: {str(e)}")
61
  embedding_model = None
62
 
63
- # Fonction de RAG (inchangée)
64
  def retrieve_relevant_context(query, embedding_model, mongo_collection, k=5):
65
  query_embedding = embedding_model.embed_query(query)
66
 
@@ -103,16 +67,7 @@ async def chat(request: Request):
103
  conversation_id = data.get("conversation_id")
104
  skip_save = data.get("skip_save", False)
105
 
106
- if not user_message:
107
- raise HTTPException(status_code=400, detail="Le champ 'message' est requis.")
108
-
109
- current_user = None
110
- try:
111
- current_user = await get_current_user(request)
112
- except HTTPException:
113
- pass
114
 
115
- # Sauvegarde du message utilisateur (si non anonyme)
116
  if not skip_save and conversation_id and current_user:
117
  db.messages.insert_one({
118
  "conversation_id": conversation_id,
@@ -122,7 +77,17 @@ async def chat(request: Request):
122
  "timestamp": datetime.utcnow()
123
  })
124
 
125
- # Vérification des limites de tokens
 
 
 
 
 
 
 
 
 
 
126
  current_tokens = 0
127
  message_tokens = 0
128
  if current_user and conversation_id:
@@ -133,21 +98,17 @@ async def chat(request: Request):
133
  if conv:
134
  current_tokens = conv.get("token_count", 0)
135
  message_tokens = int(len(user_message.split()) * 1.3)
 
136
  if current_tokens + message_tokens > MAX_TOKENS:
137
- error_message = "⚠️ **Limite de taille de conversation atteinte**\n\nCette conversation est devenue trop longue. Pour continuer à discuter, veuillez créer une nouvelle conversation."
138
-
139
- # Sauvegarder ce message d'erreur dans la BD
140
- if conversation_id and current_user:
141
- save_bot_response(conversation_id, current_user, error_message, current_tokens, message_tokens)
142
-
143
  return JSONResponse({
144
  "error": "token_limit_exceeded",
145
- "message": error_message,
146
  "tokens_used": current_tokens,
147
  "tokens_limit": MAX_TOKENS
148
  }, status_code=403)
149
 
150
- # Détection des questions sur l'historique
 
151
  is_history_question = any(
152
  phrase in user_message.lower()
153
  for phrase in [
@@ -161,7 +122,6 @@ async def chat(request: Request):
161
  or re.search(r"question pr[eé]c[eé]dente", user_message.lower()) \
162
  or re.search(r"(toutes|liste|quelles|quoi).*questions", user_message.lower())
163
 
164
- # Initialisation de l'historique si nécessaire
165
  if conversation_id not in conversation_history:
166
  conversation_history[conversation_id] = []
167
  if current_user and conversation_id:
@@ -175,106 +135,116 @@ async def chat(request: Request):
175
  else:
176
  conversation_history[conversation_id].append(f"Réponse : {msg['text']}")
177
 
178
- # Traitement spécial pour les questions sur l'historique
179
  if is_history_question:
180
- # Extraire les questions réelles (non meta)
181
  actual_questions = []
182
 
183
  if conversation_id in conversation_history:
184
  for msg in conversation_history[conversation_id]:
185
  if msg.startswith("Question : "):
186
  q_text = msg.replace("Question : ", "")
187
- # Vérifier si ce n'est pas une méta-question
188
  is_meta = any(phrase in q_text.lower() for phrase in [
189
  "ma première question", "ma précédente question", "ma dernière question",
190
  "ce que j'ai demandé", "j'ai dit quoi", "quelles questions",
191
  "c'était quoi ma", "quelle était ma", "mes questions"
192
  ]) or re.search(r"(?:quelle|quelles|quoi).*?(\d+)[a-z]{2}.*?question", q_text.lower()) \
193
- or re.search(r"derni[eè]re question", q_text.lower()) \
194
- or re.search(r"premi[eè]re question", q_text.lower()) \
195
- or re.search(r"question pr[eé]c[eé]dente", q_text.lower()) \
196
- or re.search(r"(toutes|liste|quelles|quoi).*questions", q_text.lower())
197
  if not is_meta:
198
  actual_questions.append(q_text)
199
 
200
- # Préparer la réponse en fonction du type spécifique de question
201
- history_response = ""
202
-
203
  if not actual_questions:
204
- history_response = "Vous n'avez pas encore posé de question dans cette conversation. C'est notre premier échange."
205
- else:
206
- # Rechercher des mots-clés spécifiques pour personnaliser la réponse
207
-
208
- # Cas 1: Question précédente/dernière question
209
- if any(phrase in user_message.lower() for phrase in ["question précédente", "dernière question"]) and len(actual_questions) > 1:
210
- # La dernière question est l'avant-dernière du tableau (la dernière étant la question actuelle)
211
- prev_question = actual_questions[-1] if actual_questions else "Aucune question précédente trouvée."
212
- history_response = f"**Votre question précédente était :**\n\n\"{prev_question}\""
213
-
214
- # Cas 2: Première question
215
- elif any(phrase in user_message.lower() for phrase in ["première question", "1ère question", "1ere question"]):
216
- first_question = actual_questions[0] if actual_questions else "Aucune première question trouvée."
217
- history_response = f"**Votre première question était :**\n\n\"{first_question}\""
218
 
219
- # Cas 3: Question numérotée spécifique (2ème, 3ème, etc.)
220
- elif re.search(r"(\d+)[èeme]{1,3}", user_message.lower()):
221
- match = re.search(r"(\d+)[èeme]{1,3}", user_message.lower())
222
- if match:
223
- question_num = int(match.group(1))
224
- if 0 < question_num <= len(actual_questions):
225
- specific_question = actual_questions[question_num-1]
226
- history_response = f"**Votre question n°{question_num} était :**\n\n\"{specific_question}\""
227
- else:
228
- history_response = f"Je ne trouve pas de question n°{question_num} dans notre conversation. Vous n'avez posé que {len(actual_questions)} question(s)."
229
 
230
- # Cas par défaut: Toutes les questions
 
 
 
 
231
  else:
232
- history_response = "**Voici les questions que vous avez posées dans cette conversation :**\n\n"
233
- for i, question in enumerate(actual_questions, 1):
234
- history_response += f"{i}. {question}\n"
235
-
236
- # Ajouter des informations supplémentaires pour les longues listes
237
- if len(actual_questions) > 3:
238
- history_response += f"\nVous avez posé {len(actual_questions)} questions dans cette conversation."
239
-
240
- # Ajouter l'historique en mémoire
241
- if conversation_id:
242
- conversation_history[conversation_id].append(f"Réponse : {history_response}")
243
 
244
- # IMPORTANT: Sauvegarder la réponse dans la BD
245
- if conversation_id and current_user:
246
- save_bot_response(conversation_id, current_user, history_response, current_tokens, message_tokens)
247
- print(f"✅ Réponse à la question d'historique sauvegardée pour conversation {conversation_id}")
 
 
 
 
 
 
 
 
 
 
248
 
249
- return JSONResponse({"response": history_response})
250
-
251
- # Ajout de la question actuelle à l'historique
252
- if conversation_id:
253
- conversation_history[conversation_id].append(f"Question : {user_message}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
- # Récupération du contexte RAG
256
  context = None
257
  if not is_history_question and embedding_model:
258
  context = retrieve_relevant_context(user_message, embedding_model, db.connaissances, k=5)
259
  if context and conversation_id:
260
  conversation_history[conversation_id].append(f"Contexte : {context}")
261
 
262
- # Préparation du prompt système
 
 
263
  system_prompt = (
264
- "Tu es un chatbot spécialisé dans la santé mentale, et plus particulièrement la schizophrénie. "
265
- "Tu réponds de façon fiable, claire et empathique, en t'appuyant uniquement sur des sources médicales et en français. "
266
- "IMPORTANT: Fais particulièrement attention aux questions de suivi. Si l'utilisateur pose une question qui ne précise "
267
- "pas clairement le sujet mais qui fait suite à votre échange précédent, comprends que cette question fait référence "
268
- "au contexte de la conversation précédente. Par exemple, si l'utilisateur demande 'Comment les traite-t-on?' après "
269
- "avoir parlé des symptômes positifs de la schizophrénie, ta réponse doit porter spécifiquement sur le traitement "
270
- "des symptômes positifs, et non sur la schizophrénie en général. IMPORTANT: Vise tes réponses sous forme de Markdown."
271
- "IMPÉRATIF: Structure tes réponses en Markdown, utilisant **des gras** pour les points importants, "
272
- "des titres avec ## pour les sections principales, des listes à puces avec * pour énumérer des points, "
273
- "et > pour les citations importantes. Cela rend ton contenu plus facile à lire et à comprendre."
274
-
275
- )
276
 
277
- # Enrichir l prompt avec l'historique et le contexte RAG
278
  enriched_context = ""
279
 
280
  if conversation_id in conversation_history:
@@ -321,133 +291,76 @@ async def chat(request: Request):
321
  "Tu dois donner une réponse complète et bien structurée."
322
  )
323
 
324
- # Préparation des messages pour le LLM
325
  messages = [{"role": "system", "content": system_prompt}]
326
 
327
  if conversation_id and len(conversation_history.get(conversation_id, [])) > 0:
328
  history = conversation_history[conversation_id]
329
-
330
- # Assurer l'alternance user/assistant
331
- user_messages = []
332
- bot_messages = []
333
-
334
- for i in range(len(history)):
335
- if i < len(history) and history[i].startswith("Question :"):
336
- user_text = history[i].replace("Question : ", "")
337
- user_messages.append(user_text)
338
 
339
- if i+1 < len(history) and history[i+1].startswith("Réponse :"):
340
- bot_text = history[i+1].replace("Réponse : ", "")
341
- bot_messages.append(bot_text)
342
-
343
- # Construire des paires user/assistant
344
- valid_pairs = min(len(user_messages), len(bot_messages))
345
-
346
- for i in range(valid_pairs):
347
- messages.append({"role": "user", "content": user_messages[i]})
348
- messages.append({"role": "assistant", "content": bot_messages[i]})
349
 
350
- # Ajouter le message actuel
351
  messages.append({"role": "user", "content": user_message})
352
 
353
- # Fonction génératrice pour le streaming
354
- async def generate_stream():
 
 
 
 
 
 
 
 
 
355
  try:
356
- collected_response = ""
357
-
358
- # Signal de début de stream
359
- yield "data: {\"type\": \"start\"}\n\n"
360
-
361
- # Appel à l'API Hugging Face avec streaming
362
- completion_stream = hf_client.chat.completions.create(
363
  model="mistralai/Mistral-7B-Instruct-v0.3",
364
- messages=messages,
365
- max_tokens=900,
366
- temperature=0.7,
367
- stream=True
368
  )
369
- chunk_buffer = ""
370
- chunk_count = 0
371
- MAX_CHUNKS_BEFORE_SEND = 3 # Envoyer tous les 5 chunks reçus
372
- # Traiter chaque chunk
373
- for chunk in completion_stream:
374
- if chunk.choices and chunk.choices[0].delta.content:
375
- content = chunk.choices[0].delta.content
376
- collected_response += content
377
- chunk_buffer += content
378
- chunk_count += 1
379
-
380
- # Envoyer es chunks accumulés périodiquement
381
- if chunk_count >= MAX_CHUNKS_BEFORE_SEND or '\n' in content:
382
- yield f"data: {json.dumps({'content': chunk_buffer})}\n\n"
383
- chunk_buffer = ""
384
- chunk_count = 0
385
 
386
- # Envoyer le reste du buffer
387
- if chunk_buffer:
388
- yield f"data: {json.dumps({'content': chunk_buffer})}\n\n"
389
-
390
- # Ajouter une note si nécessaire
391
- if collected_response.endswith((".", "!", "?")) == False and len(collected_response) > 500:
392
- suffix = "\n\n(Note: Ma réponse a été limitée par des contraintes de taille. N'hésitez pas à me demander de poursuivre si vous souhaitez plus d'informations.)"
393
- collected_response += suffix
394
- yield f"data: {json.dumps({'content': suffix})}\n\n"
395
-
396
- # Sauvegarder la réponse complète dans l'historique en mémoire
397
- if conversation_id:
398
- conversation_history[conversation_id].append(f"Réponse : {collected_response}")
399
-
400
- if len(conversation_history[conversation_id]) > 50:
401
- conversation_history[conversation_id] = conversation_history[conversation_id][-50:]
402
-
403
- # Sauvegarder la réponse dans la BD (sans condition skip_save)
404
- if conversation_id and current_user:
405
- save_bot_response(conversation_id, current_user, collected_response, current_tokens, message_tokens)
406
-
407
- # Signal de fin de stream
408
- yield "data: {\"type\": \"end\"}\n\n"
409
-
410
- except Exception as e:
411
- # Gérer les erreurs de streaming
412
- error_message = str(e)
413
- print(f"❌ Streaming error: {error_message}")
414
-
415
- try:
416
- # Fallback en mode non-streaming
417
- fallback = hf_client.text_generation(
418
- model="mistralai/Mistral-7B-Instruct-v0.3",
419
- prompt=f"<s>[INST] {system_prompt}\n\nQuestion: {user_message} [/INST]",
420
- max_new_tokens=512,
421
- temperature=0.7
422
- )
423
- yield f"data: {json.dumps({'content': fallback})}\n\n"
424
-
425
- # Sauvegarder la réponse de fallback dans l'historique
426
- if conversation_id:
427
- conversation_history[conversation_id].append(f"Réponse : {fallback}")
428
-
429
- # Sauvegarder la réponse fallback dans la BD
430
- if conversation_id and current_user:
431
- save_bot_response(conversation_id, current_user, fallback, current_tokens, message_tokens)
432
-
433
- except Exception as fallback_error:
434
- print(f"❌ Erreur de fallback: {str(fallback_error)}")
435
- error_response = "Je suis désolé, je rencontre actuellement des difficultés techniques. Pourriez-vous reformuler votre question ou réessayer dans quelques instants?"
436
- yield f"data: {json.dumps({'content': error_response})}\n\n"
437
-
438
- # Sauvegarder aussi les messages d'erreur technique
439
- if conversation_id and current_user:
440
- save_bot_response(conversation_id, current_user, error_response, current_tokens, message_tokens)
441
-
442
- # Signal de fin de stream
443
- yield "data: {\"type\": \"end\"}\n\n"
444
-
445
- # Retourner une réponse en streaming
446
- return StreamingResponse(
447
- generate_stream(),
448
- media_type="text/event-stream",
449
- headers={
450
- "Cache-Control": "no-cache, no-transform",
451
- "X-Accel-Buffering": "no",
452
- }
453
- )
 
1
  from fastapi import APIRouter, Request, HTTPException, Depends
2
+ from fastapi.responses import JSONResponse
3
  from datetime import datetime
4
  from bson.objectid import ObjectId
5
  from huggingface_hub import InferenceClient
6
  from sentence_transformers import SentenceTransformer
7
  from sklearn.metrics.pairwise import cosine_similarity
8
  import re
 
9
 
10
  from auth import get_current_user
11
  from database import get_db
 
16
  conversation_history = {}
17
  hf_client = InferenceClient(token=HF_TOKEN)
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  try:
20
  from langchain_community.embeddings import HuggingFaceEmbeddings
21
  embedding_model = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL)
 
24
  print(f"Erreur chargement embedding: {str(e)}")
25
  embedding_model = None
26
 
27
+ # Fonctions de RAG
28
  def retrieve_relevant_context(query, embedding_model, mongo_collection, k=5):
29
  query_embedding = embedding_model.embed_query(query)
30
 
 
67
  conversation_id = data.get("conversation_id")
68
  skip_save = data.get("skip_save", False)
69
 
 
 
 
 
 
 
 
 
70
 
 
71
  if not skip_save and conversation_id and current_user:
72
  db.messages.insert_one({
73
  "conversation_id": conversation_id,
 
77
  "timestamp": datetime.utcnow()
78
  })
79
 
80
+
81
+
82
+ if not user_message:
83
+ raise HTTPException(status_code=400, detail="Le champ 'message' est requis.")
84
+
85
+ current_user = None
86
+ try:
87
+ current_user = await get_current_user(request)
88
+ except HTTPException:
89
+ pass
90
+
91
  current_tokens = 0
92
  message_tokens = 0
93
  if current_user and conversation_id:
 
98
  if conv:
99
  current_tokens = conv.get("token_count", 0)
100
  message_tokens = int(len(user_message.split()) * 1.3)
101
+ MAX_TOKENS = 2000
102
  if current_tokens + message_tokens > MAX_TOKENS:
 
 
 
 
 
 
103
  return JSONResponse({
104
  "error": "token_limit_exceeded",
105
+ "message": "Cette conversation a atteint sa limite de taille. Veuillez en créer une nouvelle.",
106
  "tokens_used": current_tokens,
107
  "tokens_limit": MAX_TOKENS
108
  }, status_code=403)
109
 
110
+
111
+
112
  is_history_question = any(
113
  phrase in user_message.lower()
114
  for phrase in [
 
122
  or re.search(r"question pr[eé]c[eé]dente", user_message.lower()) \
123
  or re.search(r"(toutes|liste|quelles|quoi).*questions", user_message.lower())
124
 
 
125
  if conversation_id not in conversation_history:
126
  conversation_history[conversation_id] = []
127
  if current_user and conversation_id:
 
135
  else:
136
  conversation_history[conversation_id].append(f"Réponse : {msg['text']}")
137
 
 
138
  if is_history_question:
 
139
  actual_questions = []
140
 
141
  if conversation_id in conversation_history:
142
  for msg in conversation_history[conversation_id]:
143
  if msg.startswith("Question : "):
144
  q_text = msg.replace("Question : ", "")
 
145
  is_meta = any(phrase in q_text.lower() for phrase in [
146
  "ma première question", "ma précédente question", "ma dernière question",
147
  "ce que j'ai demandé", "j'ai dit quoi", "quelles questions",
148
  "c'était quoi ma", "quelle était ma", "mes questions"
149
  ]) or re.search(r"(?:quelle|quelles|quoi).*?(\d+)[a-z]{2}.*?question", q_text.lower()) \
150
+ or re.search(r"derni[eè]re question", q_text.lower()) \
151
+ or re.search(r"premi[eè]re question", q_text.lower()) \
152
+ or re.search(r"question pr[eé]c[eé]dente", q_text.lower()) \
153
+ or re.search(r"(toutes|liste|quelles|quoi).*questions", q_text.lower())
154
  if not is_meta:
155
  actual_questions.append(q_text)
156
 
 
 
 
157
  if not actual_questions:
158
+ return JSONResponse({
159
+ "response": "Vous n'avez pas encore posé de question dans cette conversation. C'est notre premier échange."
160
+ })
 
 
 
 
 
 
 
 
 
 
 
161
 
162
+ if re.search(r"derni[eè]re question", user_message.lower()):
163
+ return JSONResponse({
164
+ "response": f"Votre dernière question était : « {actual_questions[-1]} »"
165
+ })
 
 
 
 
 
 
166
 
167
+ if re.search(r"question pr[eé]c[eé]dente", user_message.lower()):
168
+ if len(actual_questions) >= 2:
169
+ return JSONResponse({
170
+ "response": f"Votre question précédente était : « {actual_questions[-2]} »"
171
+ })
172
  else:
173
+ return JSONResponse({
174
+ "response": "Il n'y a pas encore de question précédente dans notre conversation."
175
+ })
176
+
177
+ if re.search(r"premi[eè]re question", user_message.lower()) or any(p in user_message.lower() for p in ["première question", "1ère question", "1ere question"]):
178
+ return JSONResponse({
179
+ "response": f"Votre première question était : « {actual_questions[0]} »"
180
+ })
 
 
 
181
 
182
+ match_nth = re.search(r"(?:quelle|quelles|quoi).*?(\d+)[a-z]{2}.*?question", user_message.lower())
183
+ if match_nth:
184
+ try:
185
+ question_number = int(match_nth.group(1))
186
+ if 0 < question_number <= len(actual_questions):
187
+ return JSONResponse({
188
+ "response": f"Votre {question_number}{'ère' if question_number == 1 else 'ème'} question était : « {actual_questions[question_number-1]} »"
189
+ })
190
+ else:
191
+ return JSONResponse({
192
+ "response": f"Vous n'avez pas encore posé {question_number} questions dans cette conversation."
193
+ })
194
+ except:
195
+ pass
196
 
197
+ question_number = None
198
+ if any(p in user_message.lower() for p in ["deuxième question", "2ème question", "2eme question", "seconde question"]):
199
+ question_number = 2
200
+ else:
201
+ match = re.search(r'(\d+)[eèiéê]*m*e* question', user_message.lower())
202
+ if match:
203
+ try:
204
+ question_number = int(match.group(1))
205
+ except:
206
+ pass
207
+
208
+ if question_number is not None:
209
+ if 0 < question_number <= len(actual_questions):
210
+ suffix = "ère" if question_number == 1 else "ème"
211
+ return JSONResponse({
212
+ "response": f"Votre {question_number}{suffix} question était : « {actual_questions[question_number-1]} »"
213
+ })
214
+ else:
215
+ return JSONResponse({
216
+ "response": f"Vous n'avez pas encore posé {question_number} questions dans cette conversation."
217
+ })
218
+
219
+ if len(actual_questions) == 1:
220
+ return JSONResponse({
221
+ "response": f"Vous avez posé une seule question jusqu'à présent : « {actual_questions[0]} »"
222
+ })
223
+ else:
224
+ question_list = "\n".join([f"{i+1}. {q}" for i, q in enumerate(actual_questions)])
225
+ return JSONResponse({
226
+ "response": f"Voici les questions que vous avez posées dans cette conversation :\n\n{question_list}"
227
+ })
228
 
 
229
  context = None
230
  if not is_history_question and embedding_model:
231
  context = retrieve_relevant_context(user_message, embedding_model, db.connaissances, k=5)
232
  if context and conversation_id:
233
  conversation_history[conversation_id].append(f"Contexte : {context}")
234
 
235
+ if conversation_id:
236
+ conversation_history[conversation_id].append(f"Question : {user_message}")
237
+
238
  system_prompt = (
239
+ "Tu es un chatbot spécialisé dans la santé mentale, et plus particulièrement la schizophrénie. "
240
+ "Tu réponds de façon fiable, claire et empathique, en t'appuyant uniquement sur des sources médicales et en français. "
241
+ "IMPORTANT: Fais particulièrement attention aux questions de suivi. Si l'utilisateur pose une question qui ne précise "
242
+ "pas clairement le sujet mais qui fait suite à votre échange précédent, comprends que cette question fait référence "
243
+ "au contexte de la conversation précédente. Par exemple, si l'utilisateur demande 'Comment les traite-t-on?' après "
244
+ "avoir parlé des symptômes positifs de la schizophrénie, ta réponse doit porter spécifiquement sur le traitement "
245
+ "des symptômes positifs, et non sur la schizophrénie en général.IMPORTANT: Vise tes réponses sous forme de Markdown."
246
+ )
 
 
 
 
247
 
 
248
  enriched_context = ""
249
 
250
  if conversation_id in conversation_history:
 
291
  "Tu dois donner une réponse complète et bien structurée."
292
  )
293
 
 
294
  messages = [{"role": "system", "content": system_prompt}]
295
 
296
  if conversation_id and len(conversation_history.get(conversation_id, [])) > 0:
297
  history = conversation_history[conversation_id]
298
+ for i in range(0, min(20, len(history)-1), 2):
299
+ if i+1 < len(history):
300
+ if history[i].startswith("Question :"):
301
+ user_text = history[i].replace("Question : ", "")
302
+ messages.append({"role": "user", "content": user_text})
 
 
 
 
303
 
304
+ if history[i+1].startswith("Réponse :"):
305
+ assistant_text = history[i+1].replace("Réponse : ", "")
306
+ messages.append({"role": "assistant", "content": assistant_text})
 
 
 
 
 
 
 
307
 
 
308
  messages.append({"role": "user", "content": user_message})
309
 
310
+ try:
311
+ completion = hf_client.chat.completions.create(
312
+ model="mistralai/Mistral-7B-Instruct-v0.3",
313
+ messages=messages,
314
+ max_tokens=1024,
315
+ temperature=0.7
316
+ )
317
+ bot_response = completion.choices[0].message["content"].strip()
318
+ if bot_response.endswith((".", "!", "?")) == False and len(bot_response) > 500:
319
+ bot_response += "\n\n(Note: Ma réponse a été limitée par des contraintes de taille. N'hésitez pas à me demander de poursuivre si vous souhaitez plus d'informations.)"
320
+ except Exception:
321
  try:
322
+ fallback = hf_client.text_generation(
 
 
 
 
 
 
323
  model="mistralai/Mistral-7B-Instruct-v0.3",
324
+ prompt=f"<s>[INST] {system_prompt}\n\nQuestion: {user_message} [/INST]",
325
+ max_new_tokens=512,
326
+ temperature=0.7
 
327
  )
328
+ bot_response = fallback
329
+ except Exception:
330
+ bot_response = "Je suis désolé, je rencontre actuellement des difficultés techniques. Pourriez-vous reformuler votre question ou réessayer dans quelques instants?"
 
 
 
 
 
 
 
 
 
 
 
 
 
331
 
332
+ if conversation_id:
333
+ conversation_history[conversation_id].append(f"Réponse : {bot_response}")
334
+
335
+ if len(conversation_history[conversation_id]) > 50:
336
+ conversation_history[conversation_id] = conversation_history[conversation_id][-50:]
337
+
338
+ if not skip_save and conversation_id and current_user:
339
+ db.messages.insert_one({
340
+ "conversation_id": conversation_id,
341
+ "user_id": str(current_user["_id"]),
342
+ "sender": "bot",
343
+ "text": bot_response,
344
+ "timestamp": datetime.utcnow()
345
+ })
346
+
347
+ if conversation_id and current_user:
348
+ db.messages.insert_one({
349
+ "conversation_id": conversation_id,
350
+ "user_id": str(current_user["_id"]),
351
+ "sender": "bot",
352
+ "text": bot_response,
353
+ "timestamp": datetime.utcnow()
354
+ })
355
+ response_tokens = int(len(bot_response.split()) * 1.3)
356
+ total_tokens = current_tokens + message_tokens + response_tokens
357
+ db.conversations.update_one(
358
+ {"_id": ObjectId(conversation_id)},
359
+ {"$set": {
360
+ "last_message": bot_response,
361
+ "updated_at": datetime.utcnow(),
362
+ "token_count": total_tokens
363
+ }}
364
+ )
365
+
366
+ return {"response": bot_response}