agnixcode commited on
Commit
d46d52c
Β·
verified Β·
1 Parent(s): 10a4f4a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -94
app.py CHANGED
@@ -1,100 +1,104 @@
1
- import os
2
- 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}
@@ -102,22 +106,22 @@ Context:
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
 
@@ -125,46 +129,75 @@ def process_video_ui(url):
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()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import re
 
 
3
  from youtube_transcript_api import YouTubeTranscriptApi
4
  from sentence_transformers import SentenceTransformer
5
  from langchain_text_splitters import RecursiveCharacterTextSplitter
6
+ import numpy as np
7
+ import faiss
8
  from groq import Groq
9
+ import os
10
 
11
  # ===============================
12
+ # MODULE 1: IMPORTS & GLOBALS
13
  # ===============================
14
 
15
+ # Initialize Models
 
 
 
 
16
  embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
17
+ groq_client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
18
 
19
  # Global Storage
20
  vector_store = None
21
  chunks_store = []
22
 
23
  # ===============================
24
+ # MODULE 2: TRANSCRIPT FUNCTION
25
  # ===============================
26
 
27
  def extract_video_id(url):
28
+ """Extract video ID from YouTube URL"""
29
+ patterns = [
30
+ r'(?:youtube\.com\/watch\?v=)([\w-]+)',
31
+ r'(?:youtu\.be\/)([\w-]+)',
32
+ r'(?:youtube\.com\/embed\/)([\w-]+)',
33
+ r'(?:youtube\.com\/v\/)([\w-]+)'
34
+ ]
35
+
36
+ for pattern in patterns:
37
+ match = re.search(pattern, url)
38
+ if match:
39
+ return match.group(1)
40
+ raise ValueError("Invalid YouTube URL")
41
 
42
  def get_transcript(url):
43
+ """Fetch transcript using YouTubeTranscriptApi"""
 
 
44
  try:
45
  video_id = extract_video_id(url)
46
+ transcript_data = YouTubeTranscriptApi.get_transcript(video_id)
47
+ full_text = " ".join([item['text'] for item in transcript_data])
 
 
 
 
 
 
48
  return full_text
49
  except Exception as e:
50
+ return f"ERROR: {str(e)}"
51
+
52
+ # ===============================
53
+ # MODULE 3: VECTOR DATABASE
54
+ # ===============================
55
 
56
  def process_transcript(transcript):
57
+ """Convert transcript β†’ chunks β†’ embeddings β†’ FAISS index"""
58
  global vector_store, chunks_store
59
 
60
+ # Step 1: Split text into chunks
61
+ splitter = RecursiveCharacterTextSplitter(
62
+ chunk_size=500,
63
+ chunk_overlap=50
64
+ )
65
  chunks = splitter.split_text(transcript)
66
 
67
+ # Step 2: Convert chunks β†’ embeddings
68
  embeddings = embedding_model.encode(chunks)
69
 
70
+ # Step 3: Store in FAISS
71
  dimension = embeddings.shape[1]
72
  index = faiss.IndexFlatL2(dimension)
73
+ index.add(np.array(embeddings))
74
 
75
+ # Save globally
76
  vector_store = index
77
  chunks_store = chunks
78
 
79
+ # ===============================
80
+ # MODULE 4: RETRIEVAL
81
+ # ===============================
82
+
83
  def retrieve_context(query, top_k=3):
84
+ """Retrieve most relevant chunks using similarity search"""
 
 
85
  query_embedding = embedding_model.encode([query])
86
+ distances, indices = vector_store.search(np.array(query_embedding), top_k)
87
+ retrieved_chunks = [chunks_store[i] for i in indices[0]]
 
 
88
  return "\n\n".join(retrieved_chunks)
89
 
90
+ # ===============================
91
+ # MODULE 5: LLM (GROQ)
92
+ # ===============================
93
+
94
  def generate_answer(query):
95
+ """Full RAG pipeline: Query β†’ Retrieve β†’ Augment β†’ Generate"""
 
 
96
  context = retrieve_context(query)
97
+
 
 
98
  prompt = f"""
99
+ You are a helpful AI assistant.
100
+
101
+ Use ONLY the context below to answer.
102
 
103
  Context:
104
  {context}
 
106
  Question:
107
  {query}
108
 
109
+ Answer clearly and accurately:
110
  """
111
 
112
  response = groq_client.chat.completions.create(
113
  model="llama-3.3-70b-versatile",
114
  messages=[{"role": "user", "content": prompt}]
115
  )
116
+
117
  return response.choices[0].message.content
118
 
119
  # ===============================
120
+ # MODULE 6: PROCESS PIPELINE
121
  # ===============================
122
 
123
+ def process_video(url):
124
+ """Full pipeline: URL β†’ Transcript β†’ Embeddings β†’ Ready for chat"""
 
125
 
126
  transcript = get_transcript(url)
127
 
 
129
  return transcript, "❌ Failed to fetch transcript"
130
 
131
  process_transcript(transcript)
 
 
 
 
 
132
 
133
+ status = "βœ… Video processed! You can now chat."
134
+
135
+ return transcript, status
136
+
137
+ # ===============================
138
+ # MODULE 7: CHAT FUNCTION
139
+ # ===============================
140
+
141
+ def chat_with_video(user_query, history):
142
+ """Handles chat interaction"""
143
  if vector_store is None:
144
+ return history + [(user_query, "⚠️ Please process a video first.")]
 
145
 
146
  answer = generate_answer(user_query)
147
  history.append((user_query, answer))
148
+ return history
149
+
150
+ # ===============================
151
+ # MODULE 8: GRADIO UI
152
+ # ===============================
153
+
154
+ with gr.Blocks() as app:
155
+
156
+ gr.Markdown("# πŸŽ₯ YouTube RAG Q&A System")
157
+ gr.Markdown("Process a YouTube video and chat with it using AI")
158
+
159
+ # TAB 1: VIDEO PROCESSING
160
+ with gr.Tab("πŸ“₯ Process Video"):
161
+
162
+ url_input = gr.Textbox(
163
+ label="Enter YouTube URL",
164
+ placeholder="https://www.youtube.com/watch?v=..."
165
+ )
166
+
167
+ process_btn = gr.Button("▢️ Transcribe & Process")
168
+
169
+ transcript_output = gr.Textbox(
170
+ label="Transcript",
171
+ lines=10
172
+ )
173
+
174
+ status_output = gr.Textbox(
175
+ label="Status"
176
+ )
177
+
178
+ process_btn.click(
179
+ fn=process_video,
180
+ inputs=url_input,
181
+ outputs=[transcript_output, status_output]
182
+ )
183
+
184
+ # TAB 2: CHAT
185
+ with gr.Tab("πŸ’¬ Chat with Video"):
186
+
187
+ chatbot = gr.Chatbot(height=400)
188
+
189
+ user_input = gr.Textbox(
190
+ label="Ask a question about the video"
191
+ )
192
+
193
+ send_btn = gr.Button("Send")
194
+
195
+ send_btn.click(
196
+ fn=chat_with_video,
197
+ inputs=[user_input, chatbot],
198
+ outputs=chatbot
199
+ )
200
+
201
+ # CHANGE 1: Add server parameters for Hugging Face
202
+ # CHANGE 2: Remove debug=True
203
+ app.launch(server_name="0.0.0.0", server_port=7860)