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

Update pages/2_Chatbot_AR.py

Browse files
Files changed (1) hide show
  1. pages/2_Chatbot_AR.py +296 -296
pages/2_Chatbot_AR.py CHANGED
@@ -1,296 +1,296 @@
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_ar(data, r, col):
28
- """
29
- Trouve la question parente pour une ligne et colonne donnée dans le DataFrame (version AR).
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_ar(df, category, strat_id=0):
39
- """
40
- Crée un DataFrame avec questions-réponses contextuelles (version AR).
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_ar(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_ar(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_ar(excel_path: str, persist_dir: str = "./chroma_db_ar"):
93
- """
94
- Charge les données depuis plusieurs feuilles Excel (version AR),
95
- construit & stocke un Chroma VectorStore.
96
- """
97
- # 1. Charger les feuilles Excel
98
- qna_tree_ar0 = pd.read_excel(excel_path, sheet_name="Prépayé (AR)", skiprows=1).iloc[:, :5]
99
- qna_tree_ar1 = pd.read_excel(excel_path, sheet_name="Postpayé (AR)", skiprows=1).iloc[:, :5]
100
- qna_tree_ar2 = pd.read_excel(excel_path, sheet_name="Wifi (AR)", skiprows=1).iloc[:, :5]
101
-
102
- # 2. Construire le contexte
103
- context_ar0 = create_contextual_ar(qna_tree_ar0, "دفع مسبق", strat_id = 0)
104
- context_ar1 = create_contextual_ar(qna_tree_ar1, "دفع لاحق", strat_id = len(context_ar0))
105
- context_ar2 = create_contextual_ar(qna_tree_ar2, "واي فاي", strat_id = len(context_ar0) + len(context_ar1))
106
-
107
- # 3. Concaténer les DataFrame
108
- context_ar = pd.concat([context_ar0, context_ar1, context_ar2], axis=0)
109
-
110
- # 4. Créer une colonne "context"
111
- context_ar["context"] = context_ar.apply(
112
- lambda row: f"{row['question']} > {row['answer']}",
113
- axis=1
114
- )
115
-
116
- # 5. Convertir chaque ligne en Document
117
- documents_ar = [
118
- Document(
119
- page_content=row["context"],
120
- metadata={"id": row["id"], "category": row["category"]}
121
- )
122
- for _, row in context_ar.iterrows()
123
- ]
124
-
125
- # 6. Créer & persister le vecteur
126
- embedding_model_ar = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
127
- vectorstore_ar = Chroma.from_documents(documents_ar, embedding_model_ar, persist_directory=persist_dir)
128
- vectorstore_ar.persist()
129
-
130
- return vectorstore_ar
131
-
132
- def load_existing_vectorstore_ar(persist_dir: str = "./chroma_db_ar"):
133
- """
134
- Charge un VectorStore Chroma déjà stocké (version AR).
135
- """
136
- embedding_model_ar = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
137
- vectorstore_ar = Chroma(
138
- persist_directory=persist_dir,
139
- embedding_function=embedding_model_ar
140
- )
141
- return vectorstore_ar
142
-
143
- def retrieve_context_ar(retriever_ar, query, top_k=5):
144
- """
145
- Récupère les top_k résultats pour la question (version AR).
146
- """
147
- results_ar = retriever_ar.get_relevant_documents(query)
148
- context_ar_list = []
149
- for _, result in enumerate(results_ar[:top_k], start=1):
150
- context_ar_list.append(result.page_content)
151
- return context_ar_list
152
-
153
-
154
- #########################
155
- # 2. PROMPT & LLM (AR) #
156
- #########################
157
-
158
- prompt_template_ar = PromptTemplate(
159
- input_variables=["context", "query"],
160
- template=(
161
- """[SYSTEM]
162
- أنت مساعد لخدمة عملاء INWI، محترف وخبير ومتعاون. تتقن التعامل مع استفسارات ومشاكل العملاء.
163
- استند فقط إلى المعلومات المتوفرة في السياقات التالية دون اختراع معلومات غير موجودة:
164
- - استخدم تحية مهذبة وودّية، على سبيل المثال: "مرحباً، أنا المساعد الذكي من إنوي. كيف يمكنني خدمتك اليوم؟"
165
- - تعرّف على احتياج العميل واطلب التوضيح إذا لزم الأمر بالاعتماد على المعلومات المتوفرة فقط.
166
- - إن لم يكن السؤال ضمن سياق إنوي، أخبر العميل بلطف أنك غير قادر على الإجابة خارج سياق إنوي.
167
- - إذا لم تجد إجابة واضحة في السياق، يمكنك إبلاغ العميل بعدم توفر المعلومات واقتراح الاتصال بخدمة العملاء على الرقم 120.
168
- - احرص على أن تكون ردودك موجزة وفعالة. وتجنّب اختلاق أي تفاصيل غير موجودة في السياق.
169
- - أخبر العميل بأنه يمكنه التواصل معك مجدداً لمزيد من المساعدة.
170
- - لا تتحدث عن المنافسين الذين يقدمون نفس خدمات إنوي.
171
- - امتنع تماماً عن أي إهانة أو رد على إهانة.
172
- - لا تطلب أي معلومات شخصية أو هوية العميل.
173
- - وجّه العميل إلى كتالوج موقع إنوي إذا كان سؤاله يتعلق بعروض من الكتالوج.
174
- - قدّم حلولاً قياسية للمشكلات التقنية مع عرض الخيارات المتاحة.
175
- - قبل إرسال الجواب، تجنب أي تنسيق مثل "[Action] [نص]" واحتفظ فقط بالمعلومات المفيدة.
176
- - لا تتحدث عن المواضيع التالية إطلاقاً: [
177
- "السياسة", "الانتخابات", "الأحزاب", "الحكومة", "القوانين", "الإصلاحات",
178
- "الدين", "العقائد", "الممارسات الدينية", "علم اللاهوت",
179
- "الأخلاق", "الجدل", "الفلسفة", "المعايير", "التمييز",
180
- "المنافسة", "مقارنة إنوي مع شركات أخرى",
181
- "الأمن", "الاحتيال", "الصحة", "الأدوية", "التشخيص الطبي",
182
- "التمويل", "الاستثمار", "البورصة", "العملات الرقمية", "البنوك", "التأمين",
183
- "العنف", "الكراهية", "المحتوى الفاضح", "الجنس",
184
- "المخالفات القانونية", "الوثائق المزورة", "البث غير الشرعي"
185
- ]
186
- إنوي (INWI) هي شركة اتصالات مغربية تقدم خدمات الهاتف المحمول والإنترنت وحلول الاتصالات للأفراد والشركات.
187
- تتميز بالتزامها بتوفير خدمات عالية الجودة ومبتكرة، والمساهمة في التطور الرقمي في المغرب.
188
- العملاء هم أولويتنا، وهدفنا مساعدتهم وحل مشاكلهم.
189
- دورك هو تقديم خدمة عملاء احترافية وفعالة بدون اختراع معلومات من خارج السياق.
190
-
191
- [السياق]
192
- {context}
193
-
194
- [سؤال العميل]
195
- {query}
196
-
197
- [الإجابة]"""
198
- )
199
- )
200
-
201
- # Configuration du LLM HuggingFace (AR)
202
- os.environ["HUGGINGFACEHUB_API_TOKEN"]
203
- llm_ar = HuggingFaceHub(
204
- repo_id="MBZUAI-Paris/Atlas-Chat-9B",
205
- model_kwargs={
206
- "temperature": 0.5,
207
- "max_length": 500
208
- }
209
- )
210
-
211
- # Chaîne AR
212
- llm_chain_ar = LLMChain(llm=llm_ar, prompt=prompt_template_ar)
213
-
214
-
215
- #########################
216
- # 3. STREAMLIT MAIN APP #
217
- #########################
218
-
219
- def main():
220
- st.subheader("INWI IA Chatbot - Arabe")
221
-
222
- # Read local image and convert to Base64
223
- img_base64 = get_base64_of_bin_file("./img/logo inwi celeverlytics.png")
224
- css_logo = f"""
225
- <style>
226
- [data-testid="stSidebarNav"]::before {{
227
- content: "";
228
- display: block;
229
- margin: 0 auto 20px auto;
230
- width: 80%;
231
- height: 100px;
232
- background-image: url("data:image/png;base64,{img_base64}");
233
- background-size: contain;
234
- background-repeat: no-repeat;
235
- background-position: center;
236
- }}
237
- </style>
238
- """
239
-
240
- st.markdown(css_logo, unsafe_allow_html=True)
241
-
242
- if "retriever_ar" not in st.session_state:
243
- st.session_state["retriever_ar"] = None
244
-
245
- st.sidebar.subheader("Vector Store Options (AR)")
246
-
247
- if st.sidebar.button("Créer la Vector Store (AR)"):
248
- with st.spinner("Extraction et création de la vector store AR..."):
249
- excel_path = "Chatbot myinwi.xlsx"
250
- persist_directory_ar = "./chroma_db_ar"
251
- vectorstore_ar = load_excel_and_create_vectorstore_ar(
252
- excel_path=excel_path,
253
- persist_dir=persist_directory_ar
254
- )
255
- st.session_state["retriever_ar"] = vectorstore_ar.as_retriever(
256
- search_type="mmr",
257
- search_kwargs={"k": 5, "lambda_mult": 0.5}
258
- )
259
- st.success("Vector store FR créée et chargée avec succès !")
260
-
261
- if st.sidebar.button("Charger la Vector Store existante (AR)"):
262
- with st.spinner("Chargement de la vector store FR existante..."):
263
- persist_directory_ar = "./chroma_db_ar"
264
- vectorstore_ar = load_existing_vectorstore_ar(persist_directory_ar)
265
- st.session_state["retriever_ar"] = vectorstore_ar.as_retriever(
266
- search_type="mmr",
267
- search_kwargs={"k": 5, "lambda_mult": 0.5}
268
- )
269
- st.success("Vector store AR chargée avec succès !")
270
-
271
- st.write("""مرحباً! أنا هنا للإجابة على جميع أسئلتك المتعلقة بخدمات إنوي
272
- وعروض الهاتف المحمول والإنترنت، وأي حلول أخرى قد تناسب احتياجاتك (AR).""")
273
-
274
- user_query_ar = st.chat_input("Posez votre question ici (AR)...")
275
-
276
- if user_query_ar:
277
- if not st.session_state["retriever_ar"]:
278
- st.warning("Veuillez d'abord créer ou charger la Vector Store (AR).")
279
- return
280
-
281
- # Récupération du contexte
282
- context_ar_list = retrieve_context_ar(st.session_state["retriever_ar"], user_query_ar, top_k=5)
283
-
284
- if context_ar_list:
285
- with st.spinner("Génération de la réponse..."):
286
- response_ar = llm_chain_ar.run({"context": "\n".join(context_ar_list), "query": user_query_ar})
287
- response_ar = response_ar.split("[الإجابة]")[-1]
288
- st.write("**سؤال العميل:**")
289
- st.write(user_query_ar)
290
- st.write("**الإجابة:**")
291
- st.write(response_ar)
292
- else:
293
- st.write("Aucun contexte trouvé pour cette question. Essayez autre chose.")
294
-
295
- if __name__ == "__main__":
296
- main()
 
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_ar(data, r, col):
28
+ """
29
+ Trouve la question parente pour une ligne et colonne donnée dans le DataFrame (version AR).
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_ar(df, category, strat_id=0):
39
+ """
40
+ Crée un DataFrame avec questions-réponses contextuelles (version AR).
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_ar(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_ar(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_ar(excel_path: str, persist_dir: str = "./chroma_db_ar"):
93
+ """
94
+ Charge les données depuis plusieurs feuilles Excel (version AR),
95
+ construit & stocke un Chroma VectorStore.
96
+ """
97
+ # 1. Charger les feuilles Excel
98
+ qna_tree_ar0 = pd.read_excel(excel_path, sheet_name="Prépayé (AR)", skiprows=1).iloc[:, :5]
99
+ qna_tree_ar1 = pd.read_excel(excel_path, sheet_name="Postpayé (AR)", skiprows=1).iloc[:, :5]
100
+ qna_tree_ar2 = pd.read_excel(excel_path, sheet_name="Wifi (AR)", skiprows=1).iloc[:, :5]
101
+
102
+ # 2. Construire le contexte
103
+ context_ar0 = create_contextual_ar(qna_tree_ar0, "دفع مسبق", strat_id = 0)
104
+ context_ar1 = create_contextual_ar(qna_tree_ar1, "دفع لاحق", strat_id = len(context_ar0))
105
+ context_ar2 = create_contextual_ar(qna_tree_ar2, "واي فاي", strat_id = len(context_ar0) + len(context_ar1))
106
+
107
+ # 3. Concaténer les DataFrame
108
+ context_ar = pd.concat([context_ar0, context_ar1, context_ar2], axis=0)
109
+
110
+ # 4. Créer une colonne "context"
111
+ context_ar["context"] = context_ar.apply(
112
+ lambda row: f"{row['question']} > {row['answer']}",
113
+ axis=1
114
+ )
115
+
116
+ # 5. Convertir chaque ligne en Document
117
+ documents_ar = [
118
+ Document(
119
+ page_content=row["context"],
120
+ metadata={"id": row["id"], "category": row["category"]}
121
+ )
122
+ for _, row in context_ar.iterrows()
123
+ ]
124
+
125
+ # 6. Créer & persister le vecteur
126
+ embedding_model_ar = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
127
+ vectorstore_ar = Chroma.from_documents(documents_ar, embedding_model_ar, persist_directory=persist_dir)
128
+ vectorstore_ar.persist()
129
+
130
+ return vectorstore_ar
131
+
132
+ def load_existing_vectorstore_ar(persist_dir: str = "./chroma_db_ar"):
133
+ """
134
+ Charge un VectorStore Chroma déjà stocké (version AR).
135
+ """
136
+ embedding_model_ar = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
137
+ vectorstore_ar = Chroma(
138
+ persist_directory=persist_dir,
139
+ embedding_function=embedding_model_ar
140
+ )
141
+ return vectorstore_ar
142
+
143
+ def retrieve_context_ar(retriever_ar, query, top_k=5):
144
+ """
145
+ Récupère les top_k résultats pour la question (version AR).
146
+ """
147
+ results_ar = retriever_ar.get_relevant_documents(query)
148
+ context_ar_list = []
149
+ for _, result in enumerate(results_ar[:top_k], start=1):
150
+ context_ar_list.append(result.page_content)
151
+ return context_ar_list
152
+
153
+
154
+ #########################
155
+ # 2. PROMPT & LLM (AR) #
156
+ #########################
157
+
158
+ prompt_template_ar = PromptTemplate(
159
+ input_variables=["context", "query"],
160
+ template=(
161
+ """[SYSTEM]
162
+ أنت مساعد لخدمة عملاء INWI، محترف وخبير ومتعاون. تتقن التعامل مع استفسارات ومشاكل العملاء.
163
+ استند فقط إلى المعلومات المتوفرة في السياقات التالية دون اختراع معلومات غير موجودة:
164
+ - استخدم تحية مهذبة وودّية، على سبيل المثال: "مرحباً، أنا المساعد الذكي من إنوي. كيف يمكنني خدمتك اليوم؟"
165
+ - تعرّف على احتياج العميل واطلب التوضيح إذا لزم الأمر بالاعتماد على المعلومات المتوفرة فقط.
166
+ - إن لم يكن السؤال ضمن سياق إنوي، أخبر العميل بلطف أنك غير قادر على الإجابة خارج سياق إنوي.
167
+ - إذا لم تجد إجابة واضحة في السياق، يمكنك إبلاغ العميل بعدم توفر المعلومات واقتراح الاتصال بخدمة العملاء على الرقم 120.
168
+ - احرص على أن تكون ردودك موجزة وفعالة. وتجنّب اختلاق أي تفاصيل غير موجودة في السياق.
169
+ - أخبر العميل بأنه يمكنه التواصل معك مجدداً لمزيد من المساعدة.
170
+ - لا تتحدث عن المنافسين الذين يقدمون نفس خدمات إنوي.
171
+ - امتنع تماماً عن أي إهانة أو رد على إهانة.
172
+ - لا تطلب أي معلومات شخصية أو هوية العميل.
173
+ - وجّه العميل إلى كتالوج موقع إنوي إذا كان سؤاله يتعلق بعروض من الكتالوج.
174
+ - قدّم حلولاً قياسية للمشكلات التقنية مع عرض الخيارات المتاحة.
175
+ - قبل إرسال الجواب، تجنب أي تنسيق مثل "[Action] [نص]" واحتفظ فقط بالمعلومات المفيدة.
176
+ - لا تتحدث عن المواضيع التالية إطلاقاً: [
177
+ "السياسة", "الانتخابات", "الأحزاب", "الحكومة", "القوانين", "الإصلاحات",
178
+ "الدين", "العقائد", "الممارسات الدينية", "علم اللاهوت",
179
+ "الأخلاق", "الجدل", "الفلسفة", "المعايير", "التمييز",
180
+ "المنافسة", "مقارنة إنوي مع شركات أخرى",
181
+ "الأمن", "الاحتيال", "الصحة", "الأدوية", "التشخيص الطبي",
182
+ "التمويل", "الاستثمار", "البورصة", "العملات الرقمية", "البنوك", "التأمين",
183
+ "العنف", "الكراهية", "المحتوى الفاضح", "الجنس",
184
+ "المخالفات القانونية", "الوثائق المزورة", "البث غير الشرعي"
185
+ ]
186
+ إنوي (INWI) هي شركة اتصالات مغربية تقدم خدمات الهاتف المحمول والإنترنت وحلول الاتصالات للأفراد والشركات.
187
+ تتميز بالتزامها بتوفير خدمات عالية الجودة ومبتكرة، والمساهمة في التطور الرقمي في المغرب.
188
+ العملاء هم أولويتنا، وهدفنا مساعدتهم وحل مشاكلهم.
189
+ دورك هو تقديم خدمة عملاء احترافية وفعالة بدون اختراع معلومات من خارج السياق.
190
+
191
+ [السياق]
192
+ {context}
193
+
194
+ [سؤال العميل]
195
+ {query}
196
+
197
+ [الإجابة]"""
198
+ )
199
+ )
200
+
201
+ # Configuration du LLM HuggingFace (AR)
202
+ os.environ["HUGGINGFACEHUB_API"]
203
+ llm_ar = HuggingFaceHub(
204
+ repo_id="MBZUAI-Paris/Atlas-Chat-9B",
205
+ model_kwargs={
206
+ "temperature": 0.5,
207
+ "max_length": 500
208
+ }
209
+ )
210
+
211
+ # Chaîne AR
212
+ llm_chain_ar = LLMChain(llm=llm_ar, prompt=prompt_template_ar)
213
+
214
+
215
+ #########################
216
+ # 3. STREAMLIT MAIN APP #
217
+ #########################
218
+
219
+ def main():
220
+ st.subheader("INWI IA Chatbot - Arabe")
221
+
222
+ # Read local image and convert to Base64
223
+ img_base64 = get_base64_of_bin_file("./img/logo inwi celeverlytics.png")
224
+ css_logo = f"""
225
+ <style>
226
+ [data-testid="stSidebarNav"]::before {{
227
+ content: "";
228
+ display: block;
229
+ margin: 0 auto 20px auto;
230
+ width: 80%;
231
+ height: 100px;
232
+ background-image: url("data:image/png;base64,{img_base64}");
233
+ background-size: contain;
234
+ background-repeat: no-repeat;
235
+ background-position: center;
236
+ }}
237
+ </style>
238
+ """
239
+
240
+ st.markdown(css_logo, unsafe_allow_html=True)
241
+
242
+ if "retriever_ar" not in st.session_state:
243
+ st.session_state["retriever_ar"] = None
244
+
245
+ st.sidebar.subheader("Vector Store Options (AR)")
246
+
247
+ if st.sidebar.button("Créer la Vector Store (AR)"):
248
+ with st.spinner("Extraction et création de la vector store AR..."):
249
+ excel_path = "Chatbot myinwi.xlsx"
250
+ persist_directory_ar = "./chroma_db_ar"
251
+ vectorstore_ar = load_excel_and_create_vectorstore_ar(
252
+ excel_path=excel_path,
253
+ persist_dir=persist_directory_ar
254
+ )
255
+ st.session_state["retriever_ar"] = vectorstore_ar.as_retriever(
256
+ search_type="mmr",
257
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
258
+ )
259
+ st.success("Vector store FR créée et chargée avec succès !")
260
+
261
+ if st.sidebar.button("Charger la Vector Store existante (AR)"):
262
+ with st.spinner("Chargement de la vector store FR existante..."):
263
+ persist_directory_ar = "./chroma_db_ar"
264
+ vectorstore_ar = load_existing_vectorstore_ar(persist_directory_ar)
265
+ st.session_state["retriever_ar"] = vectorstore_ar.as_retriever(
266
+ search_type="mmr",
267
+ search_kwargs={"k": 5, "lambda_mult": 0.5}
268
+ )
269
+ st.success("Vector store AR chargée avec succès !")
270
+
271
+ st.write("""مرحباً! أنا هنا للإجابة على جميع أسئلتك المتعلقة بخدمات إنوي
272
+ وعروض الهاتف المحمول والإنترنت، وأي حلول أخرى قد تناسب احتياجاتك (AR).""")
273
+
274
+ user_query_ar = st.chat_input("Posez votre question ici (AR)...")
275
+
276
+ if user_query_ar:
277
+ if not st.session_state["retriever_ar"]:
278
+ st.warning("Veuillez d'abord créer ou charger la Vector Store (AR).")
279
+ return
280
+
281
+ # Récupération du contexte
282
+ context_ar_list = retrieve_context_ar(st.session_state["retriever_ar"], user_query_ar, top_k=5)
283
+
284
+ if context_ar_list:
285
+ with st.spinner("Génération de la réponse..."):
286
+ response_ar = llm_chain_ar.run({"context": "\n".join(context_ar_list), "query": user_query_ar})
287
+ response_ar = response_ar.split("[الإجابة]")[-1]
288
+ st.write("**سؤال العميل:**")
289
+ st.write(user_query_ar)
290
+ st.write("**الإجابة:**")
291
+ st.write(response_ar)
292
+ else:
293
+ st.write("Aucun contexte trouvé pour cette question. Essayez autre chose.")
294
+
295
+ if __name__ == "__main__":
296
+ main()