agnixcode commited on
Commit
670c1c5
·
verified ·
1 Parent(s): 01c13a2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -779
app.py CHANGED
@@ -3,848 +3,132 @@ import re
3
  import gradio as gr
4
  import numpy as np
5
  import faiss
6
- # Import the library
7
  from youtube_transcript_api import YouTubeTranscriptApi
8
  from sentence_transformers import SentenceTransformer
9
  from langchain_text_splitters import RecursiveCharacterTextSplitter
10
  from groq import Groq
11
 
12
  # ===============================
13
- # CONFIG & INITIALIZATION
14
  # ===============================
15
 
16
- # Get API Key from Environment Variables (Set this in HF Space Secrets)
17
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
18
  groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
19
 
20
- # Load embedding model
21
  embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
22
 
23
- # Global Storage
24
  vector_store = None
25
  chunks_store = []
26
 
27
  # ===============================
28
- # HELPER FUNCTIONS
29
  # ===============================
30
 
31
  def extract_video_id(url):
32
- """Extracts the 11-character YouTube video ID from various URL formats."""
33
  regex = r"(?:v=|\/|be\/)([0-9A-Za-z_-]{11}).*"
34
  match = re.search(regex, url)
35
- if match:
36
- return match.group(1)
37
- return None
38
 
39
  def get_transcript(url):
40
- """
41
- Fetch transcript using the correct static method.
42
- """
43
- try:
44
- video_id = extract_video_id(url)
45
- if not video_id:
46
- return "ERROR: Invalid YouTube URL. Could not find Video ID."
47
-
48
- # FIX: Calling the static method directly on the class
49
- # We also try to fetch English by default or the first available
50
- transcript_list = YouTubeTranscriptApi.get_transcript(video_id)
51
-
52
- full_text = " ".join([item['text'] for item in transcript_list])
53
- return full_text
54
- except Exception as e:
55
- return f"ERROR: Could not retrieve transcript. (Details: {str(e)})"
56
-
57
- def process_transcript(transcript):
58
- global vector_store, chunks_store
59
-
60
- # Split text into manageable chunks
61
- splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=60)
62
- chunks = splitter.split_text(transcript)
63
-
64
- # Create embeddings
65
- embeddings = embedding_model.encode(chunks)
66
-
67
- # Initialize FAISS Index
68
- dimension = embeddings.shape[1]
69
- index = faiss.IndexFlatL2(dimension)
70
- index.add(np.array(embeddings).astype('float32'))
71
-
72
- # Store globally for retrieval
73
- vector_store = index
74
- chunks_store = chunks
75
-
76
- def retrieve_context(query, top_k=3):
77
- if vector_store is None:
78
- return ""
79
-
80
- query_embedding = embedding_model.encode([query])
81
- distances, indices = vector_store.search(np.array(query_embedding).astype('float32'), top_k)
82
-
83
- # Fetch matching chunks
84
- retrieved_chunks = [chunks_store[i] for i in indices[0] if i != -1]
85
- return "\n\n".join(retrieved_chunks)
86
-
87
- def generate_answer(query):
88
- if not groq_client:
89
- return "Error: Groq API Key is not set in Hugging Face Secrets."
90
-
91
- context = retrieve_context(query)
92
- if not context:
93
- return "I don't have any context from the video yet. Please process a video first."
94
-
95
- prompt = f"""
96
- You are a professional AI Assistant. Use the provided context from a YouTube video to answer the user's question.
97
- If the answer isn't in the context, say you don't know based on the video.
98
-
99
- Context:
100
- {context}
101
-
102
- Question:
103
- {query}
104
-
105
- Answer:
106
- """
107
-
108
- response = groq_client.chat.completions.create(
109
- model="llama-3.3-70b-versatile",
110
- messages=[{"role": "user", "content": prompt}]
111
- )
112
- return response.choices[0].message.content
113
-
114
- # ===============================
115
- # UI LOGIC
116
- # ===============================
117
-
118
- def process_video_ui(url):
119
- if not url:
120
- return "Please enter a valid URL", "❌ No URL"
121
-
122
- transcript = get_transcript(url)
123
-
124
- if transcript.startswith("ERROR"):
125
- return transcript, "❌ Failed to fetch transcript"
126
-
127
- process_transcript(transcript)
128
- return transcript[:1500] + "...", "✅ Video processed! You can now chat."
129
-
130
- def chat_with_video_ui(user_query, history):
131
- if not user_query:
132
- return history, ""
133
-
134
- if vector_store is None:
135
- history.append((user_query, "⚠️ Please process a video in the first tab before chatting."))
136
- return history, ""
137
-
138
- answer = generate_answer(user_query)
139
- history.append((user_query, answer))
140
- return history, ""
141
-
142
- # ===============================
143
- # GRADIO INTERFACE
144
- # ===============================
145
-
146
- with gr.Blocks(theme=gr.themes.Soft()) as app:
147
- gr.Markdown("# 🎥 YouTube RAG AI Expert")
148
- gr.Markdown("Transcribe any YouTube video and chat with its content using Llama 3.3 & FAISS.")
149
 
150
- with gr.Tabs():
151
- with gr.Tab("1. Load Video"):
152
- url_input = gr.Textbox(label="YouTube Link", placeholder="https://www.youtube.com/watch?v=...")
153
- process_btn = gr.Button("Transcribe & Index Video", variant="primary")
154
- with gr.Row():
155
- status_output = gr.Textbox(label="Status")
156
- transcript_preview = gr.Textbox(label="Transcript Preview", lines=8)
157
-
158
- process_btn.click(process_video_ui, inputs=url_input, outputs=[transcript_preview, status_output])
159
-
160
- with gr.Tab("2. Chat with AI"):
161
- chatbot = gr.Chatbot(height=500)
162
- with gr.Row():
163
- msg = gr.Textbox(label="Your Question", placeholder="What are the key takeaways?", scale=4)
164
- submit = gr.Button("Ask", variant="primary", scale=1)
165
-
166
- submit.click(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
167
- msg.submit(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
168
-
169
- if __name__ == "__main__":
170
- app.launch()
171
- import os
172
- import re
173
- import gradio as gr
174
- import numpy as np
175
- import faiss
176
- # Import the library
177
- from youtube_transcript_api import YouTubeTranscriptApi
178
- from sentence_transformers import SentenceTransformer
179
- from langchain_text_splitters import RecursiveCharacterTextSplitter
180
- from groq import Groq
181
-
182
- # ===============================
183
- # CONFIG & INITIALIZATION
184
- # ===============================
185
-
186
- # Get API Key from Environment Variables (Set this in HF Space Secrets)
187
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
188
- groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
189
-
190
- # Load embedding model
191
- embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
192
-
193
- # Global Storage
194
- vector_store = None
195
- chunks_store = []
196
-
197
- # ===============================
198
- # HELPER FUNCTIONS
199
- # ===============================
200
-
201
- def extract_video_id(url):
202
- """Extracts the 11-character YouTube video ID from various URL formats."""
203
- regex = r"(?:v=|\/|be\/)([0-9A-Za-z_-]{11}).*"
204
- match = re.search(regex, url)
205
- if match:
206
- return match.group(1)
207
- return None
208
-
209
- def get_transcript(url):
210
- """
211
- Fetch transcript using the correct static method.
212
- """
213
  try:
214
- video_id = extract_video_id(url)
215
- if not video_id:
216
- return "ERROR: Invalid YouTube URL. Could not find Video ID."
217
-
218
- # FIX: Calling the static method directly on the class
219
- # We also try to fetch English by default or the first available
220
- transcript_list = YouTubeTranscriptApi.get_transcript(video_id)
221
-
222
- full_text = " ".join([item['text'] for item in transcript_list])
223
- return full_text
224
  except Exception as e:
225
- return f"ERROR: Could not retrieve transcript. (Details: {str(e)})"
226
 
227
- def process_transcript(transcript):
 
228
  global vector_store, chunks_store
229
 
230
- # Split text into manageable chunks
231
  splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=60)
232
- chunks = splitter.split_text(transcript)
233
 
234
- # Create embeddings
235
- embeddings = embedding_model.encode(chunks)
236
 
237
- # Initialize FAISS Index
238
  dimension = embeddings.shape[1]
239
  index = faiss.IndexFlatL2(dimension)
240
  index.add(np.array(embeddings).astype('float32'))
241
-
242
- # Store globally for retrieval
243
  vector_store = index
244
- chunks_store = chunks
245
-
246
- def retrieve_context(query, top_k=3):
247
- if vector_store is None:
248
- return ""
249
-
250
- query_embedding = embedding_model.encode([query])
251
- distances, indices = vector_store.search(np.array(query_embedding).astype('float32'), top_k)
252
-
253
- # Fetch matching chunks
254
- retrieved_chunks = [chunks_store[i] for i in indices[0] if i != -1]
255
- return "\n\n".join(retrieved_chunks)
256
-
257
- def generate_answer(query):
258
- if not groq_client:
259
- return "Error: Groq API Key is not set in Hugging Face Secrets."
260
-
261
- context = retrieve_context(query)
262
- if not context:
263
- return "I don't have any context from the video yet. Please process a video first."
264
-
265
- prompt = f"""
266
- You are a professional AI Assistant. Use the provided context from a YouTube video to answer the user's question.
267
- If the answer isn't in the context, say you don't know based on the video.
268
-
269
- Context:
270
- {context}
271
-
272
- Question:
273
- {query}
274
-
275
- Answer:
276
- """
277
-
278
- response = groq_client.chat.completions.create(
279
- model="llama-3.3-70b-versatile",
280
- messages=[{"role": "user", "content": prompt}]
281
- )
282
- return response.choices[0].message.content
283
-
284
- # ===============================
285
- # UI LOGIC
286
- # ===============================
287
-
288
- def process_video_ui(url):
289
- if not url:
290
- return "Please enter a valid URL", "❌ No URL"
291
-
292
- transcript = get_transcript(url)
293
-
294
- if transcript.startswith("ERROR"):
295
- return transcript, "❌ Failed to fetch transcript"
296
-
297
- process_transcript(transcript)
298
- return transcript[:1500] + "...", "✅ Video processed! You can now chat."
299
-
300
- def chat_with_video_ui(user_query, history):
301
- if not user_query:
302
- return history, ""
303
-
304
- if vector_store is None:
305
- history.append((user_query, "⚠️ Please process a video in the first tab before chatting."))
306
- return history, ""
307
-
308
- answer = generate_answer(user_query)
309
- history.append((user_query, answer))
310
- return history, ""
311
-
312
- # ===============================
313
- # GRADIO INTERFACE
314
- # ===============================
315
 
316
- with gr.Blocks(theme=gr.themes.Soft()) as app:
317
- gr.Markdown("# 🎥 YouTube RAG AI Expert")
318
- gr.Markdown("Transcribe any YouTube video and chat with its content using Llama 3.3 & FAISS.")
 
319
 
320
- with gr.Tabs():
321
- with gr.Tab("1. Load Video"):
322
- url_input = gr.Textbox(label="YouTube Link", placeholder="https://www.youtube.com/watch?v=...")
323
- process_btn = gr.Button("Transcribe & Index Video", variant="primary")
324
- with gr.Row():
325
- status_output = gr.Textbox(label="Status")
326
- transcript_preview = gr.Textbox(label="Transcript Preview", lines=8)
327
-
328
- process_btn.click(process_video_ui, inputs=url_input, outputs=[transcript_preview, status_output])
329
-
330
- with gr.Tab("2. Chat with AI"):
331
- chatbot = gr.Chatbot(height=500)
332
- with gr.Row():
333
- msg = gr.Textbox(label="Your Question", placeholder="What are the key takeaways?", scale=4)
334
- submit = gr.Button("Ask", variant="primary", scale=1)
335
-
336
- submit.click(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
337
- msg.submit(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
338
-
339
- if __name__ == "__main__":
340
- app.launch()
341
- import os
342
- import re
343
- import gradio as gr
344
- import numpy as np
345
- import faiss
346
- # Import the library
347
- from youtube_transcript_api import YouTubeTranscriptApi
348
- from sentence_transformers import SentenceTransformer
349
- from langchain_text_splitters import RecursiveCharacterTextSplitter
350
- from groq import Groq
351
-
352
- # ===============================
353
- # CONFIG & INITIALIZATION
354
- # ===============================
355
-
356
- # Get API Key from Environment Variables (Set this in HF Space Secrets)
357
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
358
- groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
359
-
360
- # Load embedding model
361
- embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
362
-
363
- # Global Storage
364
- vector_store = None
365
- chunks_store = []
366
-
367
- # ===============================
368
- # HELPER FUNCTIONS
369
- # ===============================
370
 
371
- def extract_video_id(url):
372
- """Extracts the 11-character YouTube video ID from various URL formats."""
373
- regex = r"(?:v=|\/|be\/)([0-9A-Za-z_-]{11}).*"
374
- match = re.search(regex, url)
375
- if match:
376
- return match.group(1)
377
- return None
378
 
379
- def get_transcript(url):
380
- """
381
- Fetch transcript using the correct static method.
382
- """
383
  try:
384
- video_id = extract_video_id(url)
385
- if not video_id:
386
- return "ERROR: Invalid YouTube URL. Could not find Video ID."
387
-
388
- # FIX: Calling the static method directly on the class
389
- # We also try to fetch English by default or the first available
390
- transcript_list = YouTubeTranscriptApi.get_transcript(video_id)
391
-
392
- full_text = " ".join([item['text'] for item in transcript_list])
393
- return full_text
394
  except Exception as e:
395
- return f"ERROR: Could not retrieve transcript. (Details: {str(e)})"
396
-
397
- def process_transcript(transcript):
398
- global vector_store, chunks_store
399
-
400
- # Split text into manageable chunks
401
- splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=60)
402
- chunks = splitter.split_text(transcript)
403
-
404
- # Create embeddings
405
- embeddings = embedding_model.encode(chunks)
406
-
407
- # Initialize FAISS Index
408
- dimension = embeddings.shape[1]
409
- index = faiss.IndexFlatL2(dimension)
410
- index.add(np.array(embeddings).astype('float32'))
411
-
412
- # Store globally for retrieval
413
- vector_store = index
414
- chunks_store = chunks
415
-
416
- def retrieve_context(query, top_k=3):
417
- if vector_store is None:
418
- return ""
419
-
420
- query_embedding = embedding_model.encode([query])
421
- distances, indices = vector_store.search(np.array(query_embedding).astype('float32'), top_k)
422
-
423
- # Fetch matching chunks
424
- retrieved_chunks = [chunks_store[i] for i in indices[0] if i != -1]
425
- return "\n\n".join(retrieved_chunks)
426
-
427
- def generate_answer(query):
428
- if not groq_client:
429
- return "Error: Groq API Key is not set in Hugging Face Secrets."
430
-
431
- context = retrieve_context(query)
432
- if not context:
433
- return "I don't have any context from the video yet. Please process a video first."
434
-
435
- prompt = f"""
436
- You are a professional AI Assistant. Use the provided context from a YouTube video to answer the user's question.
437
- If the answer isn't in the context, say you don't know based on the video.
438
-
439
- Context:
440
- {context}
441
-
442
- Question:
443
- {query}
444
-
445
- Answer:
446
- """
447
-
448
- response = groq_client.chat.completions.create(
449
- model="llama-3.3-70b-versatile",
450
- messages=[{"role": "user", "content": prompt}]
451
- )
452
- return response.choices[0].message.content
453
 
454
  # ===============================
455
  # UI LOGIC
456
  # ===============================
457
 
458
- def process_video_ui(url):
459
- if not url:
460
- return "Please enter a valid URL", "❌ No URL"
461
-
462
- transcript = get_transcript(url)
463
-
464
- if transcript.startswith("ERROR"):
465
- return transcript, "❌ Failed to fetch transcript"
466
-
467
- process_transcript(transcript)
468
- return transcript[:1500] + "...", "✅ Video processed! You can now chat."
469
-
470
- def chat_with_video_ui(user_query, history):
471
- if not user_query:
472
- return history, ""
473
-
474
- if vector_store is None:
475
- history.append((user_query, "⚠️ Please process a video in the first tab before chatting."))
476
- return history, ""
477
-
478
- answer = generate_answer(user_query)
479
- history.append((user_query, answer))
480
- return history, ""
481
-
482
- # ===============================
483
- # GRADIO INTERFACE
484
- # ===============================
485
-
486
- with gr.Blocks(theme=gr.themes.Soft()) as app:
487
- gr.Markdown("# 🎥 YouTube RAG AI Expert")
488
- gr.Markdown("Transcribe any YouTube video and chat with its content using Llama 3.3 & FAISS.")
489
-
490
- with gr.Tabs():
491
- with gr.Tab("1. Load Video"):
492
- url_input = gr.Textbox(label="YouTube Link", placeholder="https://www.youtube.com/watch?v=...")
493
- process_btn = gr.Button("Transcribe & Index Video", variant="primary")
494
- with gr.Row():
495
- status_output = gr.Textbox(label="Status")
496
- transcript_preview = gr.Textbox(label="Transcript Preview", lines=8)
497
-
498
- process_btn.click(process_video_ui, inputs=url_input, outputs=[transcript_preview, status_output])
499
-
500
- with gr.Tab("2. Chat with AI"):
501
- chatbot = gr.Chatbot(height=500)
502
- with gr.Row():
503
- msg = gr.Textbox(label="Your Question", placeholder="What are the key takeaways?", scale=4)
504
- submit = gr.Button("Ask", variant="primary", scale=1)
505
-
506
- submit.click(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
507
- msg.submit(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
508
-
509
- if __name__ == "__main__":
510
- app.launch()
511
- import os
512
- import re
513
- import gradio as gr
514
- import numpy as np
515
- import faiss
516
- # Import the library
517
- from youtube_transcript_api import YouTubeTranscriptApi
518
- from sentence_transformers import SentenceTransformer
519
- from langchain_text_splitters import RecursiveCharacterTextSplitter
520
- from groq import Groq
521
-
522
- # ===============================
523
- # CONFIG & INITIALIZATION
524
- # ===============================
525
-
526
- # Get API Key from Environment Variables (Set this in HF Space Secrets)
527
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
528
- groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
529
-
530
- # Load embedding model
531
- embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
532
-
533
- # Global Storage
534
- vector_store = None
535
- chunks_store = []
536
-
537
- # ===============================
538
- # HELPER FUNCTIONS
539
- # ===============================
540
-
541
- def extract_video_id(url):
542
- """Extracts the 11-character YouTube video ID from various URL formats."""
543
- regex = r"(?:v=|\/|be\/)([0-9A-Za-z_-]{11}).*"
544
- match = re.search(regex, url)
545
- if match:
546
- return match.group(1)
547
- return None
548
-
549
- def get_transcript(url):
550
- """
551
- Fetch transcript using the correct static method.
552
- """
553
- try:
554
- video_id = extract_video_id(url)
555
- if not video_id:
556
- return "ERROR: Invalid YouTube URL. Could not find Video ID."
557
-
558
- # FIX: Calling the static method directly on the class
559
- # We also try to fetch English by default or the first available
560
- transcript_list = YouTubeTranscriptApi.get_transcript(video_id)
561
-
562
- full_text = " ".join([item['text'] for item in transcript_list])
563
- return full_text
564
- except Exception as e:
565
- return f"ERROR: Could not retrieve transcript. (Details: {str(e)})"
566
-
567
- def process_transcript(transcript):
568
- global vector_store, chunks_store
569
-
570
- # Split text into manageable chunks
571
- splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=60)
572
- chunks = splitter.split_text(transcript)
573
-
574
- # Create embeddings
575
- embeddings = embedding_model.encode(chunks)
576
-
577
- # Initialize FAISS Index
578
- dimension = embeddings.shape[1]
579
- index = faiss.IndexFlatL2(dimension)
580
- index.add(np.array(embeddings).astype('float32'))
581
-
582
- # Store globally for retrieval
583
- vector_store = index
584
- chunks_store = chunks
585
-
586
- def retrieve_context(query, top_k=3):
587
- if vector_store is None:
588
- return ""
589
-
590
- query_embedding = embedding_model.encode([query])
591
- distances, indices = vector_store.search(np.array(query_embedding).astype('float32'), top_k)
592
-
593
- # Fetch matching chunks
594
- retrieved_chunks = [chunks_store[i] for i in indices[0] if i != -1]
595
- return "\n\n".join(retrieved_chunks)
596
-
597
- def generate_answer(query):
598
- if not groq_client:
599
- return "Error: Groq API Key is not set in Hugging Face Secrets."
600
-
601
- context = retrieve_context(query)
602
- if not context:
603
- return "I don't have any context from the video yet. Please process a video first."
604
-
605
- prompt = f"""
606
- You are a professional AI Assistant. Use the provided context from a YouTube video to answer the user's question.
607
- If the answer isn't in the context, say you don't know based on the video.
608
-
609
- Context:
610
- {context}
611
-
612
- Question:
613
- {query}
614
-
615
- Answer:
616
- """
617
-
618
- response = groq_client.chat.completions.create(
619
- model="llama-3.3-70b-versatile",
620
- messages=[{"role": "user", "content": prompt}]
621
- )
622
- return response.choices[0].message.content
623
-
624
- # ===============================
625
- # UI LOGIC
626
- # ===============================
627
-
628
- def process_video_ui(url):
629
- if not url:
630
- return "Please enter a valid URL", "❌ No URL"
631
-
632
  transcript = get_transcript(url)
633
-
634
  if transcript.startswith("ERROR"):
635
- return transcript, "❌ Failed to fetch transcript"
636
 
637
- process_transcript(transcript)
638
- return transcript[:1500] + "...", "✅ Video processed! You can now chat."
639
 
640
- def chat_with_video_ui(user_query, history):
641
- if not user_query:
642
- return history, ""
643
-
644
- if vector_store is None:
645
- history.append((user_query, "⚠️ Please process a video in the first tab before chatting."))
646
- return history, ""
647
-
648
- answer = generate_answer(user_query)
649
- history.append((user_query, answer))
650
- return history, ""
651
-
652
- # ===============================
653
- # GRADIO INTERFACE
654
- # ===============================
655
-
656
- with gr.Blocks(theme=gr.themes.Soft()) as app:
657
- gr.Markdown("# 🎥 YouTube RAG AI Expert")
658
- gr.Markdown("Transcribe any YouTube video and chat with its content using Llama 3.3 & FAISS.")
659
-
660
- with gr.Tabs():
661
- with gr.Tab("1. Load Video"):
662
- url_input = gr.Textbox(label="YouTube Link", placeholder="https://www.youtube.com/watch?v=...")
663
- process_btn = gr.Button("Transcribe & Index Video", variant="primary")
664
- with gr.Row():
665
- status_output = gr.Textbox(label="Status")
666
- transcript_preview = gr.Textbox(label="Transcript Preview", lines=8)
667
-
668
- process_btn.click(process_video_ui, inputs=url_input, outputs=[transcript_preview, status_output])
669
-
670
- with gr.Tab("2. Chat with AI"):
671
- chatbot = gr.Chatbot(height=500)
672
- with gr.Row():
673
- msg = gr.Textbox(label="Your Question", placeholder="What are the key takeaways?", scale=4)
674
- submit = gr.Button("Ask", variant="primary", scale=1)
675
-
676
- submit.click(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
677
- msg.submit(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
678
-
679
- if __name__ == "__main__":
680
- app.launch()
681
- import os
682
- import re
683
- import gradio as gr
684
- import numpy as np
685
- import faiss
686
- # Import the library
687
- from youtube_transcript_api import YouTubeTranscriptApi
688
- from sentence_transformers import SentenceTransformer
689
- from langchain_text_splitters import RecursiveCharacterTextSplitter
690
- from groq import Groq
691
-
692
- # ===============================
693
- # CONFIG & INITIALIZATION
694
- # ===============================
695
-
696
- # Get API Key from Environment Variables (Set this in HF Space Secrets)
697
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
698
- groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
699
-
700
- # Load embedding model
701
- embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
702
-
703
- # Global Storage
704
- vector_store = None
705
- chunks_store = []
706
-
707
- # ===============================
708
- # HELPER FUNCTIONS
709
- # ===============================
710
-
711
- def extract_video_id(url):
712
- """Extracts the 11-character YouTube video ID from various URL formats."""
713
- regex = r"(?:v=|\/|be\/)([0-9A-Za-z_-]{11}).*"
714
- match = re.search(regex, url)
715
- if match:
716
- return match.group(1)
717
- return None
718
-
719
- def get_transcript(url):
720
- """
721
- Fetch transcript using the correct static method.
722
- """
723
- try:
724
- video_id = extract_video_id(url)
725
- if not video_id:
726
- return "ERROR: Invalid YouTube URL. Could not find Video ID."
727
-
728
- # FIX: Calling the static method directly on the class
729
- # We also try to fetch English by default or the first available
730
- transcript_list = YouTubeTranscriptApi.get_transcript(video_id)
731
-
732
- full_text = " ".join([item['text'] for item in transcript_list])
733
- return full_text
734
- except Exception as e:
735
- return f"ERROR: Could not retrieve transcript. (Details: {str(e)})"
736
-
737
- def process_transcript(transcript):
738
- global vector_store, chunks_store
739
-
740
- # Split text into manageable chunks
741
- splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=60)
742
- chunks = splitter.split_text(transcript)
743
-
744
- # Create embeddings
745
- embeddings = embedding_model.encode(chunks)
746
-
747
- # Initialize FAISS Index
748
- dimension = embeddings.shape[1]
749
- index = faiss.IndexFlatL2(dimension)
750
- index.add(np.array(embeddings).astype('float32'))
751
-
752
- # Store globally for retrieval
753
- vector_store = index
754
- chunks_store = chunks
755
-
756
- def retrieve_context(query, top_k=3):
757
- if vector_store is None:
758
- return ""
759
-
760
- query_embedding = embedding_model.encode([query])
761
- distances, indices = vector_store.search(np.array(query_embedding).astype('float32'), top_k)
762
-
763
- # Fetch matching chunks
764
- retrieved_chunks = [chunks_store[i] for i in indices[0] if i != -1]
765
- return "\n\n".join(retrieved_chunks)
766
-
767
- def generate_answer(query):
768
- if not groq_client:
769
- return "Error: Groq API Key is not set in Hugging Face Secrets."
770
-
771
- context = retrieve_context(query)
772
- if not context:
773
- return "I don't have any context from the video yet. Please process a video first."
774
-
775
- prompt = f"""
776
- You are a professional AI Assistant. Use the provided context from a YouTube video to answer the user's question.
777
- If the answer isn't in the context, say you don't know based on the video.
778
-
779
- Context:
780
- {context}
781
-
782
- Question:
783
- {query}
784
-
785
- Answer:
786
- """
787
-
788
- response = groq_client.chat.completions.create(
789
- model="llama-3.3-70b-versatile",
790
- messages=[{"role": "user", "content": prompt}]
791
- )
792
- return response.choices[0].message.content
793
-
794
- # ===============================
795
- # UI LOGIC
796
- # ===============================
797
-
798
- def process_video_ui(url):
799
- if not url:
800
- return "Please enter a valid URL", "❌ No URL"
801
-
802
- transcript = get_transcript(url)
803
-
804
- if transcript.startswith("ERROR"):
805
- return transcript, "❌ Failed to fetch transcript"
806
-
807
- process_transcript(transcript)
808
- return transcript[:1500] + "...", "✅ Video processed! You can now chat."
809
-
810
- def chat_with_video_ui(user_query, history):
811
- if not user_query:
812
- return history, ""
813
-
814
- if vector_store is None:
815
- history.append((user_query, "⚠️ Please process a video in the first tab before chatting."))
816
- return history, ""
817
 
818
- answer = generate_answer(user_query)
819
- history.append((user_query, answer))
820
  return history, ""
821
 
822
  # ===============================
823
  # GRADIO INTERFACE
824
  # ===============================
825
 
826
- with gr.Blocks(theme=gr.themes.Soft()) as app:
827
- gr.Markdown("# 🎥 YouTube RAG AI Expert")
828
- gr.Markdown("Transcribe any YouTube video and chat with its content using Llama 3.3 & FAISS.")
829
 
830
  with gr.Tabs():
831
- with gr.Tab("1. Load Video"):
832
- url_input = gr.Textbox(label="YouTube Link", placeholder="https://www.youtube.com/watch?v=...")
833
- process_btn = gr.Button("Transcribe & Index Video", variant="primary")
834
- with gr.Row():
835
- status_output = gr.Textbox(label="Status")
836
- transcript_preview = gr.Textbox(label="Transcript Preview", lines=8)
837
 
838
- process_btn.click(process_video_ui, inputs=url_input, outputs=[transcript_preview, status_output])
839
 
840
- with gr.Tab("2. Chat with AI"):
841
- chatbot = gr.Chatbot(height=500)
842
- with gr.Row():
843
- msg = gr.Textbox(label="Your Question", placeholder="What are the key takeaways?", scale=4)
844
- submit = gr.Button("Ask", variant="primary", scale=1)
845
 
846
- submit.click(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
847
- msg.submit(chat_with_video_ui, inputs=[msg, chatbot], outputs=[chatbot, msg])
848
 
849
  if __name__ == "__main__":
850
- app.launch()
 
3
  import gradio as gr
4
  import numpy as np
5
  import faiss
 
6
  from youtube_transcript_api import YouTubeTranscriptApi
7
  from sentence_transformers import SentenceTransformer
8
  from langchain_text_splitters import RecursiveCharacterTextSplitter
9
  from groq import Groq
10
 
11
  # ===============================
12
+ # CONFIGURATION
13
  # ===============================
14
 
15
+ # Load Groq API Key from environment variables (Hugging Face Secrets)
16
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
17
  groq_client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
18
 
19
+ # Load embedding model (runs on CPU in HF Spaces)
20
  embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
21
 
22
+ # Global variables to store the "brain" of the current video
23
  vector_store = None
24
  chunks_store = []
25
 
26
  # ===============================
27
+ # CORE FUNCTIONS
28
  # ===============================
29
 
30
  def extract_video_id(url):
31
+ """Extracts the 11-character YouTube video ID."""
32
  regex = r"(?:v=|\/|be\/)([0-9A-Za-z_-]{11}).*"
33
  match = re.search(regex, url)
34
+ return match.group(1) if match else None
 
 
35
 
36
  def get_transcript(url):
37
+ """Fetches transcript and handles potential library errors."""
38
+ video_id = extract_video_id(url)
39
+ if not video_id:
40
+ return "ERROR: Invalid YouTube URL."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  try:
43
+ # Correct static method call on the YouTubeTranscriptApi class
44
+ transcript_data = YouTubeTranscriptApi.get_transcript(video_id)
45
+ return " ".join([item['text'] for item in transcript_data])
 
 
 
 
 
 
 
46
  except Exception as e:
47
+ return f"ERROR: {str(e)}"
48
 
49
+ def build_vector_index(text):
50
+ """Chunks text and stores it in a FAISS vector database."""
51
  global vector_store, chunks_store
52
 
53
+ # 1. Chunking
54
  splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=60)
55
+ chunks_store = splitter.split_text(text)
56
 
57
+ # 2. Embedding
58
+ embeddings = embedding_model.encode(chunks_store)
59
 
60
+ # 3. Indexing with FAISS
61
  dimension = embeddings.shape[1]
62
  index = faiss.IndexFlatL2(dimension)
63
  index.add(np.array(embeddings).astype('float32'))
 
 
64
  vector_store = index
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ def get_ai_response(user_query):
67
+ """Retrieves context and asks Groq Llama 3."""
68
+ if vector_store is None or not chunks_store:
69
+ return "Please load a video first."
70
 
71
+ # Search for relevant chunks
72
+ query_embedding = embedding_model.encode([user_query])
73
+ D, I = vector_store.search(np.array(query_embedding).astype('float32'), k=3)
74
+ context = "\n".join([chunks_store[i] for i in I[0] if i != -1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
+ prompt = f"""Use the following video transcript context to answer the question.
77
+ Context: {context}
78
+ Question: {user_query}
79
+ Answer:"""
 
 
 
80
 
 
 
 
 
81
  try:
82
+ completion = groq_client.chat.completions.create(
83
+ model="llama-3.3-70b-versatile",
84
+ messages=[{"role": "user", "content": prompt}]
85
+ )
86
+ return completion.choices[0].message.content
 
 
 
 
 
87
  except Exception as e:
88
+ return f"AI Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
  # ===============================
91
  # UI LOGIC
92
  # ===============================
93
 
94
+ def process_video_step(url):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  transcript = get_transcript(url)
 
96
  if transcript.startswith("ERROR"):
97
+ return transcript, "❌ Failed"
98
 
99
+ build_vector_index(transcript)
100
+ return transcript[:1000] + "...", "✅ Video Indexed! Go to Chat tab."
101
 
102
+ def chat_step(message, history):
103
+ if not GROQ_API_KEY:
104
+ return history + [("Error", "Groq API Key missing in Secrets.")], ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
+ answer = get_ai_response(message)
107
+ history.append((message, answer))
108
  return history, ""
109
 
110
  # ===============================
111
  # GRADIO INTERFACE
112
  # ===============================
113
 
114
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
115
+ gr.Markdown("# 📺 YouTube AI Expert (RAG)")
 
116
 
117
  with gr.Tabs():
118
+ with gr.Tab("1. Setup Video"):
119
+ url_input = gr.Textbox(label="YouTube URL", placeholder="https://www.youtube.com/watch?v=...")
120
+ process_btn = gr.Button("Process Video", variant="primary")
121
+ status = gr.Textbox(label="Status")
122
+ preview = gr.Textbox(label="Transcript Preview (First 1000 chars)", lines=5)
 
123
 
124
+ process_btn.click(process_video_step, inputs=url_input, outputs=[preview, status])
125
 
126
+ with gr.Tab("2. Chat with Video"):
127
+ chatbot = gr.Chatbot(height=400)
128
+ msg = gr.Textbox(label="Ask anything about the video...")
129
+ clear = gr.ClearButton([msg, chatbot])
 
130
 
131
+ msg.submit(chat_step, [msg, chatbot], [chatbot, msg])
 
132
 
133
  if __name__ == "__main__":
134
+ demo.launch()