chippyjolly commited on
Commit
cef9833
·
verified ·
1 Parent(s): 633adce

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +422 -137
app.py CHANGED
@@ -8,31 +8,30 @@ from langchain.chains import RetrievalQA
8
  from langchain.prompts import PromptTemplate
9
  from langchain_core.language_models.llms import LLM
10
  from langchain_core.callbacks import CallbackManagerForLLMRun
11
- from typing import Optional, List, Any
 
 
12
  from groq import Groq
13
  import urllib.parse
14
- import feedparser
15
 
16
- # Load Groq API Key from environment
17
- GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
18
 
19
 
20
- # -------------------------------
21
- # LangChain Wrapper for Groq LLM
22
- # -------------------------------
 
23
  class GroqWrapper(LLM):
24
  client: Any
25
  model_name: str = "llama-3.3-70b-versatile"
26
  temperature: float = 0.7
27
-
28
  @property
29
  def _llm_type(self) -> str:
30
  return "groq"
31
-
32
- @property
33
- def _identifying_params(self):
34
- return {"model_name": self.model_name}
35
-
36
  def _call(
37
  self,
38
  prompt: str,
@@ -44,51 +43,44 @@ class GroqWrapper(LLM):
44
  messages=[{"role": "user", "content": prompt}],
45
  model=self.model_name,
46
  temperature=self.temperature,
 
47
  )
48
- # Groq SDK returns message as dict
49
- return response.choices[0].message['content']
50
 
51
-
52
- # -------------------------------
53
- # Globals
54
- # -------------------------------
55
  vectorstore = None
56
  qa_chain = None
57
  groq_llm = None
58
 
59
-
60
- # -------------------------------
61
- # PDF Upload + Processing
62
- # -------------------------------
63
  def upload_pdf(file):
64
  global vectorstore, qa_chain, groq_llm
65
-
66
  try:
 
67
  groq_llm = GroqWrapper(client=Groq(api_key=GROQ_API_KEY))
68
-
69
- reader = PdfReader(file)
70
- text = ""
71
- for page in reader.pages:
72
- extracted = page.extract_text()
73
- if extracted:
74
- text += extracted + "\n"
75
-
76
  if not text.strip():
77
- return "Error: PDF contains no readable text."
78
 
79
- splitter = RecursiveCharacterTextSplitter(
 
80
  chunk_size=1000,
81
  chunk_overlap=200
82
- )
83
- chunks = splitter.split_text(text)
84
 
 
85
  embeddings = HuggingFaceEmbeddings(
86
- model_name="sentence-transformers/all-mpnet-base-v2",
87
- model_kwargs={"device": "cpu"}
88
  )
 
 
89
 
90
- vectorstore = FAISS.from_texts(chunks, embeddings)
91
-
92
  qa_chain = RetrievalQA.from_chain_type(
93
  llm=groq_llm,
94
  chain_type="stuff",
@@ -96,63 +88,68 @@ def upload_pdf(file):
96
  return_source_documents=True
97
  )
98
 
99
- return "PDF processed successfully!"
100
-
101
  except Exception as e:
102
  return f"Error: {str(e)}"
103
 
104
-
105
- # -------------------------------
106
- # Ask a Question
107
- # -------------------------------
108
  def ask_question(query):
109
  global qa_chain
110
-
111
  if qa_chain is None:
112
  return "Please upload a PDF first.", ""
113
 
114
  try:
115
- prompt_template = """
116
- Use the following context to answer the question.
117
- If you are unsure, say you do not know.
118
-
119
- Context:
120
- {context}
121
-
122
- Question: {question}
123
-
124
- Answer:
125
- """
 
126
  custom_prompt = PromptTemplate(
127
  template=prompt_template,
128
  input_variables=["context", "question"]
129
  )
130
-
 
131
  qa_chain.combine_documents_chain.llm_chain.prompt = custom_prompt
132
-
 
133
  result = qa_chain({"query": query}, return_only_outputs=False)
134
-
 
135
  answer = result["result"]
136
  sources = result.get("source_documents", [])
137
-
 
138
  if sources:
139
- citations = "\n\n---\n".join([
140
- f"Source {i+1}:\n{doc.page_content[:400]}..."
141
  for i, doc in enumerate(sources)
142
  ])
143
  else:
144
- citations = "No sources available."
145
-
146
- return answer, citations
147
-
148
  except Exception as e:
149
- return f"Error: {str(e)}", ""
150
-
151
-
152
- # -------------------------------
153
- # Summarize PDF
154
- # -------------------------------
155
- def summarize_pdf(num_points: int = 6):
 
 
 
 
 
156
  global vectorstore, groq_llm
157
 
158
  if vectorstore is None:
@@ -160,90 +157,378 @@ def summarize_pdf(num_points: int = 6):
160
 
161
  try:
162
  docs = vectorstore.similarity_search("summary", k=5)
163
- context = "\n\n".join([d.page_content for d in docs])
 
164
 
165
- prompt = f"""
166
- Summarize the research paper into {num_points} clear bullet points.
167
 
168
- Content:
169
- {context}
 
 
 
 
 
170
 
171
- Summary:
172
- """
 
 
 
 
 
 
 
173
 
174
- summary = groq_llm(prompt) # use __call__
175
  return summary.strip()
176
-
177
  except Exception as e:
178
- return f"Error: {str(e)}"
179
 
180
-
181
- # -------------------------------
182
- # Find Similar Papers via arXiv
183
- # -------------------------------
184
  def find_similar_papers():
185
- global vectorstore
186
-
187
  if vectorstore is None:
188
  return "Please upload a PDF first."
189
 
190
  try:
191
- docs = vectorstore.similarity_search("abstract introduction", k=3)
192
- combined = " ".join([d.page_content for d in docs])
193
 
194
- query_text = " ".join(combined.split()[:150]) # more context
195
- if len(query_text) < 20:
196
- query_text = "transformer neural networks research"
197
 
198
- encoded = urllib.parse.quote(query_text)
199
- url = f"http://export.arxiv.org/api/query?search_query=all:{encoded}&start=0&max_results=3"
 
 
 
 
 
 
 
 
 
200
 
201
  feed = feedparser.parse(url)
 
202
 
203
- if not feed.entries:
204
- return "No similar papers found."
205
 
206
  results = []
207
- for entry in feed.entries:
208
- results.append(
209
- f"**{entry.title}**\n{entry.summary}\n🔗 {entry.link}"
210
- )
 
211
 
212
  return "\n\n".join(results)
213
 
214
  except Exception as e:
215
- return f"Error: {str(e)}"
216
-
217
-
218
- # -------------------------------
219
- # Gradio UI
220
- # -------------------------------
221
- with gr.Blocks() as demo:
222
- gr.Markdown("## 🔬 AI Research Assistant — PDF Q&A, Summaries & Similar Papers")
223
-
224
- with gr.Tab("📄 Upload PDF"):
225
- file_upload = gr.File(label="Upload Research PDF", file_types=[".pdf"])
226
- btn = gr.Button("Process PDF")
227
- status = gr.Textbox(label="Status")
228
-
229
- with gr.Tab("❓ Ask Questions"):
230
- question = gr.Textbox(label="Ask a Question")
231
- ask_btn = gr.Button("Get Answer")
232
- answer = gr.Textbox(label="Answer")
233
- cites = gr.Textbox(label="Sources")
234
-
235
- with gr.Tab("📝 Summarize"):
236
- num_points_input = gr.Number(label="Number of Bullet Points", value=6, precision=0)
237
- sm_btn = gr.Button("Summarize PDF")
238
- sm_out = gr.Textbox(label="Summary")
239
-
240
- with gr.Tab("🔍 Similar Papers"):
241
- sim_btn = gr.Button("Find Similar Papers")
242
- sim_out = gr.Textbox(label="Similar Papers")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
- btn.click(upload_pdf, inputs=file_upload, outputs=status)
245
- ask_btn.click(ask_question, inputs=question, outputs=[answer, cites])
246
- sm_btn.click(summarize_pdf, inputs=num_points_input, outputs=sm_out)
247
- sim_btn.click(find_similar_papers, outputs=sim_out)
248
 
249
- demo.launch(share=True)
 
8
  from langchain.prompts import PromptTemplate
9
  from langchain_core.language_models.llms import LLM
10
  from langchain_core.callbacks import CallbackManagerForLLMRun
11
+ from typing import Optional, List, Dict, Any
12
+ import requests
13
+ from dotenv import load_dotenv
14
  from groq import Groq
15
  import urllib.parse
16
+ import feedparser # Added for the new function
17
 
18
+ # Load environment variables
19
+ load_dotenv()
20
 
21
 
22
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
23
+
24
+
25
+ # Custom wrapper for Groq to make it LangChain compatible
26
  class GroqWrapper(LLM):
27
  client: Any
28
  model_name: str = "llama-3.3-70b-versatile"
29
  temperature: float = 0.7
30
+
31
  @property
32
  def _llm_type(self) -> str:
33
  return "groq"
34
+
 
 
 
 
35
  def _call(
36
  self,
37
  prompt: str,
 
43
  messages=[{"role": "user", "content": prompt}],
44
  model=self.model_name,
45
  temperature=self.temperature,
46
+ **kwargs
47
  )
48
+ return response.choices[0].message.content
 
49
 
50
+ # Initialize global variables
 
 
 
51
  vectorstore = None
52
  qa_chain = None
53
  groq_llm = None
54
 
 
 
 
 
55
  def upload_pdf(file):
56
  global vectorstore, qa_chain, groq_llm
57
+
58
  try:
59
+ # Initialize Groq LLM wrapper
60
  groq_llm = GroqWrapper(client=Groq(api_key=GROQ_API_KEY))
61
+
62
+ # PDF Text Extraction
63
+ text = "".join(
64
+ page.extract_text() or ""
65
+ for page in PdfReader(file).pages
66
+ )
 
 
67
  if not text.strip():
68
+ return "Error: No readable text found in PDF"
69
 
70
+ # Text Chunking
71
+ texts = RecursiveCharacterTextSplitter(
72
  chunk_size=1000,
73
  chunk_overlap=200
74
+ ).split_text(text)
 
75
 
76
+ # Using HuggingFace embeddings
77
  embeddings = HuggingFaceEmbeddings(
78
+ model_name="sentence-transformers/all-mpnet-base-v2"
 
79
  )
80
+
81
+ vectorstore = FAISS.from_texts(texts, embeddings)
82
 
83
+ # QA System Initialization
 
84
  qa_chain = RetrievalQA.from_chain_type(
85
  llm=groq_llm,
86
  chain_type="stuff",
 
88
  return_source_documents=True
89
  )
90
 
91
+ return "PDF processed successfully!"
 
92
  except Exception as e:
93
  return f"Error: {str(e)}"
94
 
 
 
 
 
95
  def ask_question(query):
96
  global qa_chain
97
+
98
  if qa_chain is None:
99
  return "Please upload a PDF first.", ""
100
 
101
  try:
102
+ # Create a custom prompt template for better answers
103
+ prompt_template = """Use the following pieces of context to answer the question at the end.
104
+ If you don't know the answer, just say that you don't know, don't try to make up an answer.
105
+ Provide a detailed, accurate response with proper formatting.
106
+
107
+ Context:
108
+ {context}
109
+
110
+ Question: {question}
111
+
112
+ Helpful Answer:"""
113
+
114
  custom_prompt = PromptTemplate(
115
  template=prompt_template,
116
  input_variables=["context", "question"]
117
  )
118
+
119
+ # Configure the QA chain with our custom prompt
120
  qa_chain.combine_documents_chain.llm_chain.prompt = custom_prompt
121
+
122
+ # Execute the query
123
  result = qa_chain({"query": query}, return_only_outputs=False)
124
+
125
+ # Extract answer and sources
126
  answer = result["result"]
127
  sources = result.get("source_documents", [])
128
+
129
+ # Format the sources for display
130
  if sources:
131
+ source_text = "\n\n---\n".join([
132
+ f"Source {i+1}:\n{doc.page_content[:500]}{'...' if len(doc.page_content) > 500 else ''}"
133
  for i, doc in enumerate(sources)
134
  ])
135
  else:
136
+ source_text = "No sources cited"
137
+
138
+ return answer, source_text
139
+
140
  except Exception as e:
141
+ return f"Error processing your question: {str(e)}", ""
142
+
143
+ def summarize_pdf(num_points: int = 6) -> str:
144
+ """
145
+ Summarizes the uploaded PDF using the Groq LLM with a creative prompt.
146
+
147
+ Args:
148
+ num_points (int): Number of bullet points for the summary (default: 6).
149
+
150
+ Returns:
151
+ str: The summary or an error message.
152
+ """
153
  global vectorstore, groq_llm
154
 
155
  if vectorstore is None:
 
157
 
158
  try:
159
  docs = vectorstore.similarity_search("summary", k=5)
160
+ if not docs:
161
+ return "No content found to summarize."
162
 
163
+ context = "\n\n".join([doc.page_content for doc in docs])
 
164
 
165
+ prompt = (
166
+ "Imagine you are a passionate science communicator tasked with revealing the essence of a groundbreaking research paper.\n"
167
+ f"Craft a captivating summary in {num_points} vivid bullet points that not only highlights the core discoveries but also paints a clear picture of their significance.\n"
168
+ "Make it engaging, insightful, and accessible to a curious reader eager to grasp the impact of this work.\n\n"
169
+ f"Here is the paper content:\n{context}\n\n"
170
+ "Your inspired summary:"
171
+ )
172
 
173
+ if groq_llm is None:
174
+ from groq import Groq
175
+ groq_llm = GroqWrapper(
176
+ client=Groq(
177
+ api_key=os.getenv("GROQ_API_KEY"),
178
+ model="llama-3.3-70b-versatile"
179
+ ),
180
+ model_name="llama-3.3-70b-versatile"
181
+ )
182
 
183
+ summary = groq_llm(prompt)
184
  return summary.strip()
185
+
186
  except Exception as e:
187
+ return f"Error during summarization: {str(e)}"
188
 
189
+ # *** Modified find_similar_papers function ONLY ***
 
 
 
190
  def find_similar_papers():
 
 
191
  if vectorstore is None:
192
  return "Please upload a PDF first."
193
 
194
  try:
195
+ docs = vectorstore.similarity_search("abstract or introduction", k=3)
 
196
 
197
+ # Combine chunks and take the first 40 words total
198
+ combined_text = " ".join([doc.page_content for doc in docs])
199
+ query_text = " ".join(combined_text.split()[:40])
200
 
201
+ # Fallback if query_text is too short or citation-heavy
202
+ if len(query_text) < 30 or "arXiv" in query_text or "[" in query_text:
203
+ query_text = "transformer models for abstractive text summarization"
204
+
205
+ encoded_query = urllib.parse.quote(query_text)
206
+
207
+ # Build arXiv API query
208
+ url = f"http://export.arxiv.org/api/query?search_query=all:{encoded_query}&start=0&max_results=2"
209
+
210
+ print("Querying arXiv with:", query_text)
211
+ print("URL:", url)
212
 
213
  feed = feedparser.parse(url)
214
+ entries = feed.entries
215
 
216
+ if not entries:
217
+ return f"No similar papers found for query: **{query_text}**"
218
 
219
  results = []
220
+ for entry in entries:
221
+ title = entry.title
222
+ summary = entry.summary.replace('\n', ' ').strip()
223
+ link = entry.link
224
+ results.append(f"**{title}**\n{summary}\n🔗 {link}")
225
 
226
  return "\n\n".join(results)
227
 
228
  except Exception as e:
229
+ return f"Error fetching similar papers: {str(e)}"
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+ css = '''
238
+ :root {
239
+ --primary: #6e48aa;
240
+ --secondary: #9d50bb;
241
+ --accent: #4776e6;
242
+ --dark: #1a1a2e;
243
+ --darker: #16213e;
244
+ --light: #f8f9fa;
245
+ --success: #4caf50;
246
+ --warning: #ff9800;
247
+ --danger: #f44336;
248
+ }
249
+
250
+ body, .gradio-container {
251
+ margin: 0;
252
+ padding: 0;
253
+ font-family: 'Segoe UI', 'Roboto', sans-serif;
254
+ background: linear-gradient(135deg, var(--dark), var(--darker));
255
+ color: var(--light);
256
+ min-height: 100vh;
257
+ }
258
+
259
+ .header {
260
+ text-align: center;
261
+ padding: 1.5rem 0;
262
+ margin-bottom: 2rem;
263
+ color: white; /* Make text white */
264
+ font-size: 3rem;
265
+ font-weight: 800;
266
+ letter-spacing: 1px;
267
+ font-style: italic; /* Make it italic */
268
+ text-shadow: 0 2px 10px rgba(0,0,0,0.2);
269
+ }
270
+
271
+
272
+ .nav-tabs {
273
+ display: flex;
274
+ justify-content: center;
275
+ margin-bottom: 2rem;
276
+ gap: 1rem;
277
+ }
278
+
279
+ .tab-button {
280
+ background: rgba(255,255,255,0.1);
281
+ border: none;
282
+ padding: 0.8rem 1.5rem;
283
+ border-radius: 50px;
284
+ color: white;
285
+ font-weight: 600;
286
+ cursor: pointer;
287
+ transition: all 0.3s ease;
288
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
289
+ }
290
+
291
+ .tab-button:hover {
292
+ background: rgba(255,255,255,0.2);
293
+ transform: translateY(-2px);
294
+ }
295
+
296
+ .tab-button.active {
297
+ background: linear-gradient(45deg, var(--primary), var(--accent));
298
+ box-shadow: 0 4px 15px rgba(110, 72, 170, 0.4);
299
+ }
300
+
301
+ .tab-content {
302
+ display: none;
303
+ animation: fadeIn 0.5s ease-out;
304
+ }
305
+
306
+ .tab-content.active {
307
+ display: block;
308
+ }
309
+
310
+ .panel {
311
+ background: rgba(255,255,255,0.05);
312
+ border-radius: 16px;
313
+ padding: 2rem;
314
+ margin: 1rem auto;
315
+ max-width: 900px;
316
+ backdrop-filter: blur(10px);
317
+ border: 1px solid rgba(255,255,255,0.1);
318
+ box-shadow: 0 8px 32px rgba(0,0,0,0.2);
319
+ }
320
+
321
+ .panel-header {
322
+ font-size: 1.5rem;
323
+ font-weight: 700;
324
+ margin-bottom: 1.5rem;
325
+ color: white;
326
+ display: flex;
327
+ align-items: center;
328
+ gap: 0.8rem;
329
+ }
330
+
331
+ .panel-header svg {
332
+ width: 1.5rem;
333
+ height: 1.5rem;
334
+ }
335
+
336
+ button {
337
+ background: linear-gradient(45deg, var(--primary), var(--secondary));
338
+ color: white;
339
+ border: none;
340
+ padding: 0.8rem 1.5rem;
341
+ border-radius: 50px;
342
+ font-weight: 600;
343
+ cursor: pointer;
344
+ transition: all 0.3s ease;
345
+ box-shadow: 0 4px 15px rgba(110, 72, 170, 0.3);
346
+ margin: 0.5rem 0;
347
+ }
348
+
349
+ button:hover {
350
+ transform: translateY(-2px);
351
+ box-shadow: 0 6px 20px rgba(110, 72, 170, 0.4);
352
+ }
353
+
354
+ button:active {
355
+ transform: translateY(0);
356
+ }
357
+
358
+ button.secondary {
359
+ background: rgba(255,255,255,0.1);
360
+ }
361
+
362
+ button.secondary:hover {
363
+ background: rgba(255,255,255,0.2);
364
+ }
365
+
366
+ textarea, input[type="text"] {
367
+ background: rgba(255,255,255,0.1);
368
+ border: 1px solid rgba(255,255,255,0.2);
369
+ color: white;
370
+ border-radius: 8px;
371
+ padding: 0.8rem;
372
+ width: 100%;
373
+ margin-bottom: 1rem;
374
+ }
375
+
376
+ textarea:focus, input[type="text"]:focus {
377
+ outline: none;
378
+ border-color: var(--accent);
379
+ box-shadow: 0 0 0 2px rgba(71, 118, 230, 0.3);
380
+ }
381
+
382
+ .output-box {
383
+ background: rgba(0,0,0,0.3);
384
+ border-radius: 8px;
385
+ padding: 1rem;
386
+ margin-top: 1rem;
387
+ border-left: 4px solid var(--accent);
388
+ }
389
+
390
+ .output-label {
391
+ font-weight: 600;
392
+ margin-bottom: 0.5rem;
393
+ display: block;
394
+ color: #ddd;
395
+ }
396
+
397
+ @keyframes fadeIn {
398
+ from { opacity: 0; transform: translateY(10px); }
399
+ to { opacity: 1; transform: translateY(0); }
400
+ }
401
+
402
+ .slide-in {
403
+ animation: slideIn 0.5s ease-out forwards;
404
+ }
405
+
406
+ @keyframes slideIn {
407
+ from { transform: translateX(100%); opacity: 0; }
408
+ to { transform: translateX(0); opacity: 1; }
409
+ }
410
+
411
+ .file-upload {
412
+ border: 2px dashed rgba(255,255,255,0.3);
413
+ border-radius: 8px;
414
+ padding: 2rem;
415
+ text-align: center;
416
+ margin-bottom: 1rem;
417
+ transition: all 0.3s ease;
418
+ }
419
+
420
+ .file-upload:hover {
421
+ border-color: var(--accent);
422
+ background: rgba(71, 118, 230, 0.1);
423
+ }
424
+
425
+ .progress-bar {
426
+ height: 6px;
427
+ background: rgba(255,255,255,0.1);
428
+ border-radius: 3px;
429
+ margin-top: 1rem;
430
+ overflow: hidden;
431
+ }
432
+
433
+ .progress {
434
+ height: 100%;
435
+ background: linear-gradient(90deg, var(--primary), var(--accent));
436
+ width: 0%;
437
+ transition: width 0.3s ease;
438
+ }
439
+ '''
440
+
441
+ with gr.Blocks(css=css) as demo:
442
+ gr.Markdown("""
443
+ <div class='header'>
444
+ <span style="font-size:1.2em">🔬</span> AI Research Companion
445
+ <span style="font-size:1.2em">🧠</span>
446
+ </div>
447
+ """)
448
+
449
+ with gr.Tabs() as tabs:
450
+ with gr.TabItem("📄 Upload PDF", id="upload"):
451
+ with gr.Column(elem_classes=["panel"]):
452
+ gr.Markdown("""<div class="panel-header">
453
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
454
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
455
+ </svg>
456
+ Document Processing
457
+ </div>""")
458
+
459
+ with gr.Column(elem_classes=["file-upload"]):
460
+ file_upload = gr.File(
461
+ file_types=['.pdf'],
462
+ label="Drag & Drop PDF or Click to Browse",
463
+ elem_classes=["upload-box"]
464
+ )
465
+ upload_btn = gr.Button("Process Document", variant="primary")
466
+ status = gr.Textbox(label="Processing Status", interactive=False)
467
+ gr.Markdown("<div class='progress-bar'><div class='progress'></div></div>")
468
+
469
+ with gr.TabItem("❓ Ask Questions", id="qa"):
470
+ with gr.Column(elem_classes=["panel"]):
471
+ gr.Markdown("""<div class="panel-header">
472
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
473
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
474
+ </svg>
475
+ Research Q&A
476
+ </div>""")
477
+
478
+ question = gr.Textbox(
479
+ placeholder="Type your research question here...",
480
+ label="Your Question",
481
+ lines=3
482
+ )
483
+ ask_btn = gr.Button("Get Answer", variant="primary")
484
+
485
+ with gr.Column(elem_classes=["output-box"]):
486
+ gr.Markdown("<div class='output-label'>Answer</div>")
487
+ answer = gr.Textbox(show_label=False, lines=6, interactive=False)
488
+
489
+ with gr.Column(elem_classes=["output-box"]):
490
+ gr.Markdown("<div class='output-label'>Source References</div>")
491
+ citations = gr.Textbox(show_label=False, lines=4, interactive=False)
492
+
493
+ with gr.TabItem("✍️ Summarize", id="summary"):
494
+ with gr.Column(elem_classes=["panel"]):
495
+ gr.Markdown("""<div class="panel-header">
496
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
497
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7" />
498
+ </svg>
499
+ Document Summary
500
+ </div>""")
501
+
502
+ summary_btn = gr.Button("Generate Summary", variant="primary")
503
+
504
+ with gr.Column(elem_classes=["output-box"]):
505
+ gr.Markdown("<div class='output-label'>Key Insights</div>")
506
+ summary_output = gr.Textbox(show_label=False, lines=8, interactive=False)
507
+
508
+ with gr.TabItem("🔍 Similar Papers", id="papers"):
509
+ with gr.Column(elem_classes=["panel"]):
510
+ gr.Markdown("""<div class="panel-header">
511
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
512
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
513
+ </svg>
514
+ Related Research
515
+ </div>""")
516
+
517
+ similar_btn = gr.Button("Find Similar Papers", variant="primary")
518
+
519
+ with gr.Column(elem_classes=["output-box"]):
520
+ gr.Markdown("<div class='output-label'>Recommended Papers</div>")
521
+ similar_output = gr.Textbox(show_label=False, lines=8, interactive=False)
522
+
523
+ # Event handlers
524
+ upload_btn.click(upload_pdf, inputs=file_upload, outputs=status)
525
+ ask_btn.click(ask_question, inputs=question, outputs=[answer, citations])
526
+ summary_btn.click(summarize_pdf, outputs=summary_output)
527
+ similar_btn.click(find_similar_papers, outputs=similar_output)
528
+
529
+ if __name__ == "__main__":
530
+ demo.launch()
531
+
532
+
533
 
 
 
 
 
534