traleela commited on
Commit
fe0e877
Β·
verified Β·
1 Parent(s): 55b0fc7

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +65 -22
  2. requirements.txt +5 -1
app.py CHANGED
@@ -2,9 +2,10 @@ import os
2
  import fitz # PyMuPDF
3
  import numpy as np
4
  import faiss
 
5
  import gradio as gr
6
  from sentence_transformers import SentenceTransformer
7
- import pickle
8
 
9
  # === Configuration ===
10
  INDEX_DIR = "saved_index"
@@ -14,17 +15,31 @@ CHUNK_SIZE = 500
14
  # === Ensure save directory exists ===
15
  os.makedirs(INDEX_DIR, exist_ok=True)
16
 
17
- # === Load model globally ===
18
  model = SentenceTransformer(MODEL_NAME)
 
19
 
20
  # === Helper functions ===
21
- def load_pdf(file_path):
22
  doc = fitz.open(file_path)
23
- return "\n".join(page.get_text() for page in doc)
24
-
25
- def split_into_chunks(text, chunk_size=CHUNK_SIZE):
26
- words = text.split()
27
- return [" ".join(words[i:i+chunk_size]) for i in range(0, len(words), chunk_size)]
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  def create_index(chunks):
30
  embeddings = model.encode(chunks)
@@ -50,15 +65,28 @@ def load_index():
50
  else:
51
  return None, None, []
52
 
53
- def ask_question(query, index, embeddings, chunks, top_k=3, context_window=1):
 
 
 
 
 
54
  query_emb = model.encode([query])
55
  distances, indices = index.search(np.array(query_emb), top_k)
56
- context_chunks = set()
57
- for i in indices[0]:
58
- for j in range(max(0, i - context_window), min(len(chunks), i + context_window + 1)):
59
- context_chunks.add(j)
60
- sorted_context = sorted(list(context_chunks))
61
- return "\n\n".join([chunks[i] for i in sorted_context])
 
 
 
 
 
 
 
 
62
 
63
  # === Gradio Interface ===
64
  state = {"index": None, "embeddings": None, "chunks": []}
@@ -66,16 +94,15 @@ state = {"index": None, "embeddings": None, "chunks": []}
66
  def process_pdfs(files):
67
  all_chunks = []
68
  for file in files:
69
- text = load_pdf(file.name)
70
- chunks = split_into_chunks(text)
71
  all_chunks.extend(chunks)
72
  if all_chunks:
73
  index, embeddings = create_index(all_chunks)
74
  save_index(index, embeddings, all_chunks)
75
  state.update({"index": index, "embeddings": embeddings, "chunks": all_chunks})
76
- return f"Uploaded and indexed {len(files)} file(s) successfully."
77
  else:
78
- return "No text found in PDFs."
79
 
80
  def query_bot(query):
81
  if not state["index"]:
@@ -86,14 +113,30 @@ def query_bot(query):
86
  return "Please upload and process PDFs first."
87
  return ask_question(query, state["index"], state["embeddings"], state["chunks"])
88
 
 
 
 
 
 
 
 
 
 
89
  with gr.Blocks() as demo:
90
- gr.Markdown("# πŸ“˜ AI Revision Assistant – Uses Only Your PDFs")
91
  with gr.Row():
92
  file_input = gr.File(label="Upload PDFs", file_count="multiple")
93
  status = gr.Textbox(label="Status Message")
94
  file_input.change(process_pdfs, inputs=file_input, outputs=status)
95
- query = gr.Textbox(label="Ask a Question")
 
 
96
  answer = gr.Textbox(label="Answer")
97
- query.submit(query_bot, inputs=query, outputs=answer)
 
 
 
 
 
98
 
99
  demo.launch()
 
2
  import fitz # PyMuPDF
3
  import numpy as np
4
  import faiss
5
+ import pickle
6
  import gradio as gr
7
  from sentence_transformers import SentenceTransformer
8
+ from transformers import pipeline
9
 
10
  # === Configuration ===
11
  INDEX_DIR = "saved_index"
 
15
  # === Ensure save directory exists ===
16
  os.makedirs(INDEX_DIR, exist_ok=True)
17
 
18
+ # === Load sentence transformer and LLM pipeline ===
19
  model = SentenceTransformer(MODEL_NAME)
20
+ llm = pipeline("text-generation", model="mistralai/Mistral-7B-Instruct-v0.2", tokenizer="mistralai/Mistral-7B-Instruct-v0.2", device=0 if torch.cuda.is_available() else -1)
21
 
22
  # === Helper functions ===
23
+ def load_pdf_by_sections(file_path):
24
  doc = fitz.open(file_path)
25
+ chunks = []
26
+ for page in doc:
27
+ blocks = page.get_text("blocks")
28
+ blocks.sort() # sort top-to-bottom
29
+ text = ""
30
+ for b in blocks:
31
+ content = b[4].strip()
32
+ if len(content.split()) < 5:
33
+ continue
34
+ if content.isupper() or content.istitle(): # crude heading detection
35
+ if text:
36
+ chunks.append(text.strip())
37
+ text = content
38
+ else:
39
+ text += " " + content
40
+ if text:
41
+ chunks.append(text.strip())
42
+ return chunks
43
 
44
  def create_index(chunks):
45
  embeddings = model.encode(chunks)
 
65
  else:
66
  return None, None, []
67
 
68
+ def generate_answer(context, question):
69
+ prompt = f"Answer this based only on the context below. Be precise and relevant.\n\nContext:\n{context}\n\nQuestion: {question}\nAnswer:"
70
+ response = llm(prompt, max_new_tokens=200, do_sample=True, temperature=0.7)[0]["generated_text"]
71
+ return response.split("Answer:")[-1].strip()
72
+
73
+ def ask_question(query, index, embeddings, chunks, top_k=3):
74
  query_emb = model.encode([query])
75
  distances, indices = index.search(np.array(query_emb), top_k)
76
+ relevant_context = "\n\n".join([chunks[i] for i in indices[0]])
77
+ return generate_answer(relevant_context, query)
78
+
79
+ def quiz_me(index, embeddings, chunks):
80
+ np.random.seed(42)
81
+ sample_indices = np.random.choice(len(chunks), 2, replace=False)
82
+ questions = []
83
+ for i in sample_indices:
84
+ context = chunks[i]
85
+ prompt = f"Based only on the text below, generate a single clear quiz question:\n\n{context}\n\nQuestion:"
86
+ result = llm(prompt, max_new_tokens=100, do_sample=True, temperature=0.7)[0]["generated_text"]
87
+ extracted = result.split("Question:")[-1].strip()
88
+ questions.append(extracted)
89
+ return "\n\n".join(questions)
90
 
91
  # === Gradio Interface ===
92
  state = {"index": None, "embeddings": None, "chunks": []}
 
94
  def process_pdfs(files):
95
  all_chunks = []
96
  for file in files:
97
+ chunks = load_pdf_by_sections(file.name)
 
98
  all_chunks.extend(chunks)
99
  if all_chunks:
100
  index, embeddings = create_index(all_chunks)
101
  save_index(index, embeddings, all_chunks)
102
  state.update({"index": index, "embeddings": embeddings, "chunks": all_chunks})
103
+ return f"Processed {len(files)} file(s) successfully."
104
  else:
105
+ return "No usable text found in PDFs."
106
 
107
  def query_bot(query):
108
  if not state["index"]:
 
113
  return "Please upload and process PDFs first."
114
  return ask_question(query, state["index"], state["embeddings"], state["chunks"])
115
 
116
+ def quiz_bot(dummy=""):
117
+ if not state["index"]:
118
+ index, embeddings, chunks = load_index()
119
+ if index:
120
+ state.update({"index": index, "embeddings": embeddings, "chunks": chunks})
121
+ else:
122
+ return "Please upload and process PDFs first."
123
+ return quiz_me(state["index"], state["embeddings"], state["chunks"])
124
+
125
  with gr.Blocks() as demo:
126
+ gr.Markdown("# πŸŽ“ AI Study Assistant (PDF-based with Smart Q&A + Quizzing)")
127
  with gr.Row():
128
  file_input = gr.File(label="Upload PDFs", file_count="multiple")
129
  status = gr.Textbox(label="Status Message")
130
  file_input.change(process_pdfs, inputs=file_input, outputs=status)
131
+
132
+ gr.Markdown("## ❓ Ask a Question from Your Material")
133
+ question = gr.Textbox(label="Question")
134
  answer = gr.Textbox(label="Answer")
135
+ question.submit(query_bot, inputs=question, outputs=answer)
136
+
137
+ gr.Markdown("## 🧠 Quiz Me from Uploaded PDFs")
138
+ quiz_btn = gr.Button("Generate 2 Quiz Questions")
139
+ quiz_output = gr.Textbox(label="Quiz Questions")
140
+ quiz_btn.click(quiz_bot, inputs=[], outputs=quiz_output)
141
 
142
  demo.launch()
requirements.txt CHANGED
@@ -1,4 +1,8 @@
1
  pymupdf
2
  sentence-transformers
3
  faiss-cpu
4
- gradio
 
 
 
 
 
1
  pymupdf
2
  sentence-transformers
3
  faiss-cpu
4
+ gradio
5
+ transformers
6
+ torch
7
+ accelerate
8
+ safetensors