pradeepsengarr commited on
Commit
3d46a83
Β·
verified Β·
1 Parent(s): 48c1ca7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -290
app.py CHANGED
@@ -1,265 +1,158 @@
1
  import os
2
  import re
3
  import faiss
4
- import docx
5
- import PyPDF2
6
  import gradio as gr
7
  import numpy as np
8
- from typing import List, Dict
 
 
 
9
  from sentence_transformers import SentenceTransformer
10
  from transformers import pipeline
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  class SmartDocumentRAG:
14
- def __init__(self, embedder_model='sentence-transformers/all-MiniLM-L6-v2', qa_model='distilbert-base-cased-distilled-squad'):
15
- # Load sentence embedding model
16
- self.embedder = SentenceTransformer(embedder_model)
17
-
18
- # Load Q&A pipeline model
19
- self.qa_pipeline = pipeline('question-answering', model=qa_model, tokenizer=qa_model)
20
-
21
- # Document and index initialization
22
  self.documents = []
23
- self.document_metadata = []
24
- self.raw_text = ""
25
- self.document_summary = ""
26
- self.document_type = ""
27
  self.index = None
28
  self.is_indexed = False
29
- self.model_type = "distilbert-qa" # Can add flan-t5 or others as needed
30
-
31
- ####################
32
- # Text Extraction
33
- ####################
34
- def extract_text_from_file(self, file_path: str) -> str:
35
- ext = os.path.splitext(file_path)[1].lower()
36
- try:
37
- if ext == '.pdf':
38
- return self.extract_from_pdf(file_path)
39
- elif ext == '.docx':
40
- return self.extract_from_docx(file_path)
41
- elif ext == '.txt':
42
- return self.extract_from_txt(file_path)
43
- else:
44
- return f"Unsupported file type: {ext}"
45
- except Exception as e:
46
- return f"Error reading file: {e}"
47
-
48
- def extract_from_pdf(self, file_path: str) -> str:
49
- text = ""
50
- try:
51
- with open(file_path, 'rb') as f:
52
- reader = PyPDF2.PdfReader(f)
53
- for page in reader.pages:
54
- txt = page.extract_text() or ""
55
- cleaned = self.clean_text(txt)
56
- text += cleaned + "\n"
57
- return text.strip()
58
- except Exception as e:
59
- return f"Error reading PDF: {e}"
60
 
61
- def extract_from_docx(self, file_path: str) -> str:
62
- try:
63
- doc = docx.Document(file_path)
64
- paragraphs = [self.clean_text(p.text) for p in doc.paragraphs if p.text.strip()]
65
- return "\n".join(paragraphs)
66
- except Exception as e:
67
- return f"Error reading DOCX: {e}"
68
-
69
- def extract_from_txt(self, file_path: str) -> str:
70
- encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']
71
- for enc in encodings:
72
- try:
73
- with open(file_path, 'r', encoding=enc) as f:
74
- return self.clean_text(f.read())
75
- except UnicodeDecodeError:
76
- continue
77
- except Exception as e:
78
- return f"Error reading TXT: {e}"
79
- return "Could not decode TXT file."
80
-
81
- def clean_text(self, text: str) -> str:
82
- # Normalize whitespace, fix broken words, remove weird chars
83
- text = re.sub(r'\s+', ' ', text)
84
- text = re.sub(r'([a-z])([A-Z])', r'\1 \2', text) # Fix camel case merges
85
- text = text.strip()
86
- return text
87
-
88
- ####################
89
- # Document Type Detection & Summary
90
- ####################
91
- def detect_document_type(self, text: str) -> str:
92
- lower_text = text.lower()
93
- if any(k in lower_text for k in ['abstract', 'study', 'research', 'methodology']):
94
- return 'research'
95
- elif any(k in lower_text for k in ['company', 'business', 'organization', 'financial']):
96
- return 'business'
97
  else:
98
- return 'general'
99
 
100
- def create_document_summary(self, text: str) -> str:
101
- sentences = re.split(r'(?<=[.!?]) +', text)
102
- sentences = [s.strip() for s in sentences if len(s.strip()) > 10]
103
 
104
- if self.document_type == 'research':
105
- return self.extract_research_summary(sentences)
106
- elif self.document_type == 'business':
107
- return self.extract_business_summary(sentences)
108
- else:
109
- return self.extract_general_summary(sentences)
110
-
111
- def extract_research_summary(self, sentences: List[str]) -> str:
112
- for s in sentences[:7]:
113
- if any(w in s.lower() for w in ['abstract', 'study', 'research']):
114
- return s[:300] + ('...' if len(s) > 300 else '')
115
- return sentences[0][:300] if sentences else "Research document."
116
-
117
- def extract_business_summary(self, sentences: List[str]) -> str:
118
- for s in sentences[:5]:
119
- if any(w in s.lower() for w in ['company', 'business', 'organization']):
120
- return s[:300] + ('...' if len(s) > 300 else '')
121
- return sentences[0][:300] if sentences else "Business document."
122
-
123
- def extract_general_summary(self, sentences: List[str]) -> str:
124
- return sentences[0][:300] + ('...' if len(sentences[0]) > 300 else '') if sentences else "General document."
125
-
126
- ####################
127
- # Chunking
128
- ####################
129
- def enhanced_chunk_text(self, text: str, chunk_size: int = 3, overlap: int = 1) -> List[Dict]:
130
- if not text.strip():
131
- return []
132
-
133
- sentences = re.split(r'(?<=[.!?]) +', text)
134
- sentences = [s.strip() for s in sentences if len(s.strip()) > 10]
135
-
136
- chunks = []
137
- for i in range(0, len(sentences), chunk_size - overlap):
138
- chunk_sents = sentences[i:i + chunk_size]
139
- if chunk_sents:
140
- chunk_text = " ".join(chunk_sents)
141
- chunks.append({
142
- "text": chunk_text,
143
- "sentence_indices": list(range(i, min(i + chunk_size, len(sentences)))),
144
- "doc_type": self.document_type
145
- })
146
- return chunks
147
-
148
- ####################
149
- # Processing uploaded files
150
- ####################
151
- def process_documents(self, files) -> str:
152
  if not files:
153
- return "❌ No files uploaded!"
 
154
 
155
- try:
156
- all_text = ""
157
- processed_files = []
158
-
159
- for file in files:
160
- if file is None:
161
- continue
162
- file_text = self.extract_text_from_file(file.name)
163
- if not file_text.startswith("Error") and not file_text.startswith("Unsupported"):
164
- all_text += " " + file_text
165
- processed_files.append(os.path.basename(file.name))
166
- else:
167
- return f"❌ {file_text}"
168
-
169
- if not all_text.strip():
170
- return "❌ No text extracted from files!"
171
-
172
- self.raw_text = all_text.strip()
173
- self.document_type = self.detect_document_type(self.raw_text)
174
- self.document_summary = self.create_document_summary(self.raw_text)
175
-
176
- chunks = self.enhanced_chunk_text(self.raw_text)
177
- if not chunks:
178
- return "❌ No valid chunks created!"
179
 
180
- self.documents = [c["text"] for c in chunks]
181
- self.document_metadata = chunks
182
 
183
- embeddings = self.embedder.encode(self.documents, show_progress_bar=False, convert_to_numpy=True)
184
- dimension = embeddings.shape[1]
185
 
186
- self.index = faiss.IndexFlatIP(dimension)
187
- faiss.normalize_L2(embeddings)
188
- self.index.add(embeddings.astype('float32'))
189
 
190
- self.is_indexed = True
 
 
 
 
 
 
191
 
192
- return (f"βœ… Processed {len(processed_files)} files: {', '.join(processed_files)}\n"
193
- f"πŸ“„ Document Type: {self.document_type.title()}\n"
194
- f"πŸ” Created {len(self.documents)} chunks\n"
195
- f"πŸ“ Summary: {self.document_summary}\n"
196
- f"πŸš€ Ready for Q&A!")
197
 
198
- except Exception as e:
199
- return f"❌ Error processing documents: {e}"
200
 
201
- ####################
202
- # Search & Answer
203
- ####################
204
- def find_relevant_content(self, query: str, top_k: int = 3) -> str:
205
  if not self.is_indexed:
206
  return ""
207
 
208
- try:
209
- query_embedding = self.embedder.encode([query], convert_to_numpy=True)
210
- faiss.normalize_L2(query_embedding)
211
-
212
- k = min(top_k, len(self.documents))
213
- scores, indices = self.index.search(query_embedding.astype('float32'), k)
214
-
215
- relevant_chunks = []
216
- for score, idx in zip(scores[0], indices[0]):
217
- if idx < len(self.documents) and score > 0.15:
218
- relevant_chunks.append(self.documents[idx])
219
 
220
- return " ".join(relevant_chunks)
221
-
222
- except Exception as e:
223
- print(f"Search error: {e}")
224
- return ""
 
 
225
 
226
  def answer_question(self, query: str) -> str:
227
  if not query.strip():
228
  return "❓ Please ask a valid question."
229
-
230
  if not self.is_indexed:
231
- return "πŸ“ Please upload and process documents before asking questions."
232
-
233
  query_lower = query.lower()
234
-
235
  if any(word in query_lower for word in ['summary', 'summarize', 'overview', 'about']):
236
- if self.document_summary:
237
- return f"πŸ“„ Document Summary:\n\n{self.document_summary}"
238
- else:
239
- return "⚠️ Summary not available. Please process documents first."
240
-
241
  context = self.find_relevant_content(query, k=5)
242
- print(f"Context found (top 5 chunks): {context}")
243
-
244
  if not context:
245
- return "πŸ” Sorry, no relevant information was found for your question. Try rephrasing."
246
-
247
  try:
248
- if self.model_type in ["distilbert-qa", "fallback"]:
249
  result = self.qa_pipeline(question=query, context=context)
250
- print(f"QA Pipeline output: {result}")
251
  answer = result.get('answer', '').strip()
252
  score = result.get('score', 0.0)
253
-
254
  if not answer or score < 0.05:
255
- return "πŸ€” I couldn't find a confident answer to your question based on the documents."
256
-
257
  snippet = context[:300].strip()
258
  if len(context) > 300:
259
  snippet += "..."
260
-
261
  return f"**Answer:** {answer}\n\n*Context snippet:* {snippet}"
262
-
263
  elif self.model_type == "flan-t5":
264
  prompt = (
265
  f"Answer the question based on the context below.\n\n"
@@ -267,98 +160,57 @@ class SmartDocumentRAG:
267
  f"Question: {query}\nAnswer:"
268
  )
269
  result = self.qa_pipeline(prompt, max_length=200, num_return_sequences=1)
270
- print(f"Generative pipeline output: {result}")
271
-
272
  answer = result[0]['generated_text'].replace(prompt, '').strip()
273
  if not answer:
274
- return "πŸ€” I couldn't find a confident answer to your question based on the documents."
275
  return f"**Answer:** {answer}"
276
-
277
  else:
278
- return "⚠️ Unsupported model type for QA."
279
-
280
  except Exception as e:
281
- return f"❌ An error occurred while answering your question: {str(e)}"
 
282
 
283
- def extract_direct_answer(self, query: str, context: str) -> str:
284
- lower_query = query.lower()
 
285
 
286
- # Extract names (simple heuristic)
287
- if any(k in lower_query for k in ['name', 'who is', 'who']):
288
- names = re.findall(r'\b[A-Z][a-z]+ [A-Z][a-z]+\b', context)
289
- if names:
290
- return f"**Name:** {names[0]}"
 
 
 
 
 
 
291
 
292
- # Extract experience years
293
- if any(k in lower_query for k in ['experience', 'years']):
294
- exp = re.findall(r'(\d+)[\+\-\s]*(?:years?|yrs?)', context.lower())
295
- if exp:
296
- return f"**Experience:** {exp[0]} years"
297
 
298
- # Extract skills
299
- if any(k in lower_query for k in ['skill', 'technology', 'tech']):
300
- skills_regex = r'\b(Python|Java|JavaScript|React|Node|SQL|AWS|Docker|Kubernetes|Git|HTML|CSS|Angular|Vue|Spring|Django|Flask|MongoDB|PostgreSQL)\b'
301
- skills_found = list(set(re.findall(skills_regex, context, re.I)))
302
- if skills_found:
303
- return f"**Skills mentioned:** {', '.join(skills_found)}"
304
 
305
- # Extract education
306
- if any(k in lower_query for k in ['education', 'degree', 'university']):
307
- edu = re.findall(r'(?:Bachelor|Master|PhD|B\.?S\.?|M\.?S\.?|B\.?A\.?|M\.?A\.?).*?(?:in|of)\s+([^.]+)', context, re.I)
308
- if edu:
309
- return f"**Education:** {edu[0]}"
310
 
311
- # Fallback: first sentence
312
- sentences = re.split(r'(?<=[.!?]) +', context)
313
- if sentences:
314
- return f"**Answer:** {sentences[0]}"
315
 
316
- return "I found relevant information but could not extract a precise answer."
 
 
317
 
318
-
319
- # Gradio interface creation
320
- def create_interface():
321
- rag_system = SmartDocumentRAG()
322
-
323
- with gr.Blocks(title="🧠 Enhanced Document Q&A", theme=gr.themes.Soft()) as demo:
324
- gr.Markdown("""
325
- # 🧠 Enhanced Document Q&A System
326
-
327
- **Optimized with Better Chunking, Summaries, and Reduced Hallucination**
328
-
329
- **Features:**
330
- - 🎯 DistilBERT Q&A pipeline for accurate answers
331
- - ⚑ SentenceTransformer embeddings + FAISS semantic search
332
- - πŸ“Š Improved document summaries & chunking
333
- - πŸ” Direct answer fallback for facts extraction
334
- """)
335
-
336
- with gr.Tab("πŸ“€ Upload & Process"):
337
- with gr.Row():
338
- with gr.Column():
339
- file_upload = gr.File(label="πŸ“ Upload Documents", file_types=[".pdf", ".docx", ".txt"], file_count="multiple", interactive=True)
340
- process_btn = gr.Button("πŸ”„ Process Documents", variant="primary")
341
- with gr.Column():
342
- process_status = gr.Textbox(label="πŸ“‹ Processing Status", lines=8, interactive=False)
343
-
344
- process_btn.click(fn=rag_system.process_documents, inputs=[file_upload], outputs=[process_status])
345
-
346
- with gr.Tab("❓ Q&A"):
347
- with gr.Row():
348
- with gr.Column():
349
- question_input = gr.Textbox(label="πŸ€” Ask Your Question", placeholder="Enter your question here...", lines=3)
350
- with gr.Row():
351
- ask_btn = gr.Button("🧠 Get Answer", variant="primary")
352
- summary_btn = gr.Button("πŸ“Š Get Summary", variant="secondary")
353
- with gr.Column():
354
- answer_output = gr.Textbox(label="πŸ’‘ Answer", lines=8, interactive=False)
355
-
356
- ask_btn.click(fn=rag_system.answer_question, inputs=[question_input], outputs=[answer_output])
357
- summary_btn.click(fn=lambda: rag_system.answer_question("summary"), inputs=[], outputs=[answer_output])
358
 
359
  return demo
360
 
361
-
362
  if __name__ == "__main__":
363
  demo = create_interface()
364
  demo.launch(server_name="0.0.0.0", server_port=7860, share=True)
 
1
  import os
2
  import re
3
  import faiss
 
 
4
  import gradio as gr
5
  import numpy as np
6
+ import pdfplumber
7
+ import docx
8
+ from typing import List, Optional
9
+
10
  from sentence_transformers import SentenceTransformer
11
  from transformers import pipeline
12
 
13
+ # Utility: Clean text helper
14
+ def clean_text(text: str) -> str:
15
+ text = re.sub(r'\s+', ' ', text) # collapse whitespace
16
+ text = text.strip()
17
+ return text
18
+
19
+ # Text chunking (smaller chunks for better semantic search)
20
+ def chunk_text(text: str, chunk_size: int = 300, overlap: int = 50) -> List[str]:
21
+ words = text.split()
22
+ chunks = []
23
+ start = 0
24
+ while start < len(words):
25
+ end = min(start + chunk_size, len(words))
26
+ chunk = ' '.join(words[start:end])
27
+ chunks.append(clean_text(chunk))
28
+ start += chunk_size - overlap
29
+ return chunks
30
+
31
+ # Document loader for txt, pdf, docx
32
+ def load_document(file_path: str) -> str:
33
+ ext = os.path.splitext(file_path)[1].lower()
34
+ text = ""
35
+ if ext == ".txt":
36
+ with open(file_path, 'r', encoding='utf-8') as f:
37
+ text = f.read()
38
+ elif ext == ".pdf":
39
+ with pdfplumber.open(file_path) as pdf:
40
+ pages = [page.extract_text() for page in pdf.pages if page.extract_text()]
41
+ text = "\n".join(pages)
42
+ elif ext == ".docx":
43
+ doc = docx.Document(file_path)
44
+ paragraphs = [para.text for para in doc.paragraphs if para.text.strip()]
45
+ text = "\n".join(paragraphs)
46
+ else:
47
+ raise ValueError(f"Unsupported file type: {ext}")
48
+ return clean_text(text)
49
 
50
  class SmartDocumentRAG:
51
+ def __init__(self):
52
+ print("Loading embedder and models...")
53
+ self.embedder = SentenceTransformer('all-MiniLM-L6-v2') # small, fast
 
 
 
 
 
54
  self.documents = []
55
+ self.embeddings = None
 
 
 
56
  self.index = None
57
  self.is_indexed = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ # Load QA pipelines
60
+ self.model_type = "distilbert-qa" # change to "flan-t5" for generative
61
+ if self.model_type == "distilbert-qa":
62
+ self.qa_pipeline = pipeline("question-answering", model="distilbert-base-cased-distilled-squad")
63
+ elif self.model_type == "flan-t5":
64
+ self.qa_pipeline = pipeline("text2text-generation", model="google/flan-t5-base")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  else:
66
+ self.qa_pipeline = None
67
 
68
+ self.document_summary = ""
 
 
69
 
70
+ def process_documents(self, files: List[gr.File]) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  if not files:
72
+ return "⚠️ No files uploaded."
73
+ print(f"Processing {len(files)} files...")
74
 
75
+ all_text = ""
76
+ for file in files:
77
+ try:
78
+ # gr.File is a dict-like, get 'name' key for path
79
+ path = file.name if hasattr(file, 'name') else file
80
+ text = load_document(path)
81
+ all_text += text + "\n"
82
+ except Exception as e:
83
+ print(f"Error loading {file}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
+ all_text = clean_text(all_text)
86
+ chunks = chunk_text(all_text)
87
 
88
+ if not chunks:
89
+ return "⚠️ No text extracted from documents."
90
 
91
+ self.documents = chunks
92
+ print(f"Created {len(chunks)} text chunks.")
 
93
 
94
+ # Embed and build FAISS index
95
+ self.embeddings = self.embedder.encode(self.documents, convert_to_numpy=True)
96
+ dimension = self.embeddings.shape[1]
97
+ self.index = faiss.IndexFlatIP(dimension) # Cosine similarity with normalized vectors
98
+ faiss.normalize_L2(self.embeddings)
99
+ self.index.add(self.embeddings)
100
+ self.is_indexed = True
101
 
102
+ # Generate summary (simple: first 3 chunks joined)
103
+ summary_text = " ".join(self.documents[:3])
104
+ self.document_summary = summary_text if summary_text else "Summary not available."
 
 
105
 
106
+ return f"βœ… Processed {len(files)} files and created index with {len(chunks)} chunks."
 
107
 
108
+ def find_relevant_content(self, query: str, k: int = 5) -> str:
 
 
 
109
  if not self.is_indexed:
110
  return ""
111
 
112
+ query_emb = self.embedder.encode([query], convert_to_numpy=True)
113
+ faiss.normalize_L2(query_emb)
114
+ k = min(k, len(self.documents))
115
+ distances, indices = self.index.search(query_emb, k)
 
 
 
 
 
 
 
116
 
117
+ relevant_chunks = []
118
+ for dist, idx in zip(distances[0], indices[0]):
119
+ if dist > 0.1 and idx < len(self.documents):
120
+ relevant_chunks.append(self.documents[idx])
121
+ context = " ".join(relevant_chunks)
122
+ print(f"Found {len(relevant_chunks)} relevant chunks with distances >0.1")
123
+ return context
124
 
125
  def answer_question(self, query: str) -> str:
126
  if not query.strip():
127
  return "❓ Please ask a valid question."
 
128
  if not self.is_indexed:
129
+ return "πŸ“ Please upload and process documents first."
130
+
131
  query_lower = query.lower()
 
132
  if any(word in query_lower for word in ['summary', 'summarize', 'overview', 'about']):
133
+ return f"πŸ“„ Document Summary:\n\n{self.document_summary}"
134
+
 
 
 
135
  context = self.find_relevant_content(query, k=5)
136
+ print(f"Context for query: {context[:500]}...")
137
+
138
  if not context:
139
+ return "πŸ” Sorry, no relevant information found. Try rephrasing your question."
140
+
141
  try:
142
+ if self.model_type == "distilbert-qa":
143
  result = self.qa_pipeline(question=query, context=context)
144
+ print(f"QA pipeline result: {result}")
145
  answer = result.get('answer', '').strip()
146
  score = result.get('score', 0.0)
147
+
148
  if not answer or score < 0.05:
149
+ return "πŸ€” I couldn't find a confident answer based on the documents."
150
+
151
  snippet = context[:300].strip()
152
  if len(context) > 300:
153
  snippet += "..."
 
154
  return f"**Answer:** {answer}\n\n*Context snippet:* {snippet}"
155
+
156
  elif self.model_type == "flan-t5":
157
  prompt = (
158
  f"Answer the question based on the context below.\n\n"
 
160
  f"Question: {query}\nAnswer:"
161
  )
162
  result = self.qa_pipeline(prompt, max_length=200, num_return_sequences=1)
163
+ print(f"Generative pipeline result: {result}")
 
164
  answer = result[0]['generated_text'].replace(prompt, '').strip()
165
  if not answer:
166
+ return "πŸ€” I couldn't find a confident answer based on the documents."
167
  return f"**Answer:** {answer}"
168
+
169
  else:
170
+ return "⚠️ Unsupported model type."
171
+
172
  except Exception as e:
173
+ print(f"Exception in answer_question: {e}")
174
+ return f"❌ Error: {str(e)}"
175
 
176
+ # Create Gradio UI
177
+ def create_interface():
178
+ rag = SmartDocumentRAG()
179
 
180
+ with gr.Blocks(title="🧠 Enhanced Document Q&A") as demo:
181
+ gr.Markdown(
182
+ """
183
+ # 🧠 Enhanced Document Q&A System
184
+ **Features:**
185
+ - Semantic search with FAISS + SentenceTransformer
186
+ - Supports PDF, DOCX, TXT uploads
187
+ - Uses DistilBERT or Flan-T5 for Q&A
188
+ - Shows answer with context snippet
189
+ """
190
+ )
191
 
192
+ with gr.Tab("Upload & Process"):
193
+ file_upload = gr.File(file_types=['.pdf', '.docx', '.txt'], label="Upload Documents", file_count="multiple")
194
+ process_btn = gr.Button("Process Documents")
195
+ process_status = gr.Textbox(label="Processing Status", interactive=False, lines=4)
 
196
 
197
+ process_btn.click(fn=rag.process_documents, inputs=[file_upload], outputs=[process_status])
 
 
 
 
 
198
 
199
+ with gr.Tab("Q&A"):
200
+ question_input = gr.Textbox(label="Ask your question", lines=2, placeholder="Type your question here...")
201
+ ask_btn = gr.Button("Get Answer")
202
+ answer_output = gr.Textbox(label="Answer", lines=8, interactive=False)
 
203
 
204
+ ask_btn.click(fn=rag.answer_question, inputs=[question_input], outputs=[answer_output])
 
 
 
205
 
206
+ with gr.Tab("Summary"):
207
+ summary_btn = gr.Button("Get Document Summary")
208
+ summary_output = gr.Textbox(label="Summary", lines=6, interactive=False)
209
 
210
+ summary_btn.click(fn=lambda: rag.answer_question("summary"), inputs=[], outputs=[summary_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
  return demo
213
 
 
214
  if __name__ == "__main__":
215
  demo = create_interface()
216
  demo.launch(server_name="0.0.0.0", server_port=7860, share=True)