M17idd commited on
Commit
dab4d10
·
verified ·
1 Parent(s): 33a09b2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -118
app.py CHANGED
@@ -181,10 +181,12 @@ st.markdown('<div class="chat-message">👋 سلام! چطور میتونم کم
181
 
182
 
183
 
184
- # ----------------- لود csv و ساخت ایندکس -----------------
185
 
186
 
187
- # --- Embedding Class ---
 
 
 
188
  class TogetherEmbeddings(Embeddings):
189
  def __init__(self, model_name: str, api_key: str):
190
  self.model_name = model_name
@@ -202,123 +204,145 @@ class TogetherEmbeddings(Embeddings):
202
  def embed_query(self, text: str) -> List[float]:
203
  return self.embed_documents([text])[0]
204
 
205
- # --- Load CSV and Create Index ---
 
206
  @st.cache_resource
207
- def get_csv_index(csv_file):
208
- with st.spinner('📄 در حال پردازش فایل CSV...'):
209
- df = pd.read_csv(csv_file)
210
- texts = df.iloc[:, 0].astype(str).tolist()
211
- texts = [text for text in texts if text.strip()]
212
-
213
- text_splitter = RecursiveCharacterTextSplitter(
214
- chunk_size=2048,
215
- chunk_overlap=256,
216
- length_function=len,
217
- separators=["\n\n", "\n", " ", ""]
218
- )
219
-
220
- split_texts = []
221
- for text in texts:
222
- split_texts.extend(text_splitter.split_text(text))
223
-
224
- documents = [Document(page_content=text) for text in split_texts]
225
-
226
- embeddings = TogetherEmbeddings(
227
- model_name="togethercomputer/m2-bert-80M-32k-retrieval",
228
- api_key="0291f33aee03412a47fa5d8e562e515182dcc5d9aac5a7fb5eefdd1759005979"
229
- )
230
-
231
- vectorstore = FAISS.from_documents(documents, embeddings)
232
- return vectorstore, embeddings
233
-
234
- # --- Load CSV ---
235
- csv_file_path = 'output (1).csv'
236
-
237
- try:
238
- vectorstore, embedding_model = get_csv_index(csv_file_path)
239
- except Exception as e:
240
- st.error(f"خطا در ساخت ایندکس: {str(e)}")
241
- st.stop()
242
-
243
- # --- Load LLM ---
244
- llm = ChatOpenAI(
245
- base_url="https://api.together.xyz/v1",
246
- api_key='0291f33aee03412a47fa5d8e562e515182dcc5d9aac5a7fb5eefdd1759005979',
247
- model="Qwen/Qwen3-235B-A22B-fp8-tput"
248
- )
249
-
250
- # --- Chat UI ---
251
- if 'messages' not in st.session_state:
252
- st.session_state.messages = []
253
-
254
- if 'pending_prompt' not in st.session_state:
255
- st.session_state.pending_prompt = None
256
-
257
- # نمایش پیام‌های قبلی
258
- for msg in st.session_state.messages:
259
- with st.chat_message(msg['role']):
260
- st.markdown(f"🗨️ {msg['content']}", unsafe_allow_html=True)
261
-
262
- # ورودی جدید کاربر
263
- prompt = st.chat_input("چطور می‌تونم کمک کنم؟")
264
-
265
- if prompt:
266
- st.session_state.messages.append({'role': 'user', 'content': prompt})
267
- st.session_state.pending_prompt = prompt
268
- st.rerun()
269
-
270
- # پردازش سوال
271
- if st.session_state.pending_prompt:
272
- with st.chat_message('ai'):
273
- thinking = st.empty()
274
- thinking.markdown("🤖 در حال فکر کردن...")
275
-
276
- try:
277
- # امبد کردن سوال
278
- query = st.session_state.pending_prompt
279
- query_embedding = embedding_model.embed_query(query)
280
-
281
- # بازیابی اسناد مشابه
282
- docs = vectorstore.similarity_search_by_vector(query_embedding, k=4)
283
- context = "\n".join([doc.page_content for doc in docs])
284
-
285
- # ساخت پرامپت نهایی برای LLM
286
- final_prompt = f"""با توجه به اطلاعات زیر، بهترین جواب رو فقط از داخل این اطلاعات به زبان فارسی بده. لطفاً از خارج از این اطلاعات استفاده نکن و اگر اطلاعات کافی برای جواب دادن نبود، بهترین پاسخ رو بده.
287
-
288
- 🔹 اطلاعات:\n{context}\n\n❓ سؤال: {query}
289
  """
290
 
291
 
292
- # ارسال به LLM
293
- response = llm.invoke(final_prompt)
294
- raw_answer = response.content.strip()
295
-
296
- # فیلتر کردن خطوط غیرپاسخ (مثلاً <think> یا توضیح مدل)
297
- answer_lines = raw_answer.split('\n')
298
- filtered_lines = [
299
- line for line in answer_lines
300
- if not line.strip().startswith("<")
301
- and not line.strip().lower().startswith("think")
302
- and not line.strip().startswith("#")
303
- and not line.strip().lower().startswith("note")
304
- ]
305
- clean_answer = "\n".join(filtered_lines).strip()
306
- if not clean_answer:
307
- clean_answer = "متأسفم، اطلاعات دقیقی در این مورد ندارم."
308
-
309
- # تایپ کردن تدریجی پاسخ
310
- thinking.empty()
311
- full_response = ""
312
- placeholder = st.empty()
313
- for word in clean_answer.split():
314
- full_response += word + " "
315
- placeholder.markdown(full_response + "▌")
316
- time.sleep(0.03)
317
-
318
- placeholder.markdown(full_response)
319
- st.session_state.messages.append({'role': 'ai', 'content': full_response})
320
- st.session_state.pending_prompt = None
321
-
322
- except Exception as e:
323
- thinking.empty()
324
- st.error(f"خطا در پاسخ‌دهی: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
 
183
 
 
184
 
185
 
186
+
187
+
188
+
189
+ # ----------- تعریف کلاس امبدینگ با Together -----------
190
  class TogetherEmbeddings(Embeddings):
191
  def __init__(self, model_name: str, api_key: str):
192
  self.model_name = model_name
 
204
  def embed_query(self, text: str) -> List[float]:
205
  return self.embed_documents([text])[0]
206
 
207
+
208
+ # ----------- پردازش و ایندکس کردن CSV -----------
209
  @st.cache_resource
210
+ def build_vectorstore_from_csv(csv_file_path: str):
211
+ df = pd.read_csv(csv_file_path)
212
+ texts = df.iloc[:, 0].astype(str).tolist()
213
+ texts = [text.strip() for text in texts if text.strip()]
214
+
215
+ # برش متن‌ها
216
+ text_splitter = RecursiveCharacterTextSplitter(
217
+ chunk_size=2048,
218
+ chunk_overlap=256,
219
+ length_function=len,
220
+ separators=["\n\n", "\n", " ", ""]
221
+ )
222
+ split_texts = []
223
+ for text in texts:
224
+ split_texts.extend(text_splitter.split_text(text))
225
+
226
+ documents = [Document(page_content=text) for text in split_texts]
227
+
228
+ embeddings = TogetherEmbeddings(
229
+ model_name="togethercomputer/m2-bert-80M-32k-retrieval",
230
+ api_key="0291f33aee03412a47fa5d8e562e515182dcc5d9aac5a7fb5eefdd1759005979"
231
+ )
232
+
233
+ vectorstore = FAISS.from_documents(documents, embeddings)
234
+ return vectorstore, embeddings
235
+
236
+
237
+ # ----------- بارگذاری مدل زبانی -----------
238
+ def load_llm():
239
+ return ChatOpenAI(
240
+ base_url="https://api.together.xyz/v1",
241
+ api_key='0291f33aee03412a47fa5d8e562e515182dcc5d9aac5a7fb5eefdd1759005979',
242
+ model="Qwen/Qwen3-235B-A22B-fp8-tput"
243
+ )
244
+
245
+
246
+ # ----------- ساخت پرامپت نهایی برای LLM -----------
247
+ def build_prompt(context: str, user_question: str) -> str:
248
+ return f"""با توجه به اطلاعات زیر، فقط بر اساس آن‌ها به سؤال پاسخ بده. اگر اطلاعات کافی نیست، بگو اطلاعات کافی ندارم.
249
+ 🔹 اطلاعات:\n{context}\n\n❓ سؤال: {user_question}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  """
251
 
252
 
253
+ # ----------- تمیز کردن خروجی مدل از پاسخ‌های اضافی -----------
254
+ def clean_llm_response(response_text: str) -> str:
255
+ lines = response_text.split('\n')
256
+ filtered = [
257
+ line for line in lines
258
+ if not line.strip().startswith("<")
259
+ and not line.strip().lower().startswith(("think", "note", "#"))
260
+ ]
261
+ return "\n".join(filtered).strip() or "متأسفم، اطلاعات دقیقی در این مورد ندارم."
262
+
263
+
264
+ # ----------- پردازش سوال و بازیابی پاسخ‌ها -----------
265
+ def process_user_query(query: str, vectorstore, embedding_model, llm):
266
+ # 1. ساخت embedding از سوال
267
+ query_embedding = embedding_model.embed_query(query)
268
+
269
+ # 2. پیدا کردن 3 پاسخ مشابه با cosine similarity
270
+ docs = vectorstore.similarity_search_by_vector(query_embedding, k=3)
271
+ context = "\n".join([doc.page_content for doc in docs])
272
+
273
+ # 3. ساخت پرامپت نهایی با استفاده از پاسخ‌های مشابه به عنوان context
274
+ final_prompt = build_prompt(context, query)
275
+
276
+ # 4. ارسال پرامپت به LLM و دریافت پاسخ
277
+ response = llm.invoke(final_prompt)
278
+ raw_answer = response.content.strip()
279
+
280
+ # 5. تمیز کردن و نمایش پاسخ نهایی
281
+ clean_answer = clean_llm_response(raw_answer)
282
+ return clean_answer
283
+
284
+
285
+ # ----------- اجرای Streamlit UI -----------
286
+ def run_chat_ui():
287
+ st.title("💬 دستیار هوشمند متنی بر اساس فایل CSV")
288
+
289
+ # بارگذاری ایندکس
290
+ csv_file_path = 'output (1).csv'
291
+ try:
292
+ vectorstore, embedding_model = build_vectorstore_from_csv(csv_file_path)
293
+ except Exception as e:
294
+ st.error(f"خطا در پردازش فایل: {str(e)}")
295
+ st.stop()
296
+
297
+ llm = load_llm()
298
+
299
+ if 'messages' not in st.session_state:
300
+ st.session_state.messages = []
301
+
302
+ if 'pending_prompt' not in st.session_state:
303
+ st.session_state.pending_prompt = None
304
+
305
+ # نمایش پیام‌های قبلی
306
+ for msg in st.session_state.messages:
307
+ with st.chat_message(msg['role']):
308
+ st.markdown(f"{msg['content']}", unsafe_allow_html=True)
309
+
310
+ # گرفتن سوال جدید
311
+ user_prompt = st.chat_input("سوال خود را وارد کنید...")
312
+
313
+ if user_prompt:
314
+ st.session_state.messages.append({'role': 'user', 'content': user_prompt})
315
+ st.session_state.pending_prompt = user_prompt
316
+ st.rerun()
317
+
318
+ # پردازش سوال
319
+ if st.session_state.pending_prompt:
320
+ with st.chat_message("ai"):
321
+ thinking = st.empty()
322
+ thinking.markdown("🤖 در حال فکر کردن...")
323
+
324
+ try:
325
+ # پردازش سوال و دریافت پاسخ نهایی
326
+ query = st.session_state.pending_prompt
327
+ clean_answer = process_user_query(query, vectorstore, embedding_model, llm)
328
+
329
+ thinking.empty()
330
+ full_response = ""
331
+ placeholder = st.empty()
332
+ for word in clean_answer.split():
333
+ full_response += word + " "
334
+ placeholder.markdown(full_response + "▌")
335
+ time.sleep(0.03)
336
+
337
+ placeholder.markdown(full_response)
338
+ st.session_state.messages.append({'role': 'ai', 'content': full_response})
339
+ st.session_state.pending_prompt = None
340
+
341
+ except Exception as e:
342
+ thinking.empty()
343
+ st.error(f"خطا در پردازش مدل: {str(e)}")
344
+
345
+
346
+ # اجرای برنامه
347
+ if __name__ == "__main__":
348
+ run_chat_ui()