BasitAliii commited on
Commit
f1b6b8c
Β·
verified Β·
1 Parent(s): eba516d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +615 -0
app.py ADDED
@@ -0,0 +1,615 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import tempfile
4
+ import gradio as gr
5
+ from transformers import pipeline
6
+ import pdfplumber
7
+ from gtts import gTTS
8
+ import nltk
9
+ import numpy as np
10
+ from sklearn.feature_extraction.text import TfidfVectorizer
11
+ from pydub import AudioSegment
12
+ import faiss
13
+ from sentence_transformers import SentenceTransformer
14
+ from groq import Groq
15
+ from diffusers import StableDiffusionPipeline
16
+ import torch
17
+ from PIL import Image
18
+
19
+ # ==========================================================
20
+ # 🧠 NLTK Setup
21
+ # ==========================================================
22
+ for pkg in ["punkt", "punkt_tab"]:
23
+ try:
24
+ nltk.data.find(f"tokenizers/{pkg}")
25
+ except LookupError:
26
+ nltk.download(pkg)
27
+
28
+ # ==========================================================
29
+ # πŸ” Environment Setup
30
+ # ==========================================================
31
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
32
+
33
+ # ==========================================================
34
+ # βš™οΈ Model Setup
35
+ # ==========================================================
36
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
37
+ print(f"Using device: {DEVICE}")
38
+
39
+ # Initialize models
40
+ print("Loading models... please wait ⏳")
41
+
42
+ # Summarization model
43
+ SUMMARIZER_MODEL = "facebook/bart-large-cnn"
44
+ try:
45
+ summarizer = pipeline("summarization", model=SUMMARIZER_MODEL)
46
+ print("βœ… Summarizer loaded successfully.")
47
+ except Exception as e:
48
+ print("❌ Summarizer load error:", e)
49
+ summarizer = None
50
+
51
+ # Embedding model for RAG
52
+ try:
53
+ embedder = SentenceTransformer('all-MiniLM-L6-v2')
54
+ print("βœ… Embedding model loaded successfully.")
55
+ except Exception as e:
56
+ print("❌ Embedding model load error:", e)
57
+ embedder = None
58
+
59
+ # Stable Diffusion for diagram generation
60
+ try:
61
+ if torch.cuda.is_available():
62
+ sd_pipe = StableDiffusionPipeline.from_pretrained(
63
+ "runwayml/stable-diffusion-v1-5",
64
+ torch_dtype=torch.float16,
65
+ safety_checker=None,
66
+ requires_safety_checker=False
67
+ )
68
+ sd_pipe = sd_pipe.to("cuda")
69
+ else:
70
+ sd_pipe = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
71
+ sd_pipe = sd_pipe.to("cpu")
72
+ print("βœ… Stable Diffusion loaded successfully.")
73
+ except Exception as e:
74
+ print("❌ Stable Diffusion load error:", e)
75
+ sd_pipe = None
76
+
77
+ # Groq client
78
+ try:
79
+ groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
80
+ if groq_client:
81
+ print("βœ… Groq client initialized successfully.")
82
+ else:
83
+ print("⚠️ Groq API key not found. Chat functionality will be limited.")
84
+ except Exception as e:
85
+ print("❌ Groq client initialization error:", e)
86
+ groq_client = None
87
+
88
+ # ==========================================================
89
+ # 🧩 Utility Functions
90
+ # ==========================================================
91
+ def clean_text(text: str) -> str:
92
+ """Clean extracted PDF text."""
93
+ text = re.sub(r'\r\n?', '\n', text)
94
+ text = re.sub(r'\n{2,}', '\n\n', text)
95
+ text = re.sub(r'References[\s\S]*', '', text, flags=re.IGNORECASE)
96
+ text = re.sub(r'[^\x00-\x7F]+', ' ', text)
97
+ text = re.sub(r'\s+', ' ', text)
98
+ return text.strip()
99
+
100
+ def extract_text_from_pdf(path: str) -> str:
101
+ """Extract text from all pages of a PDF."""
102
+ try:
103
+ text = ""
104
+ with pdfplumber.open(path) as pdf:
105
+ for page in pdf.pages:
106
+ page_text = page.extract_text()
107
+ if page_text:
108
+ text += page_text + "\n\n"
109
+ return text.strip() if text.strip() else "No text extracted from PDF."
110
+ except Exception as e:
111
+ return f"Error extracting text: {e}"
112
+
113
+ def sentence_tokenize(text: str):
114
+ """Split text into sentences."""
115
+ return [s.strip() for s in nltk.tokenize.sent_tokenize(text) if len(s.strip()) > 10]
116
+
117
+ def chunk_text(text: str, max_chars=1500):
118
+ """Split text into chunks for summarization."""
119
+ sents = sentence_tokenize(text)
120
+ chunks, cur = [], ""
121
+ for s in sents:
122
+ if len(cur) + len(s) < max_chars:
123
+ cur += (" " if cur else "") + s
124
+ else:
125
+ chunks.append(cur)
126
+ cur = s
127
+ if cur:
128
+ chunks.append(cur)
129
+ return chunks
130
+
131
+ def extract_keywords_tfidf(text: str, top_k=8):
132
+ """Extract keywords using TF-IDF."""
133
+ try:
134
+ paras = [p.strip() for p in re.split(r'\n{2,}', text) if len(p.strip()) > 0]
135
+ vectorizer = TfidfVectorizer(stop_words='english', ngram_range=(1, 2))
136
+ X = vectorizer.fit_transform(paras)
137
+ features = vectorizer.get_feature_names_out()
138
+ scores = np.asarray(X.mean(axis=0)).ravel()
139
+ idx = np.argsort(scores)[::-1][:top_k]
140
+ return [features[i] for i in idx]
141
+ except Exception:
142
+ return []
143
+
144
+ # ==========================================================
145
+ # ✍️ Adaptive Summarization
146
+ # ==========================================================
147
+ def summarize_long_text(text: str) -> str:
148
+ """Adaptive summarization based on PDF length."""
149
+ if summarizer is None:
150
+ return "Summarization model unavailable."
151
+
152
+ text = clean_text(text)
153
+ L = len(text)
154
+
155
+ # Dynamic summarization scaling
156
+ if L < 1500:
157
+ max_len, min_len, chunk_size = 180, 60, 1400
158
+ elif L < 5000:
159
+ max_len, min_len, chunk_size = 250, 100, 1600
160
+ elif L < 15000:
161
+ max_len, min_len, chunk_size = 350, 150, 1800
162
+ else:
163
+ max_len, min_len, chunk_size = 500, 200, 2000
164
+
165
+ if L <= chunk_size:
166
+ return summarizer(text, max_length=max_len, min_length=min_len, do_sample=False)[0]["summary_text"]
167
+
168
+ parts = chunk_text(text, max_chars=chunk_size)[:6]
169
+ summaries = []
170
+ for p in parts:
171
+ try:
172
+ summaries.append(summarizer(p, max_length=200, min_length=80, do_sample=False)[0]["summary_text"])
173
+ except Exception:
174
+ continue
175
+
176
+ combined = " ".join(summaries)
177
+ final = summarizer(combined, max_length=max_len, min_length=min_len, do_sample=False)[0]["summary_text"]
178
+ return final
179
+
180
+ # ==========================================================
181
+ # πŸ–ΌοΈ Diagram Generation with Stable Diffusion
182
+ # ==========================================================
183
+ def generate_diagram(summary: str, keywords: str) -> Image.Image:
184
+ """Generate a diagram based on summary and keywords."""
185
+ if sd_pipe is None:
186
+ return None
187
+
188
+ try:
189
+ # Create a prompt for diagram generation
190
+ prompt = f"educational diagram, infographic style, clean and professional, illustrating: {summary[:500]}. Keywords: {keywords}"
191
+
192
+ # Generate image
193
+ with torch.no_grad():
194
+ if torch.cuda.is_available():
195
+ image = sd_pipe(
196
+ prompt,
197
+ num_inference_steps=25,
198
+ guidance_scale=7.5,
199
+ width=512,
200
+ height=512
201
+ ).images[0]
202
+ else:
203
+ image = sd_pipe(
204
+ prompt,
205
+ num_inference_steps=15,
206
+ guidance_scale=7.5,
207
+ width=512,
208
+ height=512
209
+ ).images[0]
210
+
211
+ return image
212
+ except Exception as e:
213
+ print(f"Diagram generation error: {e}")
214
+ return None
215
+
216
+ # ==========================================================
217
+ # πŸ’¬ RAG Chatbot Functions
218
+ # ==========================================================
219
+ class PDFChatBot:
220
+ def __init__(self):
221
+ self.vector_store = None
222
+ self.chunks = []
223
+ self.current_pdf_text = ""
224
+ self.is_processed = False
225
+
226
+ def process_pdf_for_chat(self, pdf_text: str):
227
+ """Process PDF text for RAG system."""
228
+ if not pdf_text or pdf_text.startswith("Error") or pdf_text.startswith("No text"):
229
+ return False
230
+
231
+ self.current_pdf_text = clean_text(pdf_text)
232
+
233
+ # Chunk the text
234
+ self.chunks = self._create_chunks(self.current_pdf_text, chunk_size=500, overlap=50)
235
+
236
+ # Create embeddings
237
+ if embedder is not None and self.chunks:
238
+ embeddings = embedder.encode(self.chunks)
239
+
240
+ # Create FAISS index
241
+ dimension = embeddings.shape[1]
242
+ self.vector_store = faiss.IndexFlatIP(dimension)
243
+
244
+ # Normalize embeddings for cosine similarity
245
+ faiss.normalize_L2(embeddings)
246
+ self.vector_store.add(embeddings)
247
+
248
+ self.is_processed = True
249
+ return True
250
+ return False
251
+
252
+ def _create_chunks(self, text: str, chunk_size: int = 500, overlap: int = 50):
253
+ """Create overlapping chunks of text."""
254
+ sentences = sentence_tokenize(text)
255
+ chunks = []
256
+ current_chunk = ""
257
+
258
+ for sentence in sentences:
259
+ if len(current_chunk) + len(sentence) <= chunk_size:
260
+ current_chunk += " " + sentence
261
+ else:
262
+ if current_chunk:
263
+ chunks.append(current_chunk.strip())
264
+ current_chunk = sentence
265
+
266
+ if current_chunk:
267
+ chunks.append(current_chunk.strip())
268
+
269
+ return chunks
270
+
271
+ def get_relevant_chunks(self, query: str, top_k: int = 3):
272
+ """Retrieve relevant chunks for a query."""
273
+ if self.vector_store is None or not self.chunks:
274
+ return []
275
+
276
+ try:
277
+ # Encode query
278
+ query_embedding = embedder.encode([query])
279
+ faiss.normalize_L2(query_embedding)
280
+
281
+ # Search
282
+ scores, indices = self.vector_store.search(query_embedding, top_k)
283
+
284
+ # Return relevant chunks
285
+ relevant_chunks = []
286
+ for i, score in zip(indices[0], scores[0]):
287
+ if i < len(self.chunks) and score > 0.3: # similarity threshold
288
+ relevant_chunks.append(self.chunks[i])
289
+
290
+ return relevant_chunks
291
+ except Exception as e:
292
+ print(f"Error in retrieval: {e}")
293
+ return []
294
+
295
+ def generate_answer(self, query: str, chat_history):
296
+ """Generate answer using RAG with Groq."""
297
+ if groq_client is None:
298
+ return "Groq API not available. Please set your GROQ_API_KEY in the Hugging Face Spaces secrets."
299
+
300
+ if not self.is_processed:
301
+ return "Please upload and process a PDF first. Go to the 'PDF Summarizer' tab to upload your PDF."
302
+
303
+ # Get relevant context
304
+ relevant_chunks = self.get_relevant_chunks(query)
305
+
306
+ if not relevant_chunks:
307
+ return "No relevant information found in the PDF for your question."
308
+
309
+ context = "\n\n".join(relevant_chunks[:3]) # Use top 3 chunks
310
+
311
+ # Create prompt
312
+ prompt = f"""Based on the following context from a PDF document, please answer the user's question.
313
+
314
+ Context:
315
+ {context}
316
+
317
+ Question: {query}
318
+
319
+ Please provide a helpful and accurate answer based only on the given context. If the context doesn't contain enough information to fully answer the question, please say so."""
320
+
321
+ try:
322
+ # Try different available Groq models
323
+ available_models = [
324
+ "llama-3.3-70b-versatile",
325
+ "llama-3.1-8b-instant",
326
+ "llama-3.2-3b-preview",
327
+ "llama-3.2-1b-preview",
328
+ "mixtral-8x7b-32768"
329
+ ]
330
+
331
+ for model in available_models:
332
+ try:
333
+ completion = groq_client.chat.completions.create(
334
+ model=model,
335
+ messages=[{"role": "user", "content": prompt}],
336
+ temperature=0.7,
337
+ max_tokens=1024,
338
+ top_p=1,
339
+ stream=False
340
+ )
341
+
342
+ answer = completion.choices[0].message.content
343
+ return answer
344
+
345
+ except Exception as model_error:
346
+ print(f"Model {model} failed: {model_error}")
347
+ continue
348
+
349
+ return "All available models failed. Please check your Groq API access."
350
+
351
+ except Exception as e:
352
+ return f"Error generating answer: {str(e)}"
353
+
354
+ # Initialize chatbot
355
+ chatbot = PDFChatBot()
356
+
357
+ # ==========================================================
358
+ # πŸ”Š Text-to-Speech
359
+ # ==========================================================
360
+ def text_to_speech(text):
361
+ """Convert text to speech and ensure WAV output."""
362
+ if not text:
363
+ return None
364
+ try:
365
+ # Temporary paths
366
+ mp3_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3").name
367
+ wav_path = tempfile.NamedTemporaryFile(delete=False, suffix=".wav").name
368
+
369
+ # Generate TTS (MP3)
370
+ gTTS(text=text[:900], lang="en").save(mp3_path)
371
+
372
+ # Convert to WAV for browser playback
373
+ AudioSegment.from_mp3(mp3_path).export(wav_path, format="wav")
374
+
375
+ # Clean up MP3 file
376
+ os.unlink(mp3_path)
377
+
378
+ return wav_path
379
+ except Exception as e:
380
+ print("TTS error:", e)
381
+ return None
382
+
383
+ # ==========================================================
384
+ # πŸ“„ PDF Processing - Main Function
385
+ # ==========================================================
386
+ def process_pdf(pdf_file):
387
+ """Main handler to process PDF - this will be shared across all tabs."""
388
+ if not pdf_file:
389
+ return "Please upload a PDF.", "", None, "", None, "No PDF uploaded"
390
+
391
+ text = extract_text_from_pdf(pdf_file)
392
+ if text.startswith("Error") or text.startswith("No text"):
393
+ return text, "", None, "", None, "Failed to extract text"
394
+
395
+ text = clean_text(text)
396
+ summary = summarize_long_text(text)
397
+ keywords = ", ".join(extract_keywords_tfidf(text))
398
+ audio = text_to_speech(summary)
399
+
400
+ # Generate diagram
401
+ diagram = generate_diagram(summary, keywords)
402
+
403
+ # Also process for chatbot
404
+ chatbot.process_pdf_for_chat(text)
405
+
406
+ # Return status message for chat tab
407
+ status_message = "βœ… PDF processed successfully! You can now chat with this PDF in the 'Chat with PDF' tab."
408
+
409
+ return text, summary, audio, keywords, diagram, status_message
410
+
411
+ # ==========================================================
412
+ # πŸš€ Gradio Interface with Shared PDF State
413
+ # ==========================================================
414
+ def create_interface():
415
+ with gr.Blocks(
416
+ title="AI PDF Summarizer Pro",
417
+ theme=gr.themes.Soft()
418
+ ) as demo:
419
+
420
+ gr.Markdown("""
421
+ # AI PDF Summarizer Pro
422
+ *Upload once, use everywhere across all tabs*
423
+ """)
424
+
425
+ # --- Main Tab: PDF Summarizer ---
426
+ with gr.Tab("πŸ“„ PDF Summarizer"):
427
+ with gr.Row():
428
+ with gr.Column(scale=1):
429
+ gr.Markdown("### Upload Your PDF")
430
+ gr.Markdown("Upload a PDF here and it will be automatically available in all other tabs.")
431
+ pdf_input = gr.File(
432
+ label="Upload PDF Document",
433
+ file_types=[".pdf"],
434
+ type="filepath"
435
+ )
436
+ process_btn = gr.Button(
437
+ "Process PDF",
438
+ variant="primary",
439
+ size="lg"
440
+ )
441
+
442
+ with gr.Column(scale=2):
443
+ with gr.Accordion("Extracted Text", open=False):
444
+ extracted_text = gr.Textbox(
445
+ label="",
446
+ lines=8,
447
+ interactive=False,
448
+ show_copy_button=True
449
+ )
450
+
451
+ with gr.Row():
452
+ with gr.Column():
453
+ summary_box = gr.Textbox(
454
+ label="AI Summary",
455
+ lines=4,
456
+ interactive=False,
457
+ show_copy_button=True
458
+ )
459
+ with gr.Column():
460
+ keywords_box = gr.Textbox(
461
+ label="Top Keywords",
462
+ lines=2,
463
+ interactive=False
464
+ )
465
+
466
+ with gr.Row():
467
+ with gr.Column():
468
+ audio_box = gr.Audio(
469
+ label="Summary Audio",
470
+ type="filepath",
471
+ interactive=False
472
+ )
473
+ with gr.Column():
474
+ diagram_box = gr.Image(
475
+ label="AI Generated Diagram",
476
+ interactive=False,
477
+ height=200
478
+ )
479
+
480
+ # Status message
481
+ status_display = gr.HTML(
482
+ value="<div>No PDF processed yet. Upload a PDF and click 'Process PDF'.</div>"
483
+ )
484
+
485
+ # --- Tab: AI Diagram Generator ---
486
+ with gr.Tab("πŸ–ΌοΈ AI Diagram"):
487
+ with gr.Row():
488
+ with gr.Column():
489
+ gr.Markdown("### Create Diagram")
490
+ gr.Markdown("Create diagrams using the summary from your uploaded PDF or enter custom text.")
491
+ diagram_summary_input = gr.Textbox(
492
+ label="Summary Text",
493
+ lines=3,
494
+ placeholder="Text from your PDF summary will appear here after processing..."
495
+ )
496
+ diagram_keywords_input = gr.Textbox(
497
+ label="Keywords (optional)",
498
+ placeholder="Keywords from your PDF will appear here..."
499
+ )
500
+ generate_diagram_btn = gr.Button(
501
+ "Generate Diagram",
502
+ variant="primary"
503
+ )
504
+
505
+ with gr.Column():
506
+ gr.Markdown("### Generated Diagram")
507
+ diagram_output = gr.Image(
508
+ label="",
509
+ interactive=False,
510
+ height=400,
511
+ show_download_button=True
512
+ )
513
+
514
+ # --- Tab: Chat with PDF ---
515
+ with gr.Tab("πŸ’¬ Chat with PDF"):
516
+ with gr.Row():
517
+ with gr.Column(scale=1):
518
+ gr.Markdown("### Chat with Your PDF")
519
+ gr.Markdown("""
520
+ **Ask questions about your uploaded PDF**
521
+
522
+ Simply go to the **PDF Summarizer** tab, upload and process your PDF, then come back here to start chatting!
523
+ """)
524
+
525
+ # Display current PDF status
526
+ chat_status_display = gr.HTML(
527
+ value="<div>Please upload and process a PDF in the 'PDF Summarizer' tab first.</div>"
528
+ )
529
+
530
+ with gr.Column(scale=2):
531
+ chatbot_interface = gr.ChatInterface(
532
+ fn=chatbot.generate_answer,
533
+ title="Chat with Your PDF",
534
+ description="Ask questions about the content of your uploaded PDF document",
535
+ examples=[
536
+ "What is the main topic of this document?",
537
+ "Can you summarize the key points?",
538
+ "What are the most important findings?",
539
+ "Explain the methodology used",
540
+ "What conclusions does the author reach?"
541
+ ]
542
+ )
543
+
544
+ # --- Tab: About ---
545
+ with gr.Tab("ℹ️ About"):
546
+ gr.Markdown("""
547
+ ## About AI PDF Summarizer Pro
548
+
549
+ **One PDF Upload, Multiple AI Features**
550
+
551
+ Upload your PDF once in the **PDF Summarizer** tab and use it across all features:
552
+
553
+ - **πŸ“„ PDF Summarizer**: Extract text, generate summaries, get keywords
554
+ - **πŸ–ΌοΈ AI Diagram**: Create visual diagrams from your content
555
+ - **πŸ’¬ Chat with PDF**: Ask questions and get instant answers
556
+
557
+ ### How it works:
558
+ 1. Upload your PDF in the **PDF Summarizer** tab
559
+ 2. Click **Process PDF**
560
+ 3. The same PDF is automatically available in all other tabs
561
+ 4. No need to re-upload - seamless experience!
562
+
563
+ ### Powered by:
564
+ - Hugging Face Transformers
565
+ - Stable Diffusion
566
+ - Groq API
567
+ - FAISS Vector Search
568
+
569
+ ### Setup Instructions:
570
+ For full functionality, add your Groq API key in Hugging Face Spaces secrets:
571
+ - Go to your Space settings
572
+ - Add a secret named `GROQ_API_KEY` with your Groq API key
573
+ """)
574
+
575
+ # --- Event Handlers ---
576
+
577
+ # Main PDF processing - updates all tabs
578
+ process_btn.click(
579
+ process_pdf,
580
+ inputs=[pdf_input],
581
+ outputs=[extracted_text, summary_box, audio_box, keywords_box, diagram_box, status_display]
582
+ ).then(
583
+ # Update the diagram tab inputs with the generated summary and keywords
584
+ lambda summary, keywords: (summary, keywords),
585
+ inputs=[summary_box, keywords_box],
586
+ outputs=[diagram_summary_input, diagram_keywords_input]
587
+ ).then(
588
+ # Update chat status
589
+ lambda: "<div>βœ… PDF processed successfully! You can now chat with your document.</div>",
590
+ outputs=[chat_status_display]
591
+ )
592
+
593
+ # Standalone diagram generation
594
+ generate_diagram_btn.click(
595
+ generate_diagram,
596
+ inputs=[diagram_summary_input, diagram_keywords_input],
597
+ outputs=[diagram_output]
598
+ )
599
+
600
+ return demo
601
+
602
+ # ==========================================================
603
+ # πŸš€ Launch Application
604
+ # ==========================================================
605
+ if __name__ == "__main__":
606
+ print("Starting AI PDF Summarizer Pro...")
607
+ print("Key Feature: Upload PDF once, use across all tabs!")
608
+ print("Loading AI models...")
609
+ print("βœ… Summarization Model")
610
+ print("βœ… Embedding Model")
611
+ print("βœ… Diagram Generation")
612
+ print("βœ… Chat Model")
613
+
614
+ demo = create_interface()
615
+ demo.launch(share=False)