muddasser commited on
Commit
694b155
·
verified ·
1 Parent(s): 1920e28

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -22
app.py CHANGED
@@ -18,6 +18,12 @@ from rank_bm25 import BM25Okapi
18
  from sentence_transformers import CrossEncoder, SentenceTransformer
19
  from sklearn.metrics.pairwise import cosine_similarity
20
 
 
 
 
 
 
 
21
  # ========================= CONSTANTS =========================
22
  EMBED_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
23
  RERANKER_MODEL = "cross-encoder/ms-marco-MiniLM-L-6-v2"
@@ -52,7 +58,6 @@ class HybridMemory:
52
  def load_memory(self):
53
  vector_path = os.path.join(self.memory_dir, "vector_memory")
54
  graph_path = os.path.join(self.memory_dir, "memory_graph.pkl")
55
-
56
  if os.path.exists(vector_path):
57
  self.vector_memory = FAISS.load_local(vector_path, self.embeddings, allow_dangerous_deserialization=True)
58
  if os.path.exists(graph_path):
@@ -72,12 +77,10 @@ class HybridMemory:
72
  "timestamp": datetime.now().isoformat(),
73
  "type": memory_type,
74
  })
75
-
76
  if self.vector_memory is None:
77
  self.vector_memory = FAISS.from_texts([text], self.embeddings, metadatas=[metadata])
78
  else:
79
  self.vector_memory.add_texts([text], metadatas=[metadata])
80
-
81
  self._add_to_graph(text)
82
  self.save_memory()
83
 
@@ -141,13 +144,31 @@ def load_spacy():
141
  download("en_core_web_sm")
142
  return spacy.load("en_core_web_sm")
143
 
144
- def load_pdf_documents(uploaded_files):
 
145
  docs = []
146
  for f in uploaded_files:
147
- reader = PdfReader(f)
148
- text = "\n".join([page.extract_text() or "" for page in reader.pages])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  if text.strip():
150
- docs.append({"text": text, "source": f.name})
151
  return docs
152
 
153
  def hybrid_chunking(text, embed_model, base=300, final=1000, thresh=0.6):
@@ -207,10 +228,7 @@ def build_indexes(chunks, lc_embeddings):
207
  chunk_lookup = {c["text"]: c for c in chunks}
208
  return bm25, faiss_index, chunk_lookup
209
 
210
- def hybrid_memory_retrieve(query, chunks, bm25, faiss_index, chunk_lookup,
211
- graph, entity_to_chunks, reranker, nlp, memory,
212
- top_k=5, hops=2):
213
- # Document + Graph Retrieval (simplified)
214
  texts = [c["text"] for c in chunks]
215
  bm25_scores = bm25.get_scores(query.lower().split())
216
  bm25_idx = np.argsort(bm25_scores)[::-1][:top_k*3]
@@ -229,10 +247,8 @@ def hybrid_memory_retrieve(query, chunks, bm25, faiss_index, chunk_lookup,
229
  candidates.append(c)
230
  seen.add(c["text"])
231
 
232
- # Memory Retrieval
233
  mem_items = memory.retrieve_memory(query, top_k=top_k*2)
234
 
235
- # Reranking
236
  all_texts = [c["text"] for c in candidates] + [m["text"] for m in mem_items]
237
  pairs = [(query, t) for t in all_texts]
238
  scores = reranker.predict(pairs)
@@ -248,7 +264,6 @@ def hybrid_memory_retrieve(query, chunks, bm25, faiss_index, chunk_lookup,
248
  break
249
  else:
250
  final_chunks.append({"text": t, "source": "🧠 Memory"})
251
-
252
  return final_chunks[:top_k], {"memory_used": len(mem_items)}
253
 
254
  def generate_answer(llm, query, retrieved):
@@ -278,7 +293,6 @@ with st.sidebar:
278
  chunk_method = st.selectbox("Chunking Method", ["Hybrid (Recursive + Semantic)", "Recursive Only"])
279
  top_k = st.slider("Top-K Results", 3, 15, DEFAULT_TOP_K)
280
  graph_hops = st.slider("Graph Hops", 1, 3, 2)
281
-
282
  if st.button("Clear Memory"):
283
  st.session_state.memory = HybridMemory()
284
  st.success("Memory cleared!")
@@ -296,12 +310,19 @@ if not groq_key:
296
 
297
  llm = ChatGroq(temperature=0.2, model_name=LLM_MODEL_NAME, groq_api_key=groq_key)
298
 
299
- # File Upload
300
- uploaded_files = st.file_uploader("Upload PDFs", type=["pdf"], accept_multiple_files=True)
 
 
 
 
301
 
302
  if uploaded_files and st.session_state.chunks is None:
303
  with st.spinner("Processing documents..."):
304
- docs = load_pdf_documents(uploaded_files)
 
 
 
305
  chunks = chunk_documents(docs, embed_model, chunk_method)
306
  bm25, faiss_index, chunk_lookup = build_indexes(chunks, lc_embeddings)
307
  graph, entity_to_chunks = build_knowledge_graph(chunks, nlp)
@@ -312,7 +333,7 @@ if uploaded_files and st.session_state.chunks is None:
312
  st.session_state.chunk_lookup = chunk_lookup
313
  st.session_state.graph = graph
314
  st.session_state.entity_to_chunks = entity_to_chunks
315
- st.success(f"Loaded {len(chunks)} chunks")
316
 
317
  # Query
318
  query = st.text_input("Ask a question about your documents:")
@@ -332,7 +353,6 @@ if query and st.session_state.chunks:
332
  st.subheader("Answer")
333
  st.write(answer)
334
 
335
- # Store new memories
336
  if retrieved:
337
  summary = f"Q: {query}\nA: {answer[:300]}..."
338
  st.session_state.memory.add_memory(summary, memory_type="conversation")
@@ -344,8 +364,7 @@ if query and st.session_state.chunks:
344
  for i, chunk in enumerate(retrieved, 1):
345
  st.markdown(f"**{i}.** {chunk['source']}")
346
  st.write(chunk["text"][:500] + "..." if len(chunk["text"]) > 500 else chunk["text"])
347
-
348
  else:
349
- st.info("Upload PDFs and ask a question to begin.")
350
 
351
  st.caption("Hybrid Graph RAG + Persistent Memory • Powered by Groq + NetworkX + FAISS")
 
18
  from sentence_transformers import CrossEncoder, SentenceTransformer
19
  from sklearn.metrics.pairwise import cosine_similarity
20
 
21
+ # For DOCX support
22
+ try:
23
+ from docx import Document
24
+ except ImportError:
25
+ pass # Will show warning if missing
26
+
27
  # ========================= CONSTANTS =========================
28
  EMBED_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
29
  RERANKER_MODEL = "cross-encoder/ms-marco-MiniLM-L-6-v2"
 
58
  def load_memory(self):
59
  vector_path = os.path.join(self.memory_dir, "vector_memory")
60
  graph_path = os.path.join(self.memory_dir, "memory_graph.pkl")
 
61
  if os.path.exists(vector_path):
62
  self.vector_memory = FAISS.load_local(vector_path, self.embeddings, allow_dangerous_deserialization=True)
63
  if os.path.exists(graph_path):
 
77
  "timestamp": datetime.now().isoformat(),
78
  "type": memory_type,
79
  })
 
80
  if self.vector_memory is None:
81
  self.vector_memory = FAISS.from_texts([text], self.embeddings, metadatas=[metadata])
82
  else:
83
  self.vector_memory.add_texts([text], metadatas=[metadata])
 
84
  self._add_to_graph(text)
85
  self.save_memory()
86
 
 
144
  download("en_core_web_sm")
145
  return spacy.load("en_core_web_sm")
146
 
147
+ def load_documents(uploaded_files):
148
+ """Support PDF, TXT, and DOCX"""
149
  docs = []
150
  for f in uploaded_files:
151
+ text = ""
152
+ source = f.name
153
+
154
+ try:
155
+ if f.name.lower().endswith('.pdf'):
156
+ reader = PdfReader(f)
157
+ text = "\n".join([page.extract_text() or "" for page in reader.pages])
158
+ elif f.name.lower().endswith('.txt'):
159
+ text = f.read().decode("utf-8")
160
+ elif f.name.lower().endswith(('.docx', '.doc')):
161
+ doc = Document(f)
162
+ text = "\n".join([paragraph.text for paragraph in doc.paragraphs])
163
+ else:
164
+ st.warning(f"Unsupported file type: {f.name}")
165
+ continue
166
+ except Exception as e:
167
+ st.warning(f"Error reading {f.name}: {str(e)}")
168
+ continue
169
+
170
  if text.strip():
171
+ docs.append({"text": text, "source": source})
172
  return docs
173
 
174
  def hybrid_chunking(text, embed_model, base=300, final=1000, thresh=0.6):
 
228
  chunk_lookup = {c["text"]: c for c in chunks}
229
  return bm25, faiss_index, chunk_lookup
230
 
231
+ def hybrid_memory_retrieve(query, chunks, bm25, faiss_index, chunk_lookup, graph, entity_to_chunks, reranker, nlp, memory, top_k=5, hops=2):
 
 
 
232
  texts = [c["text"] for c in chunks]
233
  bm25_scores = bm25.get_scores(query.lower().split())
234
  bm25_idx = np.argsort(bm25_scores)[::-1][:top_k*3]
 
247
  candidates.append(c)
248
  seen.add(c["text"])
249
 
 
250
  mem_items = memory.retrieve_memory(query, top_k=top_k*2)
251
 
 
252
  all_texts = [c["text"] for c in candidates] + [m["text"] for m in mem_items]
253
  pairs = [(query, t) for t in all_texts]
254
  scores = reranker.predict(pairs)
 
264
  break
265
  else:
266
  final_chunks.append({"text": t, "source": "🧠 Memory"})
 
267
  return final_chunks[:top_k], {"memory_used": len(mem_items)}
268
 
269
  def generate_answer(llm, query, retrieved):
 
293
  chunk_method = st.selectbox("Chunking Method", ["Hybrid (Recursive + Semantic)", "Recursive Only"])
294
  top_k = st.slider("Top-K Results", 3, 15, DEFAULT_TOP_K)
295
  graph_hops = st.slider("Graph Hops", 1, 3, 2)
 
296
  if st.button("Clear Memory"):
297
  st.session_state.memory = HybridMemory()
298
  st.success("Memory cleared!")
 
310
 
311
  llm = ChatGroq(temperature=0.2, model_name=LLM_MODEL_NAME, groq_api_key=groq_key)
312
 
313
+ # File Upload - Now supports PDF, TXT, DOCX
314
+ uploaded_files = st.file_uploader(
315
+ "Upload Documents (PDF, TXT, DOCX)",
316
+ type=["pdf", "txt", "docx"],
317
+ accept_multiple_files=True
318
+ )
319
 
320
  if uploaded_files and st.session_state.chunks is None:
321
  with st.spinner("Processing documents..."):
322
+ docs = load_documents(uploaded_files)
323
+ if not docs:
324
+ st.error("No readable text found in uploaded files.")
325
+ st.stop()
326
  chunks = chunk_documents(docs, embed_model, chunk_method)
327
  bm25, faiss_index, chunk_lookup = build_indexes(chunks, lc_embeddings)
328
  graph, entity_to_chunks = build_knowledge_graph(chunks, nlp)
 
333
  st.session_state.chunk_lookup = chunk_lookup
334
  st.session_state.graph = graph
335
  st.session_state.entity_to_chunks = entity_to_chunks
336
+ st.success(f"Loaded {len(chunks)} chunks from {len(docs)} document(s)")
337
 
338
  # Query
339
  query = st.text_input("Ask a question about your documents:")
 
353
  st.subheader("Answer")
354
  st.write(answer)
355
 
 
356
  if retrieved:
357
  summary = f"Q: {query}\nA: {answer[:300]}..."
358
  st.session_state.memory.add_memory(summary, memory_type="conversation")
 
364
  for i, chunk in enumerate(retrieved, 1):
365
  st.markdown(f"**{i}.** {chunk['source']}")
366
  st.write(chunk["text"][:500] + "..." if len(chunk["text"]) > 500 else chunk["text"])
 
367
  else:
368
+ st.info("Upload documents (PDF, TXT, DOCX) and ask a question to begin.")
369
 
370
  st.caption("Hybrid Graph RAG + Persistent Memory • Powered by Groq + NetworkX + FAISS")