NeelTA commited on
Commit
69553a1
Β·
1 Parent(s): ebf35ef

initial commit

Browse files
Files changed (3) hide show
  1. app.py +224 -0
  2. requirements.txt +9 -0
  3. test.ipynb +0 -0
app.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ load_dotenv()
3
+
4
+ import os
5
+
6
+ if not os.environ.get("GOOGLE_API_KEY"):
7
+ raise RuntimeError("Please set the GOOGLE_API_KEY environment variable with your Google API key.")
8
+
9
+ import gradio as gr
10
+ from youtube_transcript_api import YouTubeTranscriptApi
11
+ from langchain_core.prompts import PromptTemplate
12
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
13
+ from langchain_community.vectorstores import FAISS
14
+ from langchain_huggingface import HuggingFaceEmbeddings, HuggingFaceEndpoint, ChatHuggingFace
15
+ from langchain_google_genai import ChatGoogleGenerativeAI
16
+ from langchain_core.messages import HumanMessage
17
+ from langchain_google_genai import GoogleGenerativeAIEmbeddings
18
+
19
+
20
+
21
+ # Initialize the text splitter
22
+ text_splitter = RecursiveCharacterTextSplitter(
23
+ chunk_size=300,
24
+ chunk_overlap=30,
25
+ separators=["\n\n", "\n", " ", ".", ","],
26
+ length_function=len,
27
+ is_separator_regex=False
28
+ )
29
+
30
+ # Initialize the embeddings model
31
+ embeddings = HuggingFaceEmbeddings(
32
+ model_name="sentence-transformers/all-mpnet-base-v2"
33
+ )
34
+
35
+ # Initialize the LLM
36
+ # llm = HuggingFaceEndpoint(
37
+ # repo_id="microsoft/Phi-3-mini-4k-instruct",
38
+ # task="text-generation",
39
+ # max_new_tokens=512,
40
+ # do_sample=False,
41
+ # repetition_penalty=1.03,
42
+ # huggingfacehub_api_token=os.getenv("HUGGINGFACE_TOKEN")
43
+ # )
44
+
45
+ chat = ChatGoogleGenerativeAI(
46
+ model="gemini-2.0-flash",
47
+ temperature=0.3,
48
+ max_tokens=5000,
49
+ timeout=None,
50
+ max_retries=2
51
+ )
52
+
53
+ # Define the prompt template
54
+ prompt = PromptTemplate(
55
+ template="""
56
+ You are a helpful assistant.
57
+ Answer ONLY from the provided transcript context.
58
+ If the context is insufficient, just say you don't know.
59
+
60
+ {context}
61
+ Question: {question}
62
+ """,
63
+ input_variables=['context', 'question']
64
+ )
65
+
66
+ # Global variable to store the current retriever
67
+ current_retriever = None
68
+ current_video_id = None
69
+
70
+ def extract_video_id(url):
71
+ """Extract video ID from YouTube URL."""
72
+ if "youtube.com/watch?v=" in url:
73
+ return url.split("watch?v=")[1].split("&")[0]
74
+ elif "youtu.be/" in url:
75
+ return url.split("youtu.be/")[1].split("?")[0]
76
+ return url # Assume it's already a video ID
77
+
78
+ def process_video_url(video_url_or_id):
79
+ """Process video URL and create retriever object."""
80
+ global current_retriever, current_video_id
81
+
82
+ try:
83
+ # Extract video ID if URL is provided
84
+ video_id = extract_video_id(video_url_or_id)
85
+
86
+ # Check if we already have a retriever for this video
87
+ if current_video_id == video_id and current_retriever is not None:
88
+ return f"βœ… Video already processed: {video_id}"
89
+
90
+ # Get transcript
91
+ transcript = YouTubeTranscriptApi.get_transcript(video_id)
92
+
93
+ # Extract text segments
94
+ list_of_text_segments = [item['text'] for item in transcript]
95
+ full_transcript_text = " ".join(list_of_text_segments)
96
+
97
+ # Create chunks
98
+ chunks = text_splitter.create_documents([full_transcript_text])
99
+
100
+ # Create vector store
101
+ vector_store = FAISS.from_documents(chunks, embeddings)
102
+ current_retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 8})
103
+ print(f"βœ… Current Retreiver : {current_retriever}")
104
+ current_video_id = video_id
105
+
106
+ return f"βœ… Video processed successfully: {video_id}"
107
+
108
+ except Exception as e:
109
+ return f"❌ Error processing video: {str(e)}"
110
+
111
+ def answer_question(question):
112
+ global current_retriever
113
+
114
+ if current_retriever is None:
115
+ return "❌ Process a video first."
116
+
117
+ try:
118
+ # Retrieve docs with scores
119
+ docs_and_scores = current_retriever.vectorstore.similarity_search_with_score(
120
+ question, k=8
121
+ )
122
+ for doc, score in docs_and_scores:
123
+ print(f"[DEBUG] score={score:.3f}, text={doc.page_content}")
124
+
125
+ # Extract docs
126
+ retrieved_docs = [doc for doc, _ in docs_and_scores]
127
+
128
+ # Build context and reply as before
129
+ context_text = "\n\n".join(doc.page_content for doc in retrieved_docs)
130
+ print("\nContext text:\n", context_text)
131
+ final_prompt = prompt.invoke({"context": context_text, "question": question})
132
+ answer = chat.invoke(final_prompt)
133
+ return answer.content
134
+
135
+ except Exception as e:
136
+ print(f"[ERROR] in answer_question: {e}")
137
+ return f"❌ Error: {str(e)}"
138
+
139
+ def process_video(video_url_or_id, question):
140
+ """Legacy function for backward compatibility."""
141
+ # Process video first
142
+ process_result = process_video_url(video_url_or_id)
143
+ if "❌" in process_result:
144
+ return process_result
145
+
146
+ # Then answer question
147
+ return answer_question(question)
148
+
149
+ def main():
150
+ with gr.Blocks(title="YouTube Transcript Q&A", theme=gr.themes.Soft()) as iface:
151
+ gr.Markdown("""
152
+ # YouTube Transcript Q&A
153
+
154
+ Ask questions about any YouTube video's content!
155
+
156
+ ## How to use:
157
+ 1. Paste a YouTube video URL (e.g., https://www.youtube.com/watch?v=JaRGJVrJBQ8) or just the video ID
158
+ 2. Click "Process Video" to download and process the transcript (this happens once per video)
159
+ 3. Type your question about the video content
160
+ 4. Click "Ask Question" to get your answer
161
+ """)
162
+
163
+ with gr.Row():
164
+ with gr.Column():
165
+ video_input = gr.Textbox(
166
+ label="YouTube Video URL or ID",
167
+ placeholder="Enter YouTube URL or video ID (e.g., JaRGJVrJBQ8)",
168
+ lines=1
169
+ )
170
+ process_btn = gr.Button("Process Video", variant="primary")
171
+ process_status = gr.Textbox(
172
+ label="Processing Status",
173
+ lines=1,
174
+ interactive=False
175
+ )
176
+
177
+ gr.Markdown("---")
178
+
179
+ question_input = gr.Textbox(
180
+ label="Your Question",
181
+ placeholder="What would you like to know about this video?",
182
+ lines=2
183
+ )
184
+ ask_btn = gr.Button("Ask Question", variant="secondary")
185
+
186
+ with gr.Column():
187
+ output = gr.Textbox(
188
+ label="Answer",
189
+ lines=8,
190
+ show_copy_button=True
191
+ )
192
+
193
+ # Example inputs
194
+ gr.Examples(
195
+ examples=[
196
+ ["https://www.youtube.com/watch?v=JaRGJVrJBQ8", "What is this video about?"],
197
+ ["JaRGJVrJBQ8", "What are the main topics discussed?"],
198
+ ["https://www.youtube.com/watch?v=JaRGJVrJBQ8", "Summarize the key points"]
199
+ ],
200
+ inputs=[video_input, question_input]
201
+ )
202
+
203
+ # Connect the buttons
204
+ process_btn.click(
205
+ fn=process_video_url,
206
+ inputs=[video_input],
207
+ outputs=[process_status]
208
+ )
209
+
210
+ ask_btn.click(
211
+ fn=answer_question,
212
+ inputs=[question_input],
213
+ outputs=[output]
214
+ )
215
+
216
+ iface.launch()
217
+
218
+ if __name__ == "__main__":
219
+ import sys
220
+ if len(sys.argv) > 1 and sys.argv[1] == "--test":
221
+ result = process_video("https://www.youtube.com/watch?v=gN-QWM5iY9M", "What is this video about?")
222
+ print(result)
223
+ else:
224
+ main()
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ youtube-transcript-api>=1.1.0
2
+ langchain-core>=0.3.65
3
+ langchain-community>=0.3.25
4
+ langchain-huggingface>=0.3.0
5
+ faiss-cpu>=1.11.0
6
+ gradio>=5.34.0
7
+ huggingface-hub>=0.33.0
8
+ sentence-transformers>=4.1.0
9
+ tf_keras>=2.18.0
test.ipynb ADDED
The diff for this file is too large to render. See raw diff