abdelom commited on
Commit
28abcb2
·
verified ·
1 Parent(s): bcafeea

Update pages/1_Chatbot_FR.py

Browse files
Files changed (1) hide show
  1. pages/1_Chatbot_FR.py +305 -304
pages/1_Chatbot_FR.py CHANGED
@@ -1,304 +1,305 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import os
4
- from pathlib import Path
5
- import base64
6
-
7
- # LangChain & Hugging Face
8
- from langchain.embeddings import HuggingFaceEmbeddings
9
- from langchain.vectorstores import Chroma
10
- from langchain.schema import Document
11
- from langchain.prompts import PromptTemplate
12
- from langchain.llms import HuggingFaceHub
13
- from langchain.chains import LLMChain
14
-
15
- import pysqlite3
16
- import sys
17
- sys.modules["sqlite3"] = pysqlite3
18
-
19
- #####################
20
- # 1. HELPER FUNCTIONS
21
- #####################
22
-
23
- def get_base64_of_bin_file(bin_file_path: str) -> str:
24
- file_bytes = Path(bin_file_path).read_bytes()
25
- return base64.b64encode(file_bytes).decode()
26
-
27
- def find_parent_fr(data, r, col):
28
- """
29
- Trouve la question parente pour une ligne et colonne donnée dans le DataFrame (version FR).
30
- """
31
- i = r - 1
32
- parent = None
33
- while i >= 0 and pd.isna(parent):
34
- parent = data.iloc[i, col]
35
- i -= 1
36
- return parent
37
-
38
- def create_contextual_fr(df, category, strat_id=0):
39
- """
40
- Crée un DataFrame avec questions-réponses contextuelles (version FR).
41
- """
42
- rows = []
43
- columns_qna = list(df.columns)
44
-
45
- for r, row in df.iterrows():
46
- for level, col in enumerate(df.columns):
47
- question = row[col]
48
- if pd.isna(question):
49
- continue
50
-
51
- # Si la question est un "leaf node"
52
- if level == 4 or pd.isna(row[columns_qna[level + 1]]):
53
- # Gérer des sous-questions multiples
54
- if "\n*Si" in question or "\n *" in question or "\n*" in question:
55
- questions = question.replace("\n*Si", "\n*").replace("\n *", "\n*").split("\n*")
56
- for subquestion in questions:
57
- if len(subquestion.strip()) == 0:
58
- continue
59
-
60
- context = []
61
- for i in range(level - 1, -1, -1):
62
- parent = df.iloc[r, i]
63
- if pd.isna(parent):
64
- parent = find_parent_fr(df, r, i)
65
- if pd.notna(parent):
66
- context = [parent] + context
67
-
68
- rows.append({
69
- "id": strat_id + len(rows) + 1,
70
- "question": " > ".join(context),
71
- "answer": subquestion.strip(),
72
- "category": category,
73
- })
74
- else:
75
- context = []
76
- for i in range(level - 1, -1, -1):
77
- parent = df.iloc[r, i]
78
- if pd.isna(parent):
79
- parent = find_parent_fr(df, r, i)
80
- if pd.notna(parent):
81
- context = [parent] + context
82
-
83
- rows.append({
84
- "id": strat_id + len(rows) + 1,
85
- "question": " > ".join(context),
86
- "answer": question.strip(),
87
- "category": category,
88
- })
89
-
90
- return pd.DataFrame(rows)
91
-
92
- def load_excel_and_create_vectorstore_fr(excel_path: str, persist_dir: str = "./chroma_db_fr"):
93
- """
94
- Charge les données depuis plusieurs feuilles Excel (version FR),
95
- construit & stocke un Chroma VectorStore.
96
- """
97
- # 1. Charger les feuilles Excel
98
- qna_tree_fr0 = pd.read_excel(excel_path, sheet_name="Prépayé (FR)", skiprows=1).iloc[:, :5]
99
- qna_tree_fr1 = pd.read_excel(excel_path, sheet_name="Postpayé (FR)", skiprows=1).iloc[:, :5]
100
- qna_tree_fr2 = pd.read_excel(excel_path, sheet_name="Wifi (FR)", skiprows=1).iloc[:, :5]
101
-
102
- # 2. Construire le contexte
103
- context_fr0 = create_contextual_fr(qna_tree_fr0, "Prépayé", strat_id = 0)
104
- context_fr1 = create_contextual_fr(qna_tree_fr1, "Postpayé", strat_id = len(context_fr0))
105
- context_fr2 = create_contextual_fr(qna_tree_fr2, "Wifi", strat_id = len(context_fr0) + len(context_fr1))
106
-
107
- # 3. Concaténer les DataFrame
108
- context_fr = pd.concat([context_fr0, context_fr1, context_fr2], axis=0)
109
-
110
- # 4. Créer une colonne "context"
111
- context_fr["context"] = context_fr.apply(
112
- lambda row: f"{row['question']} > {row['answer']}",
113
- axis=1
114
- )
115
-
116
- # 5. Convertir chaque ligne en Document
117
- documents_fr = [
118
- Document(
119
- page_content=row["context"],
120
- metadata={"id": row["id"], "category": row["category"]}
121
- )
122
- for _, row in context_fr.iterrows()
123
- ]
124
-
125
- # 6. Créer & persister le vecteur
126
- embedding_model_fr = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
127
- vectorstore_fr = Chroma.from_documents(documents_fr, embedding_model_fr, persist_directory=persist_dir)
128
- vectorstore_fr.persist()
129
-
130
- return vectorstore_fr
131
-
132
- def load_existing_vectorstore_fr(persist_dir: str = "./chroma_db_fr"):
133
- """
134
- Charge un VectorStore Chroma déjà stocké (version FR).
135
- """
136
- embedding_model_fr = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
137
- vectorstore_fr = Chroma(
138
- persist_directory=persist_dir,
139
- embedding_function=embedding_model_fr
140
- )
141
- return vectorstore_fr
142
-
143
- def retrieve_context_fr(retriever_fr, query, top_k=5):
144
- """
145
- Récupère les top_k résultats pour la question (version FR).
146
- """
147
- results_fr = retriever_fr.get_relevant_documents(query)
148
- context_fr_list = []
149
- for _, result in enumerate(results_fr[:top_k], start=1):
150
- context_fr_list.append(result.page_content)
151
- return context_fr_list
152
-
153
-
154
- #########################
155
- # 2. PROMPT & LLM FR #
156
- #########################
157
-
158
- prompt_template_fr = PromptTemplate(
159
- input_variables=["context", "query"],
160
- template=(
161
- """[SYSTEM]
162
- Vous êtes un assistant client professionnel, expérimenté et bienveillant pour l'opérateur téléphonique INWI.
163
- Vous excellez dans la gestion des clients, en répondant à leurs problèmes et questions.
164
- Fournir un service client et des conseils en se basant sur les contextes fournis :
165
- - Répondre aux salutations de manière courtoise et amicale, par exemple : "Je suis l'assistant IA d'INWI'. Comment puis-je vous aider aujourd'hui ?"
166
- - Identifier le besoin du client et demander des clarifications si nécessaire, tout en s'appuyant uniquement sur le contexte.
167
- - Si la question n'est pas liée au contexte d'INWI, veuillez informer poliment que vous ne pouvez pas répondre à des questions hors contexte INWI.
168
- - Si la réponse ne figure pas dans le contexte, vous pouvez dire "Je n'ai pas assez d'information" et proposer d'appeler le service client au 120.
169
- - Structurer les réponses de manière concise et efficace. Et n'inventez pas d'infos non présentes dans le contexte.
170
- - Informer le client qu’il peut vous recontacter pour toute assistance supplémentaire.
171
- - Ne parlez pas des concurrents qui offrent la meme service d'INWI.
172
- - Ne jamais insulter ou répondre à une insulte.
173
- - Ne demandez pas d’informations personnelles ou d’identification du client.
174
- - Orientez vers le catalogue sur le site web INWI si la question concerne une offre du catalogue.
175
- - Donnez des solutions standard pour les problèmes techniques avec des options.
176
- - Avant de générer votre réponse, éliminez toutes les structures comme '[Action] [texte]' et gardez uniquement les informations utiles.
177
- - Ne jamais parler des sujets suivants : [
178
- "politique", "élections", "partis", "gouvernement", "lois", "réformes",
179
- "religion", "croyances", "pratiques religieuses", "théologie",
180
- "moralité", "débat", "philosophie", "éthique", "discrimination",
181
- "concurrence", "Maroc Telecom", "IAM", "Orange", "comparaison",
182
- "sécurité", "fraude", "santé", "médicaments", "traitement", "diagnostic", "maladie",
183
- "finance", "investissement", "bourse", "crypto", "banque", "assurance",
184
- "violence", "haine", "contenu explicite", "sexe", "adultes",
185
- "illégal", "faux documents", "streaming illégal"
186
- ]
187
- INWI est un opérateur de télécommunications marocain offrant des services mobiles, Internet et solutions de télécommunications
188
- pour les particuliers et les entreprises. Il se distingue par son engagement à fournir des services de qualité, innovants et
189
- accessibles, tout en contribuant au développement numérique du pays.
190
- Les clients sont notre priorité, et notre but est de résoudre leurs problèmes.
191
- Votre rôle est de fournir un service client professionnel et efficace sans inventer d'informations.
192
-
193
- [CONTEXTE]
194
- {context}
195
-
196
- [QUESTION DU CLIENT]
197
- {query}
198
-
199
- [RÉPONSE]"""
200
- )
201
- )
202
-
203
- # Configuration du LLM HuggingFace (FR)
204
- os.environ["HUGGINGFACEHUB_API"]
205
- llm_fr = HuggingFaceHub(
206
- repo_id="mistralai/Mistral-7B-Instruct-v0.3",
207
- model_kwargs={
208
- "temperature": 0.5,
209
- "max_length": 500
210
- }
211
- )
212
-
213
- # Chaîne FR
214
- llm_chain_fr = LLMChain(llm=llm_fr, prompt=prompt_template_fr)
215
-
216
-
217
- #########################
218
- # 3. STREAMLIT MAIN APP #
219
- #########################
220
-
221
- def main():
222
- st.subheader("INWI IA Chatbot - Français")
223
-
224
- # Read local image and convert to Base64
225
- img_base64 = get_base64_of_bin_file("./img/logo inwi celeverlytics.png")
226
- css_logo = f"""
227
- <style>
228
- [data-testid="stSidebarNav"]::before {{
229
- content: "";
230
- display: block;
231
- margin: 0 auto 20px auto;
232
- width: 80%;
233
- height: 100px;
234
- background-image: url("data:image/png;base64,{img_base64}");
235
- background-size: contain;
236
- background-repeat: no-repeat;
237
- background-position: center;
238
- }}
239
- </style>
240
- """
241
-
242
- st.markdown(css_logo, unsafe_allow_html=True)
243
-
244
- # Charger ou créer le retriever
245
- if "retriever_fr" not in st.session_state:
246
- st.session_state["retriever_fr"] = None
247
-
248
- st.sidebar.header("Vector Store Options (FR)")
249
-
250
- if st.sidebar.button("Créer la Vector Store (FR)"):
251
- with st.spinner("Extraction et création de la vector store FR..."):
252
- excel_path = "Chatbot myinwi.xlsx"
253
- persist_directory_fr = "./chroma_db_fr"
254
- vectorstore_fr = load_excel_and_create_vectorstore_fr(
255
- excel_path=excel_path,
256
- persist_dir=persist_directory_fr
257
- )
258
- st.session_state["retriever_fr"] = vectorstore_fr.as_retriever(
259
- search_type="mmr",
260
- search_kwargs={"k": 5, "lambda_mult": 0.5}
261
- )
262
- st.success("Vector store FR créée et chargée avec succès !")
263
-
264
- if st.sidebar.button("Charger la Vector Store existante (FR)"):
265
- with st.spinner("Chargement de la vector store FR existante..."):
266
- persist_directory_fr = "./chroma_db_fr"
267
- vectorstore_fr = load_existing_vectorstore_fr(persist_directory_fr)
268
- st.session_state["retriever_fr"] = vectorstore_fr.as_retriever(
269
- search_type="mmr",
270
- search_kwargs={"k": 5, "lambda_mult": 0.5}
271
- )
272
- st.success("Vector store FR chargée avec succès !")
273
-
274
- st.write("""Je suis là pour répondre à toutes vos questions concernant nos
275
- services, nos offres mobiles et Internet, ainsi que nos solutions adaptées à vos besoins (FR).""")
276
-
277
- # Zone de texte
278
- user_query_fr = st.chat_input("Posez votre question ici (FR)...")
279
-
280
- if user_query_fr:
281
- if not st.session_state["retriever_fr"]:
282
- st.warning("Veuillez d'abord créer ou charger la Vector Store (FR).")
283
- return
284
-
285
- # Récupération du contexte
286
- context_fr_list = retrieve_context_fr(st.session_state["retriever_fr"], user_query_fr, top_k=5)
287
-
288
- if context_fr_list:
289
- with st.spinner("Génération de la réponse..."):
290
- response_fr = llm_chain_fr.run({"context": "\n".join(context_fr_list), "query": user_query_fr})
291
- # Séparer si jamais le prompt contient [RÉPONSE], sinon on affiche tout
292
- response_fr = response_fr.split("[RÉPONSE]")[-1]
293
- st.write("**Question :**")
294
- st.write(user_query_fr)
295
- st.write("**Réponse :**")
296
- st.write(response_fr)
297
- else:
298
- st.write("Aucun contexte trouvé pour cette question. Essayez autre chose.")
299
-
300
-
301
- if __name__ == "__main__":
302
- main()
303
-
304
-
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import os
4
+ from pathlib import Path
5
+ import base64
6
+
7
+ # LangChain & Hugging Face
8
+ from langchain.embeddings import HuggingFaceEmbeddings
9
+ from langchain.vectorstores import Chroma
10
+ from langchain.schema import Document
11
+ from langchain.prompts import PromptTemplate
12
+ from langchain.llms import HuggingFaceHub
13
+ from langchain.chains import LLMChain
14
+
15
+ import pysqlite3
16
+ import sys
17
+ sys.modules["sqlite3"] = pysqlite3
18
+
19
+ #####################
20
+ # 1. HELPER FUNCTIONS
21
+ #####################
22
+
23
+ def get_base64_of_bin_file(bin_file_path: str) -> str:
24
+ file_bytes = Path(bin_file_path).read_bytes()
25
+ return base64.b64encode(file_bytes).decode()
26
+
27
+ def find_parent_fr(data, r, col):
28
+ """
29
+ Trouve la question parente pour une ligne et colonne donnée dans le DataFrame (version FR).
30
+ """
31
+ i = r - 1
32
+ parent = None
33
+ while i >= 0 and pd.isna(parent):
34
+ parent = data.iloc[i, col]
35
+ i -= 1
36
+ return parent
37
+
38
+ def create_contextual_fr(df, category, strat_id=0):
39
+ """
40
+ Crée un DataFrame avec questions-réponses contextuelles (version FR).
41
+ """
42
+ rows = []
43
+ columns_qna = list(df.columns)
44
+
45
+ for r, row in df.iterrows():
46
+ for level, col in enumerate(df.columns):
47
+ question = row[col]
48
+ if pd.isna(question):
49
+ continue
50
+
51
+ # Si la question est un "leaf node"
52
+ if level == 4 or pd.isna(row[columns_qna[level + 1]]):
53
+ # Gérer des sous-questions multiples
54
+ if "\n*Si" in question or "\n *" in question or "\n*" in question:
55
+ questions = question.replace("\n*Si", "\n*").replace("\n *", "\n*").split("\n*")
56
+ for subquestion in questions:
57
+ if len(subquestion.strip()) == 0:
58
+ continue
59
+
60
+ context = []
61
+ for i in range(level - 1, -1, -1):
62
+ parent = df.iloc[r, i]
63
+ if pd.isna(parent):
64
+ parent = find_parent_fr(df, r, i)
65
+ if pd.notna(parent):
66
+ context = [parent] + context
67
+
68
+ rows.append({
69
+ "id": strat_id + len(rows) + 1,
70
+ "question": " > ".join(context),
71
+ "answer": subquestion.strip(),
72
+ "category": category,
73
+ })
74
+ else:
75
+ context = []
76
+ for i in range(level - 1, -1, -1):
77
+ parent = df.iloc[r, i]
78
+ if pd.isna(parent):
79
+ parent = find_parent_fr(df, r, i)
80
+ if pd.notna(parent):
81
+ context = [parent] + context
82
+
83
+ rows.append({
84
+ "id": strat_id + len(rows) + 1,
85
+ "question": " > ".join(context),
86
+ "answer": question.strip(),
87
+ "category": category,
88
+ })
89
+
90
+ return pd.DataFrame(rows)
91
+
92
+ def load_excel_and_create_vectorstore_fr(excel_path: str, persist_dir: str = "./chroma_db_fr"):
93
+ """
94
+ Charge les données depuis plusieurs feuilles Excel (version FR),
95
+ construit & stocke un Chroma VectorStore.
96
+ """
97
+ # 1. Charger les feuilles Excel
98
+ qna_tree_fr0 = pd.read_excel(excel_path, sheet_name="Prépayé (FR)", skiprows=1).iloc[:, :5]
99
+ qna_tree_fr1 = pd.read_excel(excel_path, sheet_name="Postpayé (FR)", skiprows=1).iloc[:, :5]
100
+ qna_tree_fr2 = pd.read_excel(excel_path, sheet_name="Wifi (FR)", skiprows=1).iloc[:, :5]
101
+
102
+ # 2. Construire le contexte
103
+ context_fr0 = create_contextual_fr(qna_tree_fr0, "Prépayé", strat_id = 0)
104
+ context_fr1 = create_contextual_fr(qna_tree_fr1, "Postpayé", strat_id = len(context_fr0))
105
+ context_fr2 = create_contextual_fr(qna_tree_fr2, "Wifi", strat_id = len(context_fr0) + len(context_fr1))
106
+
107
+ # 3. Concaténer les DataFrame
108
+ context_fr = pd.concat([context_fr0, context_fr1, context_fr2], axis=0)
109
+
110
+ # 4. Créer une colonne "context"
111
+ context_fr["context"] = context_fr.apply(
112
+ lambda row: f"{row['question']} > {row['answer']}",
113
+ axis=1
114
+ )
115
+
116
+ # 5. Convertir chaque ligne en Document
117
+ documents_fr = [
118
+ Document(
119
+ page_content=row["context"],
120
+ metadata={"id": row["id"], "category": row["category"]}
121
+ )
122
+ for _, row in context_fr.iterrows()
123
+ ]
124
+
125
+ # 6. Créer & persister le vecteur
126
+ embedding_model_fr = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
127
+ vectorstore_fr = Chroma.from_documents(documents_fr, embedding_model_fr, persist_directory=persist_dir)
128
+ vectorstore_fr.persist()
129
+
130
+ return vectorstore_fr
131
+
132
+ def load_existing_vectorstore_fr(persist_dir: str = "./chroma_db_fr"):
133
+ """
134
+ Charge un VectorStore Chroma déjà stocké (version FR).
135
+ """
136
+ embedding_model_fr = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
137
+ vectorstore_fr = Chroma(
138
+ persist_directory=persist_dir,
139
+ embedding_function=embedding_model_fr
140
+ )
141
+ return vectorstore_fr
142
+
143
+ def retrieve_context_fr(retriever_fr, query, top_k=5):
144
+ """
145
+ Récupère les top_k résultats pour la question (version FR).
146
+ """
147
+ results_fr = retriever_fr.get_relevant_documents(query)
148
+ context_fr_list = []
149
+ for _, result in enumerate(results_fr[:top_k], start=1):
150
+ context_fr_list.append(result.page_content)
151
+ return context_fr_list
152
+
153
+
154
+ #########################
155
+ # 2. PROMPT & LLM FR #
156
+ #########################
157
+
158
+ prompt_template_fr = PromptTemplate(
159
+ input_variables=["context", "query"],
160
+ template=(
161
+ """[SYSTEM]
162
+ Vous êtes un assistant client professionnel, expérimenté et bienveillant pour l'opérateur téléphonique INWI.
163
+ Vous excellez dans la gestion des clients, en répondant à leurs problèmes et questions.
164
+ Fournir un service client et des conseils en se basant sur les contextes fournis :
165
+ - Répondre aux salutations de manière courtoise et amicale, par exemple : "Je suis l'assistant IA d'INWI'. Comment puis-je vous aider aujourd'hui ?"
166
+ - Identifier le besoin du client et demander des clarifications si nécessaire, tout en s'appuyant uniquement sur le contexte.
167
+ - Si la question n'est pas liée au contexte d'INWI, veuillez informer poliment que vous ne pouvez pas répondre à des questions hors contexte INWI.
168
+ - Si la réponse ne figure pas dans le contexte, vous pouvez dire "Je n'ai pas assez d'information" et proposer d'appeler le service client au 120.
169
+ - Structurer les réponses de manière concise et efficace. Et n'inventez pas d'infos non présentes dans le contexte.
170
+ - Informer le client qu’il peut vous recontacter pour toute assistance supplémentaire.
171
+ - Ne parlez pas des concurrents qui offrent la meme service d'INWI.
172
+ - Si la réponse n'exist pas dans le context explicitement ne repons pas.
173
+ - Ne jamais insulter ou répondre à une insulte.
174
+ - Ne demandez pas d’informations personnelles ou d’identification du client.
175
+ - Orientez vers le catalogue sur le site web INWI si la question concerne une offre du catalogue.
176
+ - Donnez des solutions standard pour les problèmes techniques avec des options.
177
+ - Avant de générer votre réponse, éliminez toutes les structures comme '[Action] [texte]' et gardez uniquement les informations utiles.
178
+ - Ne jamais parler des sujets suivants : [
179
+ "politique", "élections", "partis", "gouvernement", "lois", "réformes",
180
+ "religion", "croyances", "pratiques religieuses", "théologie",
181
+ "moralité", "débat", "philosophie", "éthique", "discrimination",
182
+ "concurrence", "Maroc Telecom", "IAM", "Orange", "comparaison",
183
+ "sécurité", "fraude", "santé", "médicaments", "traitement", "diagnostic", "maladie",
184
+ "finance", "investissement", "bourse", "crypto", "banque", "assurance",
185
+ "violence", "haine", "contenu explicite", "sexe", "adultes",
186
+ "illégal", "faux documents", "streaming illégal"
187
+ ]
188
+ INWI est un opérateur de télécommunications marocain offrant des services mobiles, Internet et solutions de télécommunications
189
+ pour les particuliers et les entreprises. Il se distingue par son engagement à fournir des services de qualité, innovants et
190
+ accessibles, tout en contribuant au développement numérique du pays.
191
+ Les clients sont notre priorité, et notre but est de résoudre leurs problèmes.
192
+ Votre rôle est de fournir un service client professionnel et efficace sans inventer d'informations.
193
+
194
+ [CONTEXTE]
195
+ {context}
196
+
197
+ [QUESTION DU CLIENT]
198
+ {query}
199
+
200
+ [RÉPONSE]"""
201
+ )
202
+ )
203
+
204
+ # Configuration du LLM HuggingFace (FR)
205
+ os.environ["HUGGINGFACEHUB_API"]
206
+ llm_fr = HuggingFaceHub(
207
+ repo_id="mistralai/Mistral-7B-Instruct-v0.3",
208
+ model_kwargs={
209
+ "temperature": 0.5,
210
+ "max_length": 500
211
+ }
212
+ )
213
+
214
+ # Chaîne FR
215
+ llm_chain_fr = LLMChain(llm=llm_fr, prompt=prompt_template_fr)
216
+
217
+
218
+ #########################
219
+ # 3. STREAMLIT MAIN APP #
220
+ #########################
221
+
222
+ def main():
223
+ st.subheader("INWI IA Chatbot - Français")
224
+
225
+ # Read local image and convert to Base64
226
+ img_base64 = get_base64_of_bin_file("./img/logo inwi celeverlytics.png")
227
+ css_logo = f"""
228
+ <style>
229
+ [data-testid="stSidebarNav"]::before {{
230
+ content: "";
231
+ display: block;
232
+ margin: 0 auto 20px auto;
233
+ width: 80%;
234
+ height: 100px;
235
+ background-image: url("data:image/png;base64,{img_base64}");
236
+ background-size: contain;
237
+ background-repeat: no-repeat;
238
+ background-position: center;
239
+ }}
240
+ </style>
241
+ """
242
+
243
+ st.markdown(css_logo, unsafe_allow_html=True)
244
+
245
+ # Charger ou créer le retriever
246
+ if "retriever_fr" not in st.session_state:
247
+ st.session_state["retriever_fr"] = None
248
+
249
+ st.sidebar.header("Vector Store Options (FR)")
250
+
251
+ if st.sidebar.button("Créer la Vector Store (FR)"):
252
+ with st.spinner("Extraction et création de la vector store FR..."):
253
+ excel_path = "Chatbot myinwi.xlsx"
254
+ persist_directory_fr = "./chroma_db_fr"
255
+ vectorstore_fr = load_excel_and_create_vectorstore_fr(
256
+ excel_path=excel_path,
257
+ persist_dir=persist_directory_fr
258
+ )
259
+ st.session_state["retriever_fr"] = vectorstore_fr.as_retriever(
260
+ search_type="mmr",
261
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
262
+ )
263
+ st.success("Vector store FR créée et chargée avec succès !")
264
+
265
+ if st.sidebar.button("Charger la Vector Store existante (FR)"):
266
+ with st.spinner("Chargement de la vector store FR existante..."):
267
+ persist_directory_fr = "./chroma_db_fr"
268
+ vectorstore_fr = load_existing_vectorstore_fr(persist_directory_fr)
269
+ st.session_state["retriever_fr"] = vectorstore_fr.as_retriever(
270
+ search_type="mmr",
271
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
272
+ )
273
+ st.success("Vector store FR chargée avec succès !")
274
+
275
+ st.write("""Je suis pour répondre à toutes vos questions concernant nos
276
+ services, nos offres mobiles et Internet, ainsi que nos solutions adaptées à vos besoins (FR).""")
277
+
278
+ # Zone de texte
279
+ user_query_fr = st.chat_input("Posez votre question ici (FR)...")
280
+
281
+ if user_query_fr:
282
+ if not st.session_state["retriever_fr"]:
283
+ st.warning("Veuillez d'abord créer ou charger la Vector Store (FR).")
284
+ return
285
+
286
+ # Récupération du contexte
287
+ context_fr_list = retrieve_context_fr(st.session_state["retriever_fr"], user_query_fr, top_k=5)
288
+
289
+ if context_fr_list:
290
+ with st.spinner("Génération de la réponse..."):
291
+ response_fr = llm_chain_fr.run({"context": "\n".join(context_fr_list), "query": user_query_fr})
292
+ # Séparer si jamais le prompt contient [RÉPONSE], sinon on affiche tout
293
+ response_fr = response_fr.split("[RÉPONSE]")[-1]
294
+ st.write("**Question :**")
295
+ st.write(user_query_fr)
296
+ st.write("**Réponse :**")
297
+ st.write(response_fr)
298
+ else:
299
+ st.write("Aucun contexte trouvé pour cette question. Essayez autre chose.")
300
+
301
+
302
+ if __name__ == "__main__":
303
+ main()
304
+
305
+