arinbalyan commited on
Commit
bd7fdcf
·
verified ·
1 Parent(s): 29396a7

Delete files requirements.txt with huggingface_hub

Browse files
Files changed (1) hide show
  1. requirements.txt +0 -242
requirements.txt DELETED
@@ -1,242 +0,0 @@
1
- import gradio as gr
2
- import torch
3
- import numpy as np
4
- from sentence_transformers import SentenceTransformer
5
- import faiss
6
- from transformers import AutoModelForCausalLM, AutoTokenizer
7
- from pathlib import Path
8
-
9
- # ── Config ──
10
-
11
- EMBED_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
12
- GEN_MODEL = "Qwen/Qwen2.5-1.5B-Instruct"
13
- CHUNK_SIZE = 512
14
- CHUNK_OVERLAP = 0.15
15
- TOP_K = 5
16
-
17
- theme = (
18
- gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")
19
- .set(button_primary_background_fill_hover="#4f46e5")
20
- )
21
-
22
- # ── Load models ──
23
-
24
- print("Loading embedding model...")
25
- embedder = SentenceTransformer(EMBED_MODEL)
26
- EMBED_DIM = embedder.get_sentence_embedding_dimension()
27
-
28
- print("Loading generation model (Qwen2.5-1.5B)...")
29
- gen_tokenizer = AutoTokenizer.from_pretrained(GEN_MODEL)
30
- gen_model = AutoModelForCausalLM.from_pretrained(
31
- GEN_MODEL,
32
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
33
- device_map="auto",
34
- )
35
-
36
- # ── Document store (in-memory) ──
37
-
38
- documents = [] # list of {"id", "title", "text"}
39
- all_chunks = [] # list of chunk strings
40
- chunk_to_doc = [] # chunk index → document index
41
- index = None # FAISS index
42
-
43
- def chunk_document(text, chunk_size, overlap=0.15):
44
- stride = int(chunk_size * (1 - overlap))
45
- chunks = []
46
- start = 0
47
- while start < len(text):
48
- end = min(start + chunk_size, len(text))
49
- chunk = text[start:end]
50
- if len(chunk.strip()) > 20:
51
- chunks.append(chunk)
52
- if end == len(text):
53
- break
54
- start += stride
55
- return chunks
56
-
57
- def rebuild_index():
58
- global all_chunks, chunk_to_doc, index
59
- all_chunks = []
60
- chunk_to_doc = []
61
- for doc_idx, doc in enumerate(documents):
62
- chunks = chunk_document(doc["text"], CHUNK_SIZE, CHUNK_OVERLAP)
63
- for c in chunks:
64
- all_chunks.append(c)
65
- chunk_to_doc.append(doc_idx)
66
-
67
- if not all_chunks:
68
- index = None
69
- return 0
70
-
71
- embeddings = embedder.encode(all_chunks, show_progress_bar=False)
72
- index = faiss.IndexFlatL2(EMBED_DIM)
73
- index.add(embeddings.astype(np.float32))
74
- return len(all_chunks)
75
-
76
- def add_pdf_text(text, title):
77
- doc_id = f"doc_{len(documents)}"
78
- documents.append({"id": doc_id, "title": title, "text": text})
79
- n = rebuild_index()
80
- return n
81
-
82
- def search(query, top_k=TOP_K):
83
- if index is None or index.ntotal == 0:
84
- return []
85
- q_emb = embedder.encode([query])[0]
86
- D, I = index.search(q_emb.reshape(1, -1).astype(np.float32), min(top_k, index.ntotal))
87
- results = []
88
- for rank, (dist, idx) in enumerate(zip(D[0], I[0])):
89
- doc_idx = chunk_to_doc[idx]
90
- results.append({
91
- "rank": rank + 1,
92
- "chunk": all_chunks[idx][:500],
93
- "distance": float(dist),
94
- "similarity": float(1 / (1 + dist)),
95
- "source": documents[doc_idx]["title"][:80],
96
- })
97
- return results
98
-
99
- def generate_answer(question, history):
100
- if index is None or index.ntotal == 0:
101
- return "Please upload a document first."
102
-
103
- # Retrieve
104
- results = search(question)
105
- if not results:
106
- return "No relevant documents found. Try uploading a different document."
107
-
108
- context_text = "\n\n".join([
109
- f"[Excerpt {r['rank']}]: {r['chunk']}" for r in results
110
- ])
111
-
112
- prompt = (
113
- "You are a document analysis assistant. Answer the question using ONLY the provided document excerpts.\n"
114
- "If the excerpts don't contain enough information, say so clearly.\n\n"
115
- f"Document Excerpts:\n{context_text}\n\n"
116
- f"Question: {question}\n\nAnswer:"
117
- )
118
-
119
- inputs = gen_tokenizer(prompt, return_tensors="pt", truncation=True, max_length=2048)
120
- inputs = {k: v.to(gen_model.device) for k, v in inputs.items()}
121
- with torch.no_grad():
122
- outputs = gen_model.generate(
123
- **inputs,
124
- max_new_tokens=256,
125
- temperature=0.3,
126
- do_sample=True,
127
- pad_token_id=gen_tokenizer.eos_token_id,
128
- )
129
- answer = gen_tokenizer.decode(outputs[0][inputs['input_ids'].shape[1]:], skip_special_tokens=True)
130
- return answer.strip()
131
-
132
- def upload_and_ask(file, question, history):
133
- if file is None:
134
- return question, history, "Please upload a PDF or text file first.", ""
135
-
136
- try:
137
- path = Path(file.name)
138
- text = path.read_text(encoding="utf-8", errors="replace")
139
- if len(text) < 50:
140
- text = ""
141
- import subprocess
142
- result = subprocess.run(["pdftotext", str(path), "-"], capture_output=True, text=True)
143
- text = result.stdout if result.stdout else "Could not extract text from PDF."
144
- except Exception as e:
145
- # Try pdftotext
146
- try:
147
- import subprocess
148
- result = subprocess.run(["pdftotext", str(file.name), "-"], capture_output=True, text=True)
149
- text = result.stdout if result.stdout else f"Error extracting text: {e}"
150
- except Exception:
151
- text = f"Could not process file: {e}"
152
-
153
- title = Path(file.name).stem
154
- n_chunks = add_pdf_text(text, title)
155
- status = f"✅ Indexed {n_chunks:,} chunks from '{title}' ({len(text):,} chars)"
156
-
157
- if question.strip():
158
- answer = generate_answer(question, history)
159
- history.append({"role": "user", "content": question})
160
- history.append({"role": "assistant", "content": answer})
161
- else:
162
- answer = ""
163
- history.append({"role": "assistant", "content": f"Document '{title}' loaded and indexed ({n_chunks:,} chunks). Ask me anything about it!"})
164
-
165
- return question, history, status, ""
166
-
167
- def chat_fn(message, history):
168
- if index is None or index.ntotal == 0:
169
- return "Please upload a document first using the file uploader above."
170
-
171
- answer = generate_answer(message, history)
172
- return answer
173
-
174
- # ── UI ──
175
-
176
- with gr.Blocks(theme=theme, title="RAG Document Q&A", css="""
177
- .gradio-container { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important; }
178
- header { text-align: center; padding: 1.5rem; background: linear-gradient(135deg, #6366f1, #4f46e5); color: white; border-radius: 12px; margin-bottom: 1rem; }
179
- header h1 { margin: 0; font-size: 1.75rem; }
180
- header p { margin: 0.25rem 0 0; opacity: 0.9; font-size: 0.9rem; }
181
- .status-ok { color: #16a34a; font-weight: 500; }
182
- """) as demo:
183
- gr.HTML("""
184
- <header>
185
- <h1>📄 RAG Document Q&A</h1>
186
- <p>Upload a document, ask questions — answers grounded in your content</p>
187
- </header>
188
- """)
189
-
190
- with gr.Row():
191
- with gr.Column(scale=2):
192
- file_input = gr.File(label="Upload Document (PDF or TXT)", file_types=[".pdf", ".txt"])
193
- with gr.Column(scale=1):
194
- upload_btn = gr.Button("Upload & Index", variant="primary")
195
-
196
- status_display = gr.Markdown("📎 Upload a document to get started.")
197
-
198
- gr.Markdown("---")
199
-
200
- chatbot = gr.ChatInterface(
201
- fn=chat_fn,
202
- title="Ask Questions About Your Document",
203
- description="Your questions are answered using chunks from the uploaded document.",
204
- examples=[
205
- "What is this document about?",
206
- "Summarize the key points",
207
- "What methodology was used?",
208
- "What are the main conclusions?",
209
- ],
210
- )
211
-
212
- gr.Markdown("""
213
- <div style="text-align:center; color:gray; font-size:0.85rem; padding:1rem;">
214
- Embeddings: all-MiniLM-L6-v2 · Vector Store: FAISS · Generator: Qwen2.5-1.5B-Instruct · Chunk size: 512
215
- </div>
216
- """)
217
-
218
- def handle_upload(file):
219
- if file is None:
220
- return "📎 Upload a document to get started."
221
- try:
222
- path = Path(file.name)
223
- text = path.read_text(encoding="utf-8", errors="replace")
224
- if len(text) < 50:
225
- raise ValueError("Short text")
226
- except Exception:
227
- try:
228
- import subprocess
229
- result = subprocess.run(["pdftotext", str(file.name), "-"], capture_output=True, text=True)
230
- text = result.stdout if result.stdout else "Could not extract text from PDF."
231
- except Exception as e:
232
- return f"❌ Error: {e}"
233
-
234
- title = Path(file.name).stem
235
- n_chunks = add_pdf_text(text, title)
236
- return f"✅ Indexed **{n_chunks:,}** chunks from **'{title}'** ({len(text):,} chars). Start asking questions!"
237
-
238
- upload_btn.click(handle_upload, inputs=[file_input], outputs=[status_display])
239
- file_input.change(handle_upload, inputs=[file_input], outputs=[status_display])
240
-
241
- if __name__ == "__main__":
242
- demo.launch()