chippyjolly commited on
Commit
8a288b1
·
verified ·
1 Parent(s): 7ea049a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -409
app.py CHANGED
@@ -9,20 +9,16 @@ 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"
@@ -39,48 +35,47 @@ class GroqWrapper(LLM):
39
  run_manager: Optional[CallbackManagerForLLMRun] = None,
40
  **kwargs: Any,
41
  ) -> str:
 
42
  response = self.client.chat.completions.create(
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",
@@ -89,67 +84,63 @@ def upload_pdf(file):
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,370 +148,91 @@ def summarize_pdf(num_points: int = 6) -> str:
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)
@@ -528,7 +240,3 @@ with gr.Blocks(css=css) as demo:
528
 
529
  if __name__ == "__main__":
530
  demo.launch()
531
-
532
-
533
-
534
-
 
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
  from groq import Groq
13
  import urllib.parse
14
+ import feedparser
15
 
16
+ # Load Groq API Key from HuggingFace Secrets
17
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
18
 
19
+ # -------------------------------
20
+ # LangChain Wrapper for Groq LLM
21
+ # -------------------------------
 
 
22
  class GroqWrapper(LLM):
23
  client: Any
24
  model_name: str = "llama-3.3-70b-versatile"
 
35
  run_manager: Optional[CallbackManagerForLLMRun] = None,
36
  **kwargs: Any,
37
  ) -> str:
38
+
39
  response = self.client.chat.completions.create(
40
  messages=[{"role": "user", "content": prompt}],
41
  model=self.model_name,
42
+ temperature=self.temperature
 
43
  )
44
  return response.choices[0].message.content
45
 
46
+ # Globals
47
  vectorstore = None
48
  qa_chain = None
49
  groq_llm = None
50
 
51
+
52
+ # -------------------------------
53
+ # PDF Upload + Processing
54
+ # -------------------------------
55
  def upload_pdf(file):
56
  global vectorstore, qa_chain, groq_llm
57
+
58
  try:
59
+ # Initialize Groq LLM
60
  groq_llm = GroqWrapper(client=Groq(api_key=GROQ_API_KEY))
61
+
62
+ # Extract text from PDF
63
+ text = "".join(page.extract_text() or "" for page in PdfReader(file).pages)
64
+
 
 
65
  if not text.strip():
66
+ return "Error: PDF contains no readable text."
67
 
68
+ # Split into chunks
69
+ splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
70
+ texts = splitter.split_text(text)
 
 
71
 
72
+ # Create vector embeddings
73
  embeddings = HuggingFaceEmbeddings(
74
  model_name="sentence-transformers/all-mpnet-base-v2"
75
  )
 
76
  vectorstore = FAISS.from_texts(texts, embeddings)
77
 
78
+ # Configure RetrievalQA
79
  qa_chain = RetrievalQA.from_chain_type(
80
  llm=groq_llm,
81
  chain_type="stuff",
 
84
  )
85
 
86
  return "PDF processed successfully!"
87
+
88
  except Exception as e:
89
  return f"Error: {str(e)}"
90
 
91
+
92
+ # -------------------------------
93
+ # Ask a Question
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
+ prompt_template = """
103
+ Use the following context to answer the question.
104
+ If unsure, say you do not know.
105
+
106
+ Context:
107
+ {context}
108
+
109
+ Question: {question}
110
+
111
+ Answer:
112
+ """
113
+
114
  custom_prompt = PromptTemplate(
115
  template=prompt_template,
116
  input_variables=["context", "question"]
117
  )
118
+
 
119
  qa_chain.combine_documents_chain.llm_chain.prompt = custom_prompt
120
+
 
121
  result = qa_chain({"query": query}, return_only_outputs=False)
122
+
 
123
  answer = result["result"]
124
  sources = result.get("source_documents", [])
125
+
 
126
  if sources:
127
  source_text = "\n\n---\n".join([
128
+ f"Source {i+1}:\n{doc.page_content[:500]}..."
129
  for i, doc in enumerate(sources)
130
  ])
131
  else:
132
+ source_text = "No sources found."
133
+
134
  return answer, source_text
135
+
136
  except Exception as e:
137
+ return f"Error: {str(e)}", ""
138
 
139
+
140
+ # -------------------------------
141
+ # Summarize PDF
142
+ # -------------------------------
143
+ def summarize_pdf(num_points: int = 6):
 
 
 
 
 
144
  global vectorstore, groq_llm
145
 
146
  if vectorstore is None:
 
148
 
149
  try:
150
  docs = vectorstore.similarity_search("summary", k=5)
 
 
 
151
  context = "\n\n".join([doc.page_content for doc in docs])
152
 
153
+ prompt = f"""
154
+ Summarize the research PDF into {num_points} clear bullet points.
 
 
 
 
 
155
 
156
+ Content:
157
+ {context}
158
+
159
+ Summary:
160
+ """
 
 
 
 
161
 
162
  summary = groq_llm(prompt)
163
  return summary.strip()
164
+
165
  except Exception as e:
166
+ return f"Error: {str(e)}"
167
+
168
 
169
+ # -------------------------------
170
+ # Find Similar Papers (arXiv API)
171
+ # -------------------------------
172
  def find_similar_papers():
173
+ global vectorstore
174
+
175
  if vectorstore is None:
176
  return "Please upload a PDF first."
177
 
178
  try:
179
  docs = vectorstore.similarity_search("abstract or introduction", k=3)
180
+ combined = " ".join([d.page_content for d in docs])
181
+ query_text = " ".join(combined.split()[:40])
182
 
183
+ if len(query_text) < 30:
184
+ query_text = "transformer models for summarization"
 
 
 
 
 
 
 
 
 
 
185
 
186
+ encoded = urllib.parse.quote(query_text)
187
+ url = f"http://export.arxiv.org/api/query?search_query=all:{encoded}&start=0&max_results=2"
188
 
189
  feed = feedparser.parse(url)
 
190
 
191
+ if not feed.entries:
192
+ return "No similar papers found."
193
 
194
+ output = []
195
+ for entry in feed.entries:
196
+ output.append(
197
+ f"**{entry.title}**\n{entry.summary}\n🔗 {entry.link}"
198
+ )
 
199
 
200
+ return "\n\n".join(output)
201
 
202
  except Exception as e:
203
+ return f"Error: {str(e)}"
204
+
205
+
206
+ # -------------------------------
207
+ # UI (Gradio)
208
+ # -------------------------------
209
+ css = """
210
+ /* Your CSS unchanged */
211
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
213
  with gr.Blocks(css=css) as demo:
214
+ gr.Markdown("<div class='header'> 🔬 AI Research Companion 🧠 </div>")
215
+
216
+ with gr.Tabs():
217
+ with gr.TabItem("📄 Upload PDF"):
218
+ file_upload = gr.File(file_types=['.pdf'], label="Upload PDF")
219
+ upload_btn = gr.Button("Process Document")
220
+ status = gr.Textbox(label="Status", interactive=False)
221
+
222
+ with gr.TabItem(" Ask Questions"):
223
+ question = gr.Textbox(label="Your Question")
224
+ ask_btn = gr.Button("Get Answer")
225
+ answer = gr.Textbox(label="Answer")
226
+ citations = gr.Textbox(label="Sources")
227
+
228
+ with gr.TabItem("✍️ Summarize"):
229
+ summary_btn = gr.Button("Generate Summary")
230
+ summary_output = gr.Textbox(label="Summary")
231
+
232
+ with gr.TabItem("🔍 Similar Papers"):
233
+ similar_btn = gr.Button("Find Papers")
234
+ similar_output = gr.Textbox(label="Similar Papers")
235
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  upload_btn.click(upload_pdf, inputs=file_upload, outputs=status)
237
  ask_btn.click(ask_question, inputs=question, outputs=[answer, citations])
238
  summary_btn.click(summarize_pdf, outputs=summary_output)
 
240
 
241
  if __name__ == "__main__":
242
  demo.launch()