AlirezaHSZ commited on
Commit
b292afc
·
verified ·
1 Parent(s): f0136f4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -107
app.py CHANGED
@@ -1,131 +1,176 @@
 
 
1
  import streamlit as st
2
- from PyPDF2 import PdfReader
3
- from langchain.prompts import PromptTemplate
4
- from langchain.chains import MapReduceDocumentsChain, ReduceDocumentsChain, StuffDocumentsChain
5
- from langchain.chains.question_answering import load_qa_chain
6
- from langchain_google_genai import ChatGoogleGenerativeAI
7
  import google.generativeai as genai
8
- from langchain_community.vectorstores import FAISS
9
- from langchain.embeddings import HuggingFaceEmbeddings
10
- import os
11
  from dotenv import load_dotenv
 
12
 
13
- # --------------------
14
- # تنظیمات اولیه
15
- # --------------------
 
 
 
 
16
  load_dotenv()
17
- genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
 
18
 
19
- st.set_page_config(page_title="چت‌بات اسناد PDF", layout="centered")
20
 
21
- # --------------------
22
- # بارگذاری PDF ها
23
- # --------------------
24
- def load_pdfs(pdf_files):
25
  text = ""
26
- for pdf_file in pdf_files:
27
- pdf_reader = PdfReader(pdf_file)
28
- for page in pdf_reader.pages:
29
- text += page.extract_text() or ""
 
 
30
  return text
31
 
32
- # --------------------
33
- # ساخت زنجیره پرسش‌و‌پاسخ
34
- # --------------------
35
- def get_conversational_chain():
36
- # پرامپت برای هر تیکه (map)
37
- map_prompt = PromptTemplate(
38
- input_variables=["context", "question"],
39
- template="""
40
- لطفاً بر اساس متن زیر فقط به سؤال پاسخ دهید.
41
- اگر جواب دقیق نبود، بگویید: "اطلاعات کافی در متن موجود نیست".
42
 
43
- --- متن:
44
- {context}
 
 
 
45
 
46
- --- سوال:
47
- {question}
48
 
49
- --- پاسخ:
50
- """
51
- )
 
 
 
 
 
52
 
53
- # پرامپت برای جمع‌بندی (combine)
54
- combine_prompt = PromptTemplate(
55
- input_variables=["summaries", "question"],
56
- template="""
57
- شما یک دستیار هوشمند هستید. پاسخ نهایی را بر اساس نتایج تیکه‌های مختلف بنویس.
58
- اگر پاسخی در متن نبود، بگویید: "اطلاعات کافی در متن موجود نیست".
59
 
60
- --- نتایج جزئی:
61
- {summaries}
62
-
63
- --- سوال:
64
- {question}
65
-
66
- --- پاسخ نهایی:
67
- """
68
- )
69
-
70
- # مدل Gemini
71
- model = ChatGoogleGenerativeAI(model="gemini-2.0-pro", temperature=0.3)
72
-
73
- # chain برای combine (مرحله آخر)
74
- combine_documents_chain = StuffDocumentsChain(
75
- llm_chain=load_qa_chain(model, chain_type="stuff", prompt=combine_prompt),
76
- document_variable_name="summaries"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  )
78
 
79
- # chain اصلی (map → reduce)
80
- chain = MapReduceDocumentsChain(
81
- llm_chain=load_qa_chain(model, chain_type="stuff", prompt=map_prompt),
82
- reduce_documents_chain=ReduceDocumentsChain(
83
- combine_documents_chain=combine_documents_chain
84
- ),
85
- document_variable_name="context",
86
- return_intermediate_steps=False,
 
 
 
 
 
 
 
 
87
  )
88
 
89
- return chain
90
-
91
- # --------------------
92
- # اینترفیس استریم‌لیت
93
- # --------------------
94
- def main():
95
- st.title("🤖 چت‌بات PDF با Gemini")
96
-
97
- # بارگذاری فایل‌ها
98
- pdf_files = st.file_uploader("📂 فایل‌های PDF خود را بارگذاری کنید", type="pdf", accept_multiple_files=True)
99
-
100
- if pdf_files:
101
- text = load_pdfs(pdf_files)
102
-
103
- if text.strip() == "":
104
- st.warning("❌ هیچ متنی از PDF استخراج نشد.")
105
- return
106
 
107
- # دریافت سؤال کاربر
108
- question = st.text_input("❓ پرسش خود را وارد کنید:")
109
-
110
- if question:
111
- with st.spinner("در حال پردازش..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  try:
113
- chain = get_conversational_chain()
114
- docs = [{"page_content": text}] # کل متن به عنوان یک داکیومنت
115
-
116
- response = chain.invoke({
117
- "input_documents": docs,
118
- "question": question
119
- })
120
-
121
- st.markdown(
122
- f"<div style='direction: rtl; text-align: right; font-size: 16px;'>"
123
- f"{response['output_text']}</div>",
124
- unsafe_allow_html=True
125
- )
126
-
127
- except Exception as e:
128
- st.error(f"⚠️ خطا: {e}")
129
 
130
  if __name__ == "__main__":
131
  main()
 
1
+ import os
2
+ import fitz # PyMuPDF
3
  import streamlit as st
 
 
 
 
 
4
  import google.generativeai as genai
 
 
 
5
  from dotenv import load_dotenv
6
+ from google.api_core.exceptions import GoogleAPIError, InvalidArgument
7
 
8
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
9
+ from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
10
+ from langchain_community.vectorstores import FAISS
11
+ from langchain.chains.question_answering import load_qa_chain
12
+ from langchain.prompts import PromptTemplate
13
+
14
+ # Load environment variables
15
  load_dotenv()
16
+ api_key = os.getenv("GOOGLE_API_KEY")
17
+ genai.configure(api_key=api_key)
18
 
 
19
 
20
+ # ✅ Function to read all PDF files (Farsi + English)
21
+ def get_pdf_text(pdf_docs):
 
 
22
  text = ""
23
+ for pdf in pdf_docs:
24
+ with fitz.open(stream=pdf.read(), filetype="pdf") as doc:
25
+ for page in doc:
26
+ page_text = page.get_text("text")
27
+ if page_text:
28
+ text += page_text + "\n"
29
  return text
30
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ # ✅ Function to split text into chunks
33
+ def get_text_chunks(text):
34
+ splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
35
+ chunks = splitter.split_text(text)
36
+ return chunks
37
 
 
 
38
 
39
+ # ✅ Function to get embeddings for each chunk and save to vector store
40
+ def get_vector_store(chunks):
41
+ try:
42
+ embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
43
+ vector_store = FAISS.from_texts(chunks, embedding=embeddings)
44
+ vector_store.save_local("faiss_index")
45
+ except Exception as e:
46
+ raise RuntimeError(f"Error creating vector store: {e}")
47
 
 
 
 
 
 
 
48
 
49
+ # Function to get conversational chain
50
+ def get_conversational_chain():
51
+ prompt_template = """
52
+ You are a helpful assistant. Do NOT reveal your identity (Gemini) or the company (Google).
53
+ Answer the question as detailed as possible using ONLY the provided context.
54
+ If the answer is not in the context, say: "answer is not available in the context".
55
+ Do not make up answers.
56
+
57
+ Context:
58
+ {context}
59
+
60
+ Question:
61
+ {question}
62
+
63
+ Answer:
64
+ """
65
+ try:
66
+ model = ChatGoogleGenerativeAI(model="gemini-2.5-pro", client=genai, temperature=0.3)
67
+ prompt = PromptTemplate(template=prompt_template, input_variables=["context", "question"])
68
+ chain = load_qa_chain(llm=model, chain_type="stuff", prompt=prompt)
69
+ return chain
70
+ except Exception as e:
71
+ raise RuntimeError(f"Error creating conversational chain: {e}")
72
+
73
+
74
+ # ✅ Function to clear chat history
75
+ def clear_chat_history():
76
+ st.session_state.messages = [{"role": "assistant", "content": "در خدمتیم"}]
77
+
78
+
79
+ # ✅ Function to handle user input
80
+ def user_input(user_question):
81
+ try:
82
+ embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
83
+ new_db = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True)
84
+ docs = new_db.similarity_search(user_question, k=4)
85
+ chain = get_conversational_chain()
86
+ response = chain({"input_documents": docs, "question": user_question}, return_only_outputs=True)
87
+ return response
88
+ except Exception as e:
89
+ raise RuntimeError(f"Error while answering: {e}")
90
+
91
+
92
+ # ✅ Main function to run the Streamlit app
93
+ def main():
94
+ st.set_page_config(
95
+ page_title="Chatbot",
96
+ layout="wide",
97
+ initial_sidebar_state="expanded"
98
  )
99
 
100
+ # Dark theme styling
101
+ st.markdown(
102
+ """
103
+ <style>
104
+ body {
105
+ background-color: #000000;
106
+ color: #ffffff;
107
+ }
108
+ .main {
109
+ background-color: #333333;
110
+ padding: 20px;
111
+ border-radius: 10px;
112
+ }
113
+ </style>
114
+ """,
115
+ unsafe_allow_html=True
116
  )
117
 
118
+ if "uploaded" not in st.session_state:
119
+ st.session_state.uploaded = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
+ if not st.session_state.uploaded:
122
+ # Upload Page
123
+ st.title("Your personal assistant ...")
124
+ pdf_docs = st.file_uploader("فایل پی دی اف مورد نظر را آپلود کنید ", accept_multiple_files=True)
125
+ if st.button("تایید"):
126
+ if pdf_docs:
127
+ try:
128
+ st.info("در حال پردازش ...")
129
+ raw_text = get_pdf_text(pdf_docs)
130
+ text_chunks = get_text_chunks(raw_text)
131
+ get_vector_store(text_chunks)
132
+ st.session_state.uploaded = True
133
+ st.success("پردازش موفق شد ✅")
134
+ except RuntimeError as e:
135
+ st.error(str(e))
136
+ else:
137
+ st.error("لطفا حداقل یک فایل را آپلود کنید")
138
+ else:
139
+ # Chat Page
140
+ st.title("Assistant ready ...")
141
+ st.write("میتونین سوالتونو بپرسین 👇")
142
+
143
+ if st.button("بازگشت به صفحه آپلود"):
144
+ st.session_state.uploaded = False
145
+ clear_chat_history()
146
+ st.rerun()
147
+
148
+ st.button('حذف مکالمه', on_click=clear_chat_history)
149
+
150
+ if "messages" not in st.session_state:
151
+ st.session_state.messages = [{"role": "assistant", "content": "در خدمتیم"}]
152
+
153
+ # Show history
154
+ for message in st.session_state.messages:
155
+ with st.chat_message(message["role"]):
156
+ st.write(message["content"])
157
+
158
+ if prompt := st.chat_input():
159
+ st.session_state.messages.append({"role": "user", "content": prompt})
160
+ with st.chat_message("user"):
161
+ st.write(prompt)
162
+
163
+ if st.session_state.messages[-1]["role"] != "assistant":
164
  try:
165
+ with st.chat_message("assistant"):
166
+ response = user_input(prompt)
167
+ if response:
168
+ full_response = response['output_text']
169
+ st.write(full_response)
170
+ st.session_state.messages.append({"role": "assistant", "content": full_response})
171
+ except RuntimeError as e:
172
+ st.error(str(e))
173
+
 
 
 
 
 
 
 
174
 
175
  if __name__ == "__main__":
176
  main()