OutrageousOtter commited on
Commit
2579346
·
verified ·
1 Parent(s): 277ecd2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +229 -45
app.py CHANGED
@@ -1,80 +1,267 @@
1
  #region# import libs
2
  import streamlit as st
3
  import os
4
- from transformers import pipeline
5
- from transformers import AutoModelForCausalLM, AutoTokenizer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  #endregion
7
-
8
  #region# Titre de l'application et mise en page
9
  st.set_page_config(page_title="Expert nutrition volaille", page_icon="🤖")
10
  st.title("Chatbot AI avec l'expert nutrition")
11
  st.sidebar.image("img/avril_logo_rvb.jpg")
12
  st.sidebar.header("")
13
  #endregion
14
-
15
  #region# Elements graphiques
16
-
17
  #Choix production
18
  choix_prod = st.sidebar.pills(
19
  "Sur quelle espèce voulez-vous avoir des renseignements ?",
20
- ("Pondeuse", "Chair"),)
21
-
22
- #Niveau vulgarisation
23
- choix_vulgarisation = st.sidebar.pills(
24
- "Quel niveau de vulgarisation souhaitez-vous ? (1- Très vulgarisé 2-Intermédiaire 3-Technique)",
25
- ("1", "2", "3"),)
26
-
27
-
28
  #Années de publication
29
  choix_annee = st.sidebar.slider("Années de publication",
30
  min_value=2015,
31
  max_value=2025,
32
  value=(2020,2025))
33
-
34
- #endregion
35
-
36
- #region# Chargement du modèle
37
- mistral_token = os.getenv('api_mistral')
38
-
39
- @st.cache_data
40
- def load_model():
41
- return pipeline("text-generation", model="gpt2")
42
-
43
- model = load_model()
44
  #endregion
45
-
46
  #region# Interface utilisateur
47
- st.sidebar.header("")
48
  user_input = st.text_area("Entrez votre question:", placeholder="E.g., quelle est la meilleure alimentation pour les poules pondeuses ?")
49
-
50
  if st.button("Envoyer la question..."):
51
  if user_input and choix_prod and choix_vulgarisation and choix_annee :
52
  with st.spinner("Veuillez patienter quelques instants..."):
53
  # Génération de la réponse
54
- response = model(user_input, max_length=100, num_return_sequences=1)
55
- bot_response = response[0]['generated_text']
 
 
 
 
 
 
 
 
 
56
 
57
  # st.markdown("**Bot :** \\t " + bot_response)
58
  # Afficher un titre
59
  st.subheader("Réponse :")
60
-
61
  # Ajouter du texte Markdown avec un cadre
62
  st.markdown(f"""
63
  <div style="border: 2px solid #453103; padding: 15px; border-radius: 10px;">
64
  {bot_response}
65
  </div>
66
  """, unsafe_allow_html=True)
67
-
68
  # Afficher un titre
69
  st.subheader("Sources :")
70
-
71
  # Ajouter du texte Markdown avec un cadre
72
  st.markdown("""
73
  <div style="border: 2px solid #453103; padding: 15px; border-radius: 10px;">
74
- Sources
75
  </div>
76
  """, unsafe_allow_html=True)
77
-
78
  st.markdown("""
79
  <div style="border: 2px solid #453103; padding: 15px; border-radius: 10px;">
80
  Reviews
@@ -89,30 +276,30 @@ if st.button("Envoyer la question..."):
89
  if not choix_prod or not choix_vulgarisation or not choix_annee:
90
  st.warning("Veuillez compléter les paramètres dans le bandeau latéral de gauche!")
91
  #endregion
92
-
93
  # choix_prod, choix_vulgarisation, choix_annee
94
-
95
  #region# Markdown
96
  # Styles CSS pour améliorer l'apparence
97
  st.markdown("""
98
  <style>
99
-
100
  /* Changer la couleur de fond de l'ensemble de l'application */
101
  body {
102
  background-color: #feffb3; /* Couleur de fond bleu clair (par exemple) */
103
  color: #453103; /* Couleur de la police en gris foncé */
104
  }
105
-
106
  /* Personnaliser la couleur des titres */
107
  h1, h2, h3, h4, h5, h6 {
108
  color: #4CAF50; /* Changer la couleur des titres en vert */
109
  }
110
-
111
  /* Personnaliser la couleur des paragraphes */
112
  p {
113
  color: #555555; /* Couleur du texte dans les paragraphes */
114
  }
115
-
116
  .st-bx {
117
  background-color: #f1f1f1;
118
  border-radius: 10px;
@@ -127,8 +314,5 @@ st.markdown("""
127
  }
128
  </style>
129
  """, unsafe_allow_html=True)
130
-
131
-
132
-
133
-
134
- #endregion
 
1
  #region# import libs
2
  import streamlit as st
3
  import os
4
+ from mistralai import Mistral
5
+
6
+ MISTRAL_API_KEY = os.getenv("api_mistral")
7
+ model = 'mistral-large-latest'
8
+ mistral_client = Mistral(api_key=MISTRAL_API_KEY)
9
+ MAX_TOKENS = 1500
10
+
11
+ def generate_prompts(score:str, type: str, annee_min: str, annee_max:str ):
12
+ """
13
+ Genere les prefixes et suffixes des prompts pour Mistral en fonction du score de vulgarisation, du type d'espece, et les années des documents
14
+
15
+ Args:
16
+ score (str): 1 = vulgarisé, 2 = intermédiaire, 3 = technique
17
+ type (str): 'ponte' ou 'chair'
18
+ annee_min (str): annee min de publication
19
+ annee_max (str): annee max de publication
20
+
21
+ """
22
+
23
+ if type == "Ponte":
24
+ type_description = "volailles pondeuses"
25
+ elif type == "Chair":
26
+ type_description = "volailles de chair"
27
+ else:
28
+ raise ValueError("Type must be either 'Ponte' or 'Chair'")
29
+
30
+ if score == "1":
31
+ prefix_prompt = f"""Tu es un assistant IA spécialisé en nutrition de la volaille. Ton utilisateur est un chercheur travaillant sur l'amélioration des régimes
32
+ alimentaires pour optimiser la santé et la croissance des {type_description}.
33
+ Réponds en fournissant des explications claires et simples. N'oublie pas de citer à la fin de ta réponse les références sur
34
+ lesquelles tu t'es basé avec son année (entre {annee_min} et {annee_max})."""
35
+ suffix_prompt = """Réponds en français et donne une réponse directe et claire, donne les références de façon bibliographique."""
36
+ elif score == "2":
37
+ prefix_prompt = f"""Tu es un assistant IA spécialisé en nutrition de la volaille. Ton utilisateur est un chercheur travaillant sur l'amélioration des régimes
38
+ alimentaires pour optimiser la santé et la croissance des {type_description}.
39
+ Réponds en fournissant des explications détaillées et précises, adaptées à la complexité de la question posée.
40
+ N'oublie pas de citer à la fin de ta réponse les références sur lesquelles tu t'es basé avec son année (entre {annee_min} et {annee_max}). Sois concis et clair."""
41
+ suffix_prompt = """Réponds en français et en suivant cette structure :
42
+ Donne une explication scientifique détaillée (mécanismes biologiques, données chiffrées).
43
+ Fini par les études ou références bibliographiques si disponibles."""
44
+ elif score == "3":
45
+ prefix_prompt = f"""Tu es un assistant IA spécialisé en nutrition de la volaille. Ton utilisateur est un chercheur travaillant sur l'amélioration des régimes
46
+ alimentaires pour optimiser la santé et la croissance des {type_description}.
47
+ Réponds en fournissant des explications détaillées et précises, adaptées à la complexité de la question posée.
48
+ N'oublie pas de citer à la fin de ta réponse les références sur lesquelles tu t'es basé avec son année (entre {annee_min} et {annee_max}).
49
+ Inclus des formules, des valeurs nutritionnelles précises et des références aux normes actuelles. Sois concis et clair."""
50
+ suffix_prompt = """Réponds en français et en suivant cette structure : Explication scientifique détaillée (mécanismes biologiques, données chiffrées).
51
+ Études ou références bibliographiques si disponibles."""
52
+ else:
53
+ raise ValueError("Score must be 1, 2, or 3")
54
+
55
+ return prefix_prompt, suffix_prompt
56
+
57
+ def send_prompt_to_mistral(type_reponse: str, user_prompt: str, temperature:int, n_comp:int, prefix_prompt: str, suffix_prompt:str, verbose=True):
58
+ """
59
+ Envoie un prompt à Mistral pour obtenir une réponse
60
+
61
+ Args:
62
+ type_reponse (str): Le rôle de l'utilisateur, peut être 'technicien' ou 'chercheur'.
63
+ Si le rôle ne correspond pas à l'un de ces deux, une exception sera levée.
64
+ user_prompt (str): Le texte du prompt à envoyer à Mistral.
65
+ temperature (int): Lower values make the model more deterministic, focusing on likely responses for accuracy
66
+ verbose (bool): Print la reponse avant le return
67
+ prefixe (str): Prefixe du prompt
68
+ suffixe (str): Suffixe du prompt
69
+
70
+ Returns:
71
+ dict: La réponse du modèle Mistral à partir du prompt fourni.
72
+
73
+ Raises:
74
+ ValueError: Si le rôle spécifié n'est pas 'technicien' ou 'chercheur'.
75
+ """
76
+
77
+ # Création du message à envoyer à Mistral
78
+ messages = [{"role": type_reponse, "content": suffix_prompt}]
79
+
80
+ # Envoi de la requête à Mistral et récupération de la réponse
81
+ chat_response = mistral_client.chat.complete(
82
+ model = model,
83
+ messages=[
84
+ {"role": "system", "content": prefix_prompt},
85
+ {"role": "user", "content": user_prompt + suffix_prompt},
86
+ ],
87
+
88
+ #response_format={
89
+ # 'type': 'json_object'
90
+ #},
91
+ temperature=temperature,
92
+ n=n_comp,
93
+ max_tokens=MAX_TOKENS,
94
+ stream=False
95
+ #stop='\n'
96
+ )
97
+
98
+ if verbose:
99
+ print(chat_response)
100
+
101
+ return chat_response
102
+
103
+ def is_valid_mistral_response(response: dict) -> bool:
104
+ """
105
+ Vérifie si la réponse de l'API Mistral est valide.
106
+
107
+ Critères de validité :
108
+ - La clé "choices" existe et contient au moins un élément.
109
+ - Le texte généré n'est pas vide et ne contient pas uniquement des tabulations ou espaces.
110
+ - La génération ne s'est pas arrêtée pour une raison autre que 'stop'.
111
+ - La réponse ne contient pas de texte générique inutile.
112
+
113
+ :param response: Dictionnaire représentant la réponse de l'API Mistral
114
+ :return: True si la réponse est valide, False sinon
115
+ """
116
+ if not isinstance(response, dict):
117
+ return False
118
+
119
+ choices = response.get("choices")
120
+ if not choices or not isinstance(choices, list):
121
+ return False
122
+
123
+ first_choice = choices[0]
124
+ if not isinstance(first_choice, dict):
125
+ return False
126
+
127
+ text = first_choice.get("message", {}).get("content", "").strip()
128
+ if not text or text in ["\t\t", "", "N/A"]:
129
+ return False
130
+
131
+ finish_reason = first_choice.get("finish_reason", "")
132
+ if finish_reason in ["error", "tool_calls"]:
133
+ return False
134
+
135
+ # Vérification du contenu inutile
136
+ invalid_responses = [
137
+ "Je suis une IA", "Désolé, je ne peux pas répondre", "Je ne sais pas"
138
+ ]
139
+ if any(invalid in text for invalid in invalid_responses):
140
+ return False
141
+
142
+ return True
143
+
144
+ def print_pretty_response(response: dict, verbose=True):
145
+
146
+ pretty = response.choices[0].message.content
147
+
148
+ if verbose:
149
+ print(pretty)
150
+
151
+ return pretty
152
+
153
+ def response_details(response, verbose=True):
154
+ """
155
+ Envoie les details techniques du prompt
156
+ """
157
+
158
+ details = {}
159
+ details["id"] = response.id
160
+ details["total_tokens"] = response.usage.total_tokens
161
+ details["prefix"] = response.choices[0].message.prefix
162
+ details["role"] = response.choices[0].message.role
163
+
164
+ if verbose:
165
+ print(details)
166
+
167
+ return details
168
+
169
+ def prompt_pipeline(user_prompt: str, niveau_detail: str, type_reponse: str, souche: str, annee_publication_min: str, annee_publication_max: str):
170
+ """
171
+ Fonction visible de l'application pour appeler un prompt et obtenir sa reponse
172
+
173
+ Args:
174
+ prompt (str): Prompt utilisateur
175
+ niveau_detail (str): Niveau de detail de la requete : 1, 2, 3. Plus haut = plus d'infos
176
+ type_reponse (str): 'Ponte', 'Chair'
177
+ """
178
+
179
+ prefix_prompt, suffix_prompt = generate_prompts(score=niveau_detail, type=type_reponse, annee_min=annee_publication_min, annee_max=annee_publication_max)
180
+
181
+ reponse_mistral = send_prompt_to_mistral(
182
+ type_reponse=type_reponse,
183
+ user_prompt=user_prompt,
184
+ temperature=0.10,
185
+ n_comp=1,
186
+ verbose=False,
187
+ prefix_prompt=prefix_prompt,
188
+ suffix_prompt=suffix_prompt
189
+ )
190
+
191
+ to_return = {}
192
+ to_return["reponse_propre"] = print_pretty_response(reponse_mistral, verbose=True)
193
+ to_return["details"] = response_details(reponse_mistral, verbose=False)
194
+
195
+ return to_return
196
+
197
  #endregion
198
+
199
  #region# Titre de l'application et mise en page
200
  st.set_page_config(page_title="Expert nutrition volaille", page_icon="🤖")
201
  st.title("Chatbot AI avec l'expert nutrition")
202
  st.sidebar.image("img/avril_logo_rvb.jpg")
203
  st.sidebar.header("")
204
  #endregion
205
+
206
  #region# Elements graphiques
207
+
208
  #Choix production
209
  choix_prod = st.sidebar.pills(
210
  "Sur quelle espèce voulez-vous avoir des renseignements ?",
211
+ ("Ponte", "Chair"),)
212
+
 
 
 
 
 
 
213
  #Années de publication
214
  choix_annee = st.sidebar.slider("Années de publication",
215
  min_value=2015,
216
  max_value=2025,
217
  value=(2020,2025))
218
+
219
+ #Niveau vulgarisation
220
+ choix_vulgarisation = st.sidebar.pills(
221
+ "Quel niveau de vulgarisation souhaitez-vous ? (1- Très vulgarisé 2-Intermédiaire 3-Technique)",
222
+ ("1", "2", "3"),)
223
+
 
 
 
 
 
224
  #endregion
225
+
226
  #region# Interface utilisateur
 
227
  user_input = st.text_area("Entrez votre question:", placeholder="E.g., quelle est la meilleure alimentation pour les poules pondeuses ?")
228
+
229
  if st.button("Envoyer la question..."):
230
  if user_input and choix_prod and choix_vulgarisation and choix_annee :
231
  with st.spinner("Veuillez patienter quelques instants..."):
232
  # Génération de la réponse
233
+ response0 = prompt_pipeline(
234
+ user_prompt = user_input,
235
+ niveau_detail=choix_vulgarisation,
236
+ type_reponse=choix_prod,
237
+ souche=None,
238
+ annee_publication_max=max(choix_annee),
239
+ annee_publication_min=min(choix_annee)
240
+ )
241
+ print(response0["reponse_propre"])
242
+ response = response0["reponse_propre"]
243
+ bot_response = response
244
 
245
  # st.markdown("**Bot :** \\t " + bot_response)
246
  # Afficher un titre
247
  st.subheader("Réponse :")
248
+
249
  # Ajouter du texte Markdown avec un cadre
250
  st.markdown(f"""
251
  <div style="border: 2px solid #453103; padding: 15px; border-radius: 10px;">
252
  {bot_response}
253
  </div>
254
  """, unsafe_allow_html=True)
255
+
256
  # Afficher un titre
257
  st.subheader("Sources :")
258
+
259
  # Ajouter du texte Markdown avec un cadre
260
  st.markdown("""
261
  <div style="border: 2px solid #453103; padding: 15px; border-radius: 10px;">
 
262
  </div>
263
  """, unsafe_allow_html=True)
264
+
265
  st.markdown("""
266
  <div style="border: 2px solid #453103; padding: 15px; border-radius: 10px;">
267
  Reviews
 
276
  if not choix_prod or not choix_vulgarisation or not choix_annee:
277
  st.warning("Veuillez compléter les paramètres dans le bandeau latéral de gauche!")
278
  #endregion
279
+
280
  # choix_prod, choix_vulgarisation, choix_annee
281
+
282
  #region# Markdown
283
  # Styles CSS pour améliorer l'apparence
284
  st.markdown("""
285
  <style>
286
+
287
  /* Changer la couleur de fond de l'ensemble de l'application */
288
  body {
289
  background-color: #feffb3; /* Couleur de fond bleu clair (par exemple) */
290
  color: #453103; /* Couleur de la police en gris foncé */
291
  }
292
+
293
  /* Personnaliser la couleur des titres */
294
  h1, h2, h3, h4, h5, h6 {
295
  color: #4CAF50; /* Changer la couleur des titres en vert */
296
  }
297
+
298
  /* Personnaliser la couleur des paragraphes */
299
  p {
300
  color: #555555; /* Couleur du texte dans les paragraphes */
301
  }
302
+
303
  .st-bx {
304
  background-color: #f1f1f1;
305
  border-radius: 10px;
 
314
  }
315
  </style>
316
  """, unsafe_allow_html=True)
317
+
318
+ #endregion