AlirezaHSZ commited on
Commit
8d577a4
·
verified ·
1 Parent(s): 701f497

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -109
app.py CHANGED
@@ -3,20 +3,21 @@ import fitz # PyMuPDF
3
  import streamlit as st
4
  import google.generativeai as genai
5
  from dotenv import load_dotenv
 
6
 
7
  from langchain.text_splitter import RecursiveCharacterTextSplitter
8
  from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
9
  from langchain_community.vectorstores import FAISS
 
10
  from langchain.prompts import PromptTemplate
11
- from langchain.chains import LLMChain
12
- from langchain.chains import MapReduceDocumentsChain, ReduceDocumentsChain, StuffDocumentsChain
13
 
14
- # ========== Config ==========
15
  load_dotenv()
16
  api_key = os.getenv("GOOGLE_API_KEY")
17
  genai.configure(api_key=api_key)
18
 
19
- # ========== PDF to text (fa+en) ==========
 
20
  def get_pdf_text(pdf_docs):
21
  text = ""
22
  for pdf in pdf_docs:
@@ -27,12 +28,15 @@ def get_pdf_text(pdf_docs):
27
  text += page_text + "\n"
28
  return text
29
 
30
- # ========== Chunking ==========
 
31
  def get_text_chunks(text):
32
- splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
33
- return splitter.split_text(text)
 
 
34
 
35
- # ========== Build & save vector store ==========
36
  def get_vector_store(chunks):
37
  try:
38
  embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
@@ -41,124 +45,65 @@ def get_vector_store(chunks):
41
  except Exception as e:
42
  raise RuntimeError(f"Error creating vector store: {e}")
43
 
44
- # ========== Map-Reduce QA chain (بدون load_qa_chain) ==========
45
- from langchain.prompts import PromptTemplate
46
- from langchain.chains import LLMChain
47
- from langchain.chains import MapReduceDocumentsChain, ReduceDocumentsChain, StuffDocumentsChain
48
- from langchain_google_genai import ChatGoogleGenerativeAI
49
 
 
50
  def get_conversational_chain():
51
- # مرحله Map: خلاصه یا نِکات مرتبط از هر تکه
52
- map_prompt_tmpl = PromptTemplate(
53
- template=(
54
- "You are a helpful assistant. Extract a concise summary strictly from the context "
55
- "that would help answer the final question. Do NOT add outside info.\n\n"
56
- "Context:\n{context}\n\n"
57
- "Question:\n{question}\n\n"
58
- "Summary:"
59
- ),
60
- input_variables=["context", "question"],
61
- )
62
-
63
- # مرحله Combine: پاسخ نهایی فقط بر اساس خلاصه‌ها
64
- combine_prompt_tmpl = PromptTemplate(
65
- template=(
66
- "You are a helpful assistant. Answer the question using ONLY the provided summaries. "
67
- "If the answer is not in the summaries, say: \"answer is not available in the context\".\n\n"
68
- "Summaries:\n{summaries}\n\n"
69
- "Question:\n{question}\n\n"
70
- "Answer:"
71
- ),
72
- input_variables=["summaries", "question"],
73
- )
74
-
75
- # مرحله Collapse: اگر خلاصه‌ها زیاد شد، کوتاه‌شان کن (برای رد شدن از سقف توکن)
76
- collapse_prompt_tmpl = PromptTemplate(
77
- template=(
78
- "Condense the following summaries into a shorter consolidated summary, keeping only "
79
- "information relevant to the question.\n\n"
80
- "Question:\n{question}\n\n"
81
- "Summaries:\n{summaries}\n\n"
82
- "Shorter summaries:"
83
- ),
84
- input_variables=["summaries", "question"],
85
- )
86
-
87
- model = ChatGoogleGenerativeAI(model="gemini-1.5-flash", client=genai, temperature=0.3)
88
-
89
- # LLMChain ها
90
- map_llm_chain = LLMChain(llm=model, prompt=map_prompt_tmpl) # expects: context, question
91
- combine_llm_chain = LLMChain(llm=model, prompt=combine_prompt_tmpl) # expects: summaries, question
92
- collapse_llm_chain = LLMChain(llm=model, prompt=collapse_prompt_tmpl) # expects: summaries, question
93
 
94
- # زنجیره‌های ترکیب و فشرده‌سازی
95
- combine_documents_chain = StuffDocumentsChain(
96
- llm_chain=combine_llm_chain,
97
- document_variable_name="summaries"
98
- )
99
- collapse_documents_chain = StuffDocumentsChain(
100
- llm_chain=collapse_llm_chain,
101
- document_variable_name="summaries"
102
- )
103
 
104
- # Reduce با عدد صحیح برای token_max (مثلاً 8000)
105
- reduce_chain = ReduceDocumentsChain(
106
- combine_documents_chain=combine_documents_chain,
107
- collapse_documents_chain=collapse_documents_chain,
108
- token_max=8000 # ✅ عدد صحیح؛ می‌تونی بر اساس نیازت کم/زیادش کنی
109
- )
110
 
111
- # زنجیره‌ی Map → Reduce
112
- chain = MapReduceDocumentsChain(
113
- llm_chain=map_llm_chain,
114
- reduce_documents_chain=reduce_chain,
115
- document_variable_name="context", # باید با ورودی map_prompt یکی باشد
116
- return_intermediate_steps=False,
117
- )
118
- return chain
 
119
 
120
 
121
- # ========== Chat history utils ==========
122
  def clear_chat_history():
123
  st.session_state.messages = [{"role": "assistant", "content": "در خدمتیم"}]
124
 
125
- # ========== Ask ==========
 
126
  def user_input(user_question):
127
  try:
128
  embeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
129
  new_db = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True)
130
-
131
- # برای متن‌های بلند recall را افزایش بده
132
- docs = new_db.similarity_search(user_question, k=12)
133
-
134
- # اجرای زنجیره
135
  chain = get_conversational_chain()
136
- response = chain.invoke({"input_documents": docs, "question": user_question})
137
- # MapReduceDocumentsChain خروجی را در output_text می‌دهد
138
  return response
139
  except Exception as e:
140
  raise RuntimeError(f"Error while answering: {e}")
141
 
142
- # ========== UI ==========
 
143
  def main():
144
- st.set_page_config(page_title="Chatbot", layout="wide", initial_sidebar_state="expanded")
145
-
146
- st.markdown(
147
- """
148
- <style>
149
- body { background-color: #000000; color: #ffffff; }
150
- .main { background-color: #333333; padding: 20px; border-radius: 10px; }
151
- </style>
152
- """,
153
- unsafe_allow_html=True
154
  )
155
 
156
  if "uploaded" not in st.session_state:
157
  st.session_state.uploaded = False
158
 
159
  if not st.session_state.uploaded:
 
160
  st.title("Your personal assistant ...")
161
- pdf_docs = st.file_uploader("فایل پیدیاف(ها) را آپلود کنید", type="pdf", accept_multiple_files=True)
162
  if st.button("تایید"):
163
  if pdf_docs:
164
  try:
@@ -171,8 +116,9 @@ def main():
171
  except RuntimeError as e:
172
  st.error(str(e))
173
  else:
174
- st.error("لطفاً حداقل یک فایل را آپلود کنید")
175
  else:
 
176
  st.title("Assistant ready ...")
177
  st.write("میتونین سوالتونو بپرسین 👇")
178
 
@@ -181,41 +127,53 @@ def main():
181
  clear_chat_history()
182
  st.rerun()
183
 
184
- st.button("حذف مکالمه", on_click=clear_chat_history)
185
 
186
  if "messages" not in st.session_state:
187
  st.session_state.messages = [{"role": "assistant", "content": "در خدمتیم"}]
188
 
189
- # نمایش تاریخچه
190
  for message in st.session_state.messages:
191
  with st.chat_message(message["role"]):
192
  st.markdown(
193
- f"<div style='direction: rtl; text-align: right; font-size: 16px;'>{message['content']}</div>",
 
 
 
 
194
  unsafe_allow_html=True
195
  )
196
 
197
- # ورودی چت
198
  if prompt := st.chat_input():
199
  st.session_state.messages.append({"role": "user", "content": prompt})
200
  with st.chat_message("user"):
201
  st.markdown(
202
- f"<div style='direction: rtl; text-align: right; font-size: 16px;'>{prompt}</div>",
 
 
 
 
203
  unsafe_allow_html=True
204
  )
205
 
206
  if st.session_state.messages[-1]["role"] != "assistant":
207
  try:
208
  with st.chat_message("assistant"):
209
- result = user_input(prompt)
210
- if result:
211
- full_response = result.get("output_text", "")
212
  st.markdown(
213
- f"<div style='direction: rtl; text-align: right; font-size: 16px;'>{full_response}</div>",
 
 
 
 
214
  unsafe_allow_html=True
215
  )
216
  st.session_state.messages.append({"role": "assistant", "content": full_response})
217
  except RuntimeError as e:
218
  st.error(str(e))
219
 
 
220
  if __name__ == "__main__":
221
  main()
 
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:
 
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=2000, chunk_overlap=300)
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")
 
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.
53
+ Answer the question as detailed as possible using the provided context.
54
+ If the answer is unclear, summarize the most relevant part of 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-1.5-flash", 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=15)
 
 
 
 
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
  if "uploaded" not in st.session_state:
101
  st.session_state.uploaded = False
102
 
103
  if not st.session_state.uploaded:
104
+ # Upload Page
105
  st.title("Your personal assistant ...")
106
+ pdf_docs = st.file_uploader("فایل پی دی اف مورد نظر را آپلود کنید ", accept_multiple_files=True)
107
  if st.button("تایید"):
108
  if pdf_docs:
109
  try:
 
116
  except RuntimeError as e:
117
  st.error(str(e))
118
  else:
119
+ st.error("لطفا حداقل یک فایل را آپلود کنید")
120
  else:
121
+ # Chat Page
122
  st.title("Assistant ready ...")
123
  st.write("میتونین سوالتونو بپرسین 👇")
124
 
 
127
  clear_chat_history()
128
  st.rerun()
129
 
130
+ st.button('حذف مکالمه', on_click=clear_chat_history)
131
 
132
  if "messages" not in st.session_state:
133
  st.session_state.messages = [{"role": "assistant", "content": "در خدمتیم"}]
134
 
135
+ # Show history
136
  for message in st.session_state.messages:
137
  with st.chat_message(message["role"]):
138
  st.markdown(
139
+ f"""
140
+ <div style="direction: rtl; text-align: right; font-size: 16px;">
141
+ {message["content"]}
142
+ </div>
143
+ """,
144
  unsafe_allow_html=True
145
  )
146
 
 
147
  if prompt := st.chat_input():
148
  st.session_state.messages.append({"role": "user", "content": prompt})
149
  with st.chat_message("user"):
150
  st.markdown(
151
+ f"""
152
+ <div style="direction: rtl; text-align: right; font-size: 16px;">
153
+ {prompt}
154
+ </div>
155
+ """,
156
  unsafe_allow_html=True
157
  )
158
 
159
  if st.session_state.messages[-1]["role"] != "assistant":
160
  try:
161
  with st.chat_message("assistant"):
162
+ response = user_input(prompt)
163
+ if response:
164
+ full_response = response['output_text']
165
  st.markdown(
166
+ f"""
167
+ <div style="direction: rtl; text-align: right; font-size: 16px;">
168
+ {full_response}
169
+ </div>
170
+ """,
171
  unsafe_allow_html=True
172
  )
173
  st.session_state.messages.append({"role": "assistant", "content": full_response})
174
  except RuntimeError as e:
175
  st.error(str(e))
176
 
177
+
178
  if __name__ == "__main__":
179
  main()