Spaces:
Sleeping
Sleeping
Update Jul 25
Browse files- FC_tool_main.py +103 -196
- app.py +1 -3
FC_tool_main.py
CHANGED
@@ -7,26 +7,22 @@ the LangChain library for natural language processing tasks and the YouTube Tran
|
|
7 |
API for fetching video transcripts.
|
8 |
|
9 |
Classes:
|
10 |
-
|
11 |
-
Extracts and formats
|
12 |
-
Timestamps are formatted for direct use in YouTube comments, enabling clickable
|
13 |
-
links to specific video sections when pasted.
|
14 |
-
SummaryExtractor:
|
15 |
-
Handles the extraction and formatting of video summaries.
|
16 |
QuestionAnswerExtractor:
|
17 |
Processes user questions and extracts answers from video transcripts.
|
18 |
YouTubeAgent:
|
19 |
Manages the overall agent setup for interacting with YouTube videos and processing user queries.
|
20 |
|
21 |
Key Features:
|
22 |
-
- Main points
|
23 |
-
- Video content summarization
|
24 |
- Question answering based on video content
|
25 |
- Flexible AI agent for handling various YouTube video-related tasks
|
26 |
"""
|
27 |
|
28 |
import os
|
29 |
import openai
|
|
|
30 |
from typing import List, Dict, Any, Union, Type
|
31 |
from youtube_transcript_api import YouTubeTranscriptApi
|
32 |
from langchain_core.pydantic_v1 import BaseModel, Field
|
@@ -40,7 +36,6 @@ from langchain_core.utils.function_calling import convert_to_openai_function
|
|
40 |
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
|
41 |
from langchain.agents.format_scratchpad import format_to_openai_functions
|
42 |
from langchain.memory import ConversationBufferWindowMemory
|
43 |
-
from dotenv import load_dotenv, find_dotenv
|
44 |
|
45 |
# _ = load_dotenv(find_dotenv()) # read local .env file
|
46 |
openai.api_key = os.getenv('OPENAI_API_KEY') #os.environ['OPENAI_API_KEY']
|
@@ -55,59 +50,63 @@ def set_temperature(new_temperature):
|
|
55 |
get_temperature = new_get_temperature
|
56 |
# print(f"Temperature set to: {get_temperature()}")
|
57 |
|
58 |
-
class
|
59 |
"""Pydantic model for representing extracted points from Youtube-Transcript"""
|
60 |
-
timestamp: float = Field(description="The timestamp (in floating-point number) of when main points are discussed
|
61 |
main_point: str = Field(description="A title for Main point.")
|
62 |
-
summary: str = Field(description="A summary of main points discussed at that timestamp.
|
63 |
emoji: str = Field(description="An emoji that matches the summary.")
|
64 |
-
|
65 |
-
class
|
66 |
"""Pydantic model for representing extracted points."""
|
67 |
main_point: str = Field(description="The main topic, theme, or subject extracted from the subtitle.")
|
|
|
68 |
summary: str = Field(description="The context or brief explanation of the main point.")
|
69 |
emoji: str = Field(description="An emoji that represents or summarizes the main point.")
|
70 |
-
|
71 |
-
|
72 |
-
class MainPointsExtractor:
|
73 |
"""
|
74 |
-
A tool for extracting and formatting main points from YouTube video transcripts.
|
75 |
|
76 |
-
This class provides methods to process transcripts
|
77 |
-
|
78 |
"""
|
79 |
|
80 |
-
class
|
81 |
-
"""Pydantic model for representing a collection of points."""
|
82 |
-
points: List[
|
83 |
|
84 |
-
class
|
85 |
-
"""Pydantic model for representing a collection of points."""
|
86 |
-
points: List[
|
87 |
|
88 |
@staticmethod
|
89 |
@tool(return_direct=True)
|
90 |
-
def
|
91 |
"""
|
92 |
-
Extracts and formats
|
93 |
|
94 |
Args:
|
95 |
youtube_video_id (str): The ID of the YouTube video.
|
96 |
|
97 |
Returns:
|
98 |
-
str: Formatted string of main points
|
99 |
"""
|
100 |
try:
|
101 |
-
transcript =
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
|
|
|
|
|
|
|
|
106 |
except Exception as e:
|
107 |
raise
|
108 |
|
109 |
@staticmethod
|
110 |
-
def
|
111 |
"""
|
112 |
Fetches the transcript for a YouTube video.
|
113 |
|
@@ -128,12 +127,12 @@ class MainPointsExtractor:
|
|
128 |
raise
|
129 |
|
130 |
@staticmethod
|
131 |
-
def
|
132 |
"""
|
133 |
Extracts main points from the transcript using NLP techniques.
|
134 |
|
135 |
This method maintains a conversation history to provide context for subsequent calls.
|
136 |
-
|
137 |
Args:
|
138 |
transcript (str): The full transcript of the video.
|
139 |
|
@@ -143,22 +142,19 @@ class MainPointsExtractor:
|
|
143 |
main_points_extraction_function = [convert_to_openai_function(info_model)]
|
144 |
|
145 |
model = ChatOpenAI(temperature=get_temperature())
|
146 |
-
|
147 |
-
extraction_model = model.bind(functions=main_points_extraction_function)
|
148 |
|
149 |
system_message = f"""
|
150 |
-
You are an AI assistant that extracts info from video transcripts.
|
151 |
-
|
152 |
-
|
|
|
|
|
|
|
|
|
|
|
153 |
|
154 |
-
In addition to these specific requirements, you have the authority to make other improvements as you see fit. This may include:
|
155 |
-
|
156 |
-
- Refining the summaries for clarity and conciseness
|
157 |
-
- Adjusting emoji choices to better represent the content
|
158 |
-
- Reorganizing points for better logical flow
|
159 |
-
- Removing redundant information
|
160 |
-
- Adding context where necessary
|
161 |
-
|
162 |
Your goal is to produce a refined and accurate representation of the main points from the video transcript. Use your judgment to balance adherence to the specific rules with overall improvement of the extracted information.
|
163 |
"""
|
164 |
|
@@ -169,11 +165,11 @@ class MainPointsExtractor:
|
|
169 |
|
170 |
extraction_chain = prompt | extraction_model | JsonKeyOutputFunctionsParser(key_name="points")
|
171 |
|
172 |
-
text_splitter = RecursiveCharacterTextSplitter(chunk_overlap=0, chunk_size=
|
173 |
|
174 |
prep = RunnableLambda(lambda x: [{"input": doc} for doc in text_splitter.split_text(x)])
|
175 |
|
176 |
-
chain = prep | extraction_chain.map() |
|
177 |
|
178 |
result_1 = chain.invoke(transcript)
|
179 |
|
@@ -183,17 +179,19 @@ class MainPointsExtractor:
|
|
183 |
def _flatten(matrix):
|
184 |
"""Flattens a 2D list into a 1D list."""
|
185 |
return [item for row in matrix for item in row]
|
186 |
-
|
187 |
@staticmethod
|
188 |
-
def
|
189 |
"""
|
190 |
-
Formats extracted main points into a YouTube-style comment.
|
191 |
|
192 |
Args:
|
193 |
-
|
|
|
|
|
194 |
|
195 |
Returns:
|
196 |
-
str: Formatted string representing the main points as a YouTube comment.
|
197 |
"""
|
198 |
def _format_timestamp(seconds):
|
199 |
hours = int(seconds // 3600)
|
@@ -202,125 +200,24 @@ class MainPointsExtractor:
|
|
202 |
return f"{hours:02}:{minutes:02}:{seconds:02}"
|
203 |
|
204 |
formatted_comment = ""
|
205 |
-
for
|
206 |
-
timestamp = _format_timestamp(
|
207 |
-
|
208 |
-
summary = entry['summary']
|
209 |
|
210 |
-
if
|
211 |
-
|
|
|
|
|
212 |
else:
|
213 |
-
|
214 |
-
|
215 |
-
formatted_comment += f"{timestamp} {emoji} {point}: {summary}\n"
|
216 |
|
217 |
return formatted_comment.strip()
|
218 |
-
|
219 |
-
#######################################################################################################################################
|
220 |
-
|
221 |
-
class Summary(BaseModel):
|
222 |
-
"""Pydantic model for representing extracted summary."""
|
223 |
-
summary: str = Field(description="Extract detailed information from the content.")
|
224 |
-
|
225 |
-
class SummaryExtractor:
|
226 |
-
"""
|
227 |
-
A tool for extracting and formatting summaries from YouTube video transcripts.
|
228 |
-
|
229 |
-
This class provides methods to process transcripts and generate concise summaries
|
230 |
-
using natural language processing techniques.
|
231 |
-
"""
|
232 |
-
|
233 |
-
class Info(BaseModel):
|
234 |
-
"""Pydantic model for representing a collection of summaries."""
|
235 |
-
summary: List[Summary]
|
236 |
-
|
237 |
-
@staticmethod
|
238 |
-
@tool(return_direct=False)
|
239 |
-
def get_youtube_video_summary(youtube_video_id: str) -> str:
|
240 |
-
"""
|
241 |
-
Extracts and formats a summary from a YouTube video transcript.
|
242 |
-
|
243 |
-
Args:
|
244 |
-
youtube_video_id (str): The ID of the YouTube video.
|
245 |
-
|
246 |
-
Returns:
|
247 |
-
str: Formatted string of the summary extracted from the video.
|
248 |
-
"""
|
249 |
-
try:
|
250 |
-
transcript = SummaryExtractor._get_youtube_video_transcript(youtube_video_id)
|
251 |
-
summary = SummaryExtractor._extract_summary(transcript)
|
252 |
-
return SummaryExtractor._format_summary(summary)
|
253 |
-
except Exception as e:
|
254 |
-
return f"Error extracting summary: {str(e)}"
|
255 |
-
|
256 |
-
@staticmethod
|
257 |
-
def _get_youtube_video_transcript(youtube_video_id: str) -> str:
|
258 |
-
"""
|
259 |
-
Fetches the transcript for a YouTube video.
|
260 |
-
|
261 |
-
Args:
|
262 |
-
youtube_video_id (str): The ID of the YouTube video.
|
263 |
-
|
264 |
-
Returns:
|
265 |
-
str: The full transcript of the video.
|
266 |
-
|
267 |
-
Raises:
|
268 |
-
Exception: If there's an error fetching the transcript.
|
269 |
-
"""
|
270 |
-
try:
|
271 |
-
transcript_json = YouTubeTranscriptApi.get_transcript(youtube_video_id)
|
272 |
-
transcript_data = [entry['text'] for entry in transcript_json]
|
273 |
-
return " ".join(transcript_data)
|
274 |
-
except Exception as e:
|
275 |
-
raise
|
276 |
-
|
277 |
-
@staticmethod
|
278 |
-
def _extract_summary(transcript: str) -> List[Summary]:
|
279 |
-
"""
|
280 |
-
Extracts a summary from a YouTube video transcript.
|
281 |
-
|
282 |
-
Args:
|
283 |
-
transcript (str): The full transcript of the video.
|
284 |
-
|
285 |
-
Returns:
|
286 |
-
Summary: A Summary object containing the extracted summary.
|
287 |
-
"""
|
288 |
-
summary_extraction_function = [convert_to_openai_function(SummaryExtractor.Info)]
|
289 |
-
|
290 |
-
model = ChatOpenAI(temperature=get_temperature())
|
291 |
-
|
292 |
-
extraction_model = model.bind(functions=summary_extraction_function)
|
293 |
-
|
294 |
-
prompt = ChatPromptTemplate.from_messages([("human", "{input}")])
|
295 |
-
|
296 |
-
extraction_chain = prompt | extraction_model | JsonKeyOutputFunctionsParser(key_name="summary")
|
297 |
-
|
298 |
-
text_splitter = RecursiveCharacterTextSplitter(chunk_overlap=0, chunk_size=8192, separators=[f" {char}" for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"])
|
299 |
-
|
300 |
-
prep = RunnableLambda(lambda x: [{"input": doc} for doc in text_splitter.split_text(x)])
|
301 |
-
|
302 |
-
chain = prep | extraction_chain.map() | MainPointsExtractor._flatten
|
303 |
-
|
304 |
-
return chain.invoke(transcript)
|
305 |
-
|
306 |
-
@staticmethod
|
307 |
-
def _format_summary(summaries: List[Summary]) -> str:
|
308 |
-
"""
|
309 |
-
Formats the list of summaries into a single string.
|
310 |
-
Args:
|
311 |
-
summaries (List[Summary]): List of Summary objects.
|
312 |
-
Returns:
|
313 |
-
str: A formatted string containing all summaries.
|
314 |
-
"""
|
315 |
-
return "\n\n".join([s["summary"] for s in summaries])
|
316 |
-
|
317 |
-
#############################################################################################################################################################
|
318 |
|
319 |
class Answer(BaseModel):
|
320 |
"""Pydantic model for representing an answer to a question."""
|
321 |
answer: str = Field(description="The answer to the user's question based on the video transcript.")
|
322 |
confidence: float = Field(description="A confidence score between 0 and 1 indicating how certain the model is about the answer.")
|
323 |
-
|
324 |
class QuestionAnswerExtractor:
|
325 |
"""
|
326 |
A tool for answering questions about YouTube videos based on their transcripts.
|
@@ -349,7 +246,7 @@ class QuestionAnswerExtractor:
|
|
349 |
try:
|
350 |
transcript = QuestionAnswerExtractor._get_youtube_video_transcript(youtube_video_id)
|
351 |
answer = QuestionAnswerExtractor._extract_answer(transcript, question)
|
352 |
-
return
|
353 |
except Exception as e:
|
354 |
return f"Error answering question: {str(e)}"
|
355 |
|
@@ -384,7 +281,7 @@ class QuestionAnswerExtractor:
|
|
384 |
question (str): The user's question about the video.
|
385 |
|
386 |
Returns:
|
387 |
-
List[Answer]: A list
|
388 |
"""
|
389 |
answer_extraction_function = [convert_to_openai_function(QuestionAnswerExtractor.Info)]
|
390 |
|
@@ -408,34 +305,37 @@ class QuestionAnswerExtractor:
|
|
408 |
|
409 |
chain = prep | extraction_chain.map() | QuestionAnswerExtractor._flatten
|
410 |
|
411 |
-
|
|
|
412 |
|
413 |
-
|
414 |
-
|
415 |
-
"""Flattens a 2D list into a 1D list."""
|
416 |
-
return [item for row in matrix for item in row]
|
417 |
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
Formats the list of answers into a single string.
|
422 |
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
"""
|
429 |
-
if not answers:
|
430 |
-
return "I couldn't find an answer to your question based on the video transcript."
|
431 |
|
432 |
-
|
433 |
-
|
434 |
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
439 |
class YouTubeAgent:
|
440 |
"""
|
441 |
An agent for interacting with YouTube videos and processing user queries.
|
@@ -448,12 +348,19 @@ class YouTubeAgent:
|
|
448 |
"""Initializes the YouTubeAgent with necessary tools and components."""
|
449 |
|
450 |
self.tools = [
|
451 |
-
|
452 |
-
|
453 |
-
QuestionAnswerExtractor.get_answer
|
454 |
]
|
455 |
|
456 |
-
self.sys_message = "You are a helpful assistant.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
457 |
|
458 |
self.functions = [convert_to_openai_function(f) for f in self.tools]
|
459 |
|
|
|
7 |
API for fetching video transcripts.
|
8 |
|
9 |
Classes:
|
10 |
+
YouTubeTranscriptPointsExtractor:
|
11 |
+
Extracts and formats comments with clickable timestamps from a YouTube video transcript.
|
|
|
|
|
|
|
|
|
12 |
QuestionAnswerExtractor:
|
13 |
Processes user questions and extracts answers from video transcripts.
|
14 |
YouTubeAgent:
|
15 |
Manages the overall agent setup for interacting with YouTube videos and processing user queries.
|
16 |
|
17 |
Key Features:
|
18 |
+
- Main points formatted as youtube comment with clickable timestamps
|
|
|
19 |
- Question answering based on video content
|
20 |
- Flexible AI agent for handling various YouTube video-related tasks
|
21 |
"""
|
22 |
|
23 |
import os
|
24 |
import openai
|
25 |
+
import json
|
26 |
from typing import List, Dict, Any, Union, Type
|
27 |
from youtube_transcript_api import YouTubeTranscriptApi
|
28 |
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
|
36 |
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
|
37 |
from langchain.agents.format_scratchpad import format_to_openai_functions
|
38 |
from langchain.memory import ConversationBufferWindowMemory
|
|
|
39 |
|
40 |
# _ = load_dotenv(find_dotenv()) # read local .env file
|
41 |
openai.api_key = os.getenv('OPENAI_API_KEY') #os.environ['OPENAI_API_KEY']
|
|
|
50 |
get_temperature = new_get_temperature
|
51 |
# print(f"Temperature set to: {get_temperature()}")
|
52 |
|
53 |
+
class TimestampedPoint_1(BaseModel):
|
54 |
"""Pydantic model for representing extracted points from Youtube-Transcript"""
|
55 |
+
timestamp: float = Field(description="The timestamp (in floating-point number) of when main points are discussed in the video.")
|
56 |
main_point: str = Field(description="A title for Main point.")
|
57 |
+
summary: str = Field(description="A summary of main points discussed at that timestamp.")
|
58 |
emoji: str = Field(description="An emoji that matches the summary.")
|
59 |
+
|
60 |
+
class TimestampedPoint_2(BaseModel):
|
61 |
"""Pydantic model for representing extracted points."""
|
62 |
main_point: str = Field(description="The main topic, theme, or subject extracted from the subtitle.")
|
63 |
+
timestamp: float = Field(description="The timestamp (in floating-point number) from the video where the main point is mentioned.")
|
64 |
summary: str = Field(description="The context or brief explanation of the main point.")
|
65 |
emoji: str = Field(description="An emoji that represents or summarizes the main point.")
|
66 |
+
|
67 |
+
class YouTubeTranscriptPointsExtractor:
|
|
|
68 |
"""
|
69 |
+
A tool for extracting and formatting main points with clickable timestamps from YouTube video transcripts.
|
70 |
|
71 |
+
This class provides methods to process transcripts, identify key points,
|
72 |
+
and format them for use in YouTube comments with clickable timestamps.
|
73 |
"""
|
74 |
|
75 |
+
class PointsCollection_1(BaseModel):
|
76 |
+
"""Pydantic model for representing a collection of timestamped points."""
|
77 |
+
points: List[TimestampedPoint_1]
|
78 |
|
79 |
+
class PointsCollection_2(BaseModel):
|
80 |
+
"""Pydantic model for representing a collection of timestamped points."""
|
81 |
+
points: List[TimestampedPoint_2]
|
82 |
|
83 |
@staticmethod
|
84 |
@tool(return_direct=True)
|
85 |
+
def extract_clickable_points(youtube_video_id: str) -> str:
|
86 |
"""
|
87 |
+
Extracts and formats comments with clickable timestamps from a YouTube video transcript.
|
88 |
|
89 |
Args:
|
90 |
youtube_video_id (str): The ID of the YouTube video.
|
91 |
|
92 |
Returns:
|
93 |
+
str: Formatted string of main points with clickable timestamps, ready for use in YouTube comments.
|
94 |
"""
|
95 |
try:
|
96 |
+
transcript = YouTubeTranscriptPointsExtractor._fetch_transcript(youtube_video_id)
|
97 |
+
extracted_points_1 = YouTubeTranscriptPointsExtractor._process_transcript(transcript, YouTubeTranscriptPointsExtractor.PointsCollection_1)
|
98 |
+
formatted_output_1 = YouTubeTranscriptPointsExtractor._format_for_youtube_comment(extracted_points_1, True)
|
99 |
+
formatted_output_1a = YouTubeTranscriptPointsExtractor._format_for_youtube_comment(extracted_points_1, False)
|
100 |
+
|
101 |
+
extracted_points_2 = YouTubeTranscriptPointsExtractor._process_transcript(transcript, YouTubeTranscriptPointsExtractor.PointsCollection_2)
|
102 |
+
formatted_output_2 = YouTubeTranscriptPointsExtractor._format_for_youtube_comment(extracted_points_2, True)
|
103 |
+
formatted_output_2a = YouTubeTranscriptPointsExtractor._format_for_youtube_comment(extracted_points_2, False)
|
104 |
+
return f"""Main points extracted from YouTube video (ID: {youtube_video_id})\nOutput_style_1:\n```\n{formatted_output_1}\n```\nOutput_Style_1a:\n```\n{formatted_output_1a}\n```\nOutput_Style_2a:\n```\n{formatted_output_2}\n```\nOutput_Style_2a:\n```\n{formatted_output_2a}\n```\nChoose the style that best suits your needs for presenting the main points of the video."""
|
105 |
except Exception as e:
|
106 |
raise
|
107 |
|
108 |
@staticmethod
|
109 |
+
def _fetch_transcript(youtube_video_id: str) -> str:
|
110 |
"""
|
111 |
Fetches the transcript for a YouTube video.
|
112 |
|
|
|
127 |
raise
|
128 |
|
129 |
@staticmethod
|
130 |
+
def _process_transcript(transcript: str, info_model: Union[Type[PointsCollection_1], Type[PointsCollection_2]]) -> List[Dict[str, Any]]:
|
131 |
"""
|
132 |
Extracts main points from the transcript using NLP techniques.
|
133 |
|
134 |
This method maintains a conversation history to provide context for subsequent calls.
|
135 |
+
|
136 |
Args:
|
137 |
transcript (str): The full transcript of the video.
|
138 |
|
|
|
142 |
main_points_extraction_function = [convert_to_openai_function(info_model)]
|
143 |
|
144 |
model = ChatOpenAI(temperature=get_temperature())
|
145 |
+
|
146 |
+
extraction_model = model.bind(functions=main_points_extraction_function, function_call={"name": info_model.__name__})
|
147 |
|
148 |
system_message = f"""
|
149 |
+
You are an AI assistant that extracts essential info from video transcripts.
|
150 |
+
You have the authority to make improvements as you see fit.
|
151 |
+
|
152 |
+
Rules To Follow:
|
153 |
+
- Refining the summaries for clarity and conciseness.
|
154 |
+
- Adjusting emoji choices to better represent the content.
|
155 |
+
- Removing redundant information.
|
156 |
+
- Grouping two points into a single point if the timestamps are close enough.
|
157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
Your goal is to produce a refined and accurate representation of the main points from the video transcript. Use your judgment to balance adherence to the specific rules with overall improvement of the extracted information.
|
159 |
"""
|
160 |
|
|
|
165 |
|
166 |
extraction_chain = prompt | extraction_model | JsonKeyOutputFunctionsParser(key_name="points")
|
167 |
|
168 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_overlap=0, chunk_size=16000, separators=[f" {char}" for char in "123456789"])
|
169 |
|
170 |
prep = RunnableLambda(lambda x: [{"input": doc} for doc in text_splitter.split_text(x)])
|
171 |
|
172 |
+
chain = prep | extraction_chain.map() | YouTubeTranscriptPointsExtractor._flatten
|
173 |
|
174 |
result_1 = chain.invoke(transcript)
|
175 |
|
|
|
179 |
def _flatten(matrix):
|
180 |
"""Flattens a 2D list into a 1D list."""
|
181 |
return [item for row in matrix for item in row]
|
182 |
+
|
183 |
@staticmethod
|
184 |
+
def _format_for_youtube_comment(points: List[Dict[str, Any]], detailed: bool = True) -> str:
|
185 |
"""
|
186 |
+
Formats extracted main points into a YouTube-style comment with clickable timestamps.
|
187 |
|
188 |
Args:
|
189 |
+
points (List[Dict[str, Any]]): List of dictionaries containing main points with timestamps.
|
190 |
+
detailed (bool): If True, returns a detailed format with emojis and summaries.
|
191 |
+
If False, returns a simpler format with just timestamps and main points.
|
192 |
|
193 |
Returns:
|
194 |
+
str: Formatted string representing the main points as a YouTube comment with clickable timestamps.
|
195 |
"""
|
196 |
def _format_timestamp(seconds):
|
197 |
hours = int(seconds // 3600)
|
|
|
200 |
return f"{hours:02}:{minutes:02}:{seconds:02}"
|
201 |
|
202 |
formatted_comment = ""
|
203 |
+
for point in points:
|
204 |
+
timestamp = _format_timestamp(point['timestamp'])
|
205 |
+
main_point = point['main_point'].rstrip('.')
|
|
|
206 |
|
207 |
+
if detailed:
|
208 |
+
emoji = point['emoji']
|
209 |
+
summary = point['summary']
|
210 |
+
formatted_comment += f"{timestamp} {emoji} {main_point}: {summary}\n"
|
211 |
else:
|
212 |
+
formatted_comment += f"{timestamp} {main_point}\n"
|
|
|
|
|
213 |
|
214 |
return formatted_comment.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
|
216 |
class Answer(BaseModel):
|
217 |
"""Pydantic model for representing an answer to a question."""
|
218 |
answer: str = Field(description="The answer to the user's question based on the video transcript.")
|
219 |
confidence: float = Field(description="A confidence score between 0 and 1 indicating how certain the model is about the answer.")
|
220 |
+
|
221 |
class QuestionAnswerExtractor:
|
222 |
"""
|
223 |
A tool for answering questions about YouTube videos based on their transcripts.
|
|
|
246 |
try:
|
247 |
transcript = QuestionAnswerExtractor._get_youtube_video_transcript(youtube_video_id)
|
248 |
answer = QuestionAnswerExtractor._extract_answer(transcript, question)
|
249 |
+
return answer
|
250 |
except Exception as e:
|
251 |
return f"Error answering question: {str(e)}"
|
252 |
|
|
|
281 |
question (str): The user's question about the video.
|
282 |
|
283 |
Returns:
|
284 |
+
List[Answer]: A list containing a single Answer object with the consolidated answer.
|
285 |
"""
|
286 |
answer_extraction_function = [convert_to_openai_function(QuestionAnswerExtractor.Info)]
|
287 |
|
|
|
305 |
|
306 |
chain = prep | extraction_chain.map() | QuestionAnswerExtractor._flatten
|
307 |
|
308 |
+
# Get partial answers
|
309 |
+
partial_answers = chain.invoke({"transcript": transcript, "question": question})
|
310 |
|
311 |
+
# Filter out low-confidence answers
|
312 |
+
filtered_answers = [answer for answer in partial_answers if answer['confidence'] > 0.4]
|
|
|
|
|
313 |
|
314 |
+
# If all answers were filtered out, return a low-confidence "no answer" response
|
315 |
+
if not filtered_answers:
|
316 |
+
return "I couldn't find a reliable answer to your question based on the video transcript."
|
|
|
317 |
|
318 |
+
# Consolidate filtered partial answers
|
319 |
+
consolidation_prompt = ChatPromptTemplate.from_messages([
|
320 |
+
("system", "You are an AI assistant tasked with consolidating multiple partial answers into a comprehensive final answer."),
|
321 |
+
("human", "Question: {question}\n\nPartial Answers: {partial_answers}\n\nPlease provide a consolidated, comprehensive answer to the question based on these partial answers. Ignore any information from answers with low confidence (0.5 or below).")
|
322 |
+
])
|
|
|
|
|
|
|
323 |
|
324 |
+
consolidation_model = ChatOpenAI(temperature=get_temperature())
|
325 |
+
consolidation_chain = consolidation_prompt | consolidation_model
|
326 |
|
327 |
+
final_answer = consolidation_chain.invoke({
|
328 |
+
"question": question,
|
329 |
+
"partial_answers": json.dumps(filtered_answers, indent=2)
|
330 |
+
})
|
331 |
+
|
332 |
+
return final_answer.content
|
333 |
+
|
334 |
+
@staticmethod
|
335 |
+
def _flatten(matrix):
|
336 |
+
"""Flattens a 2D list into a 1D list."""
|
337 |
+
return [item for row in matrix for item in row]
|
338 |
+
|
339 |
class YouTubeAgent:
|
340 |
"""
|
341 |
An agent for interacting with YouTube videos and processing user queries.
|
|
|
348 |
"""Initializes the YouTubeAgent with necessary tools and components."""
|
349 |
|
350 |
self.tools = [
|
351 |
+
QuestionAnswerExtractor.get_answer,
|
352 |
+
YouTubeTranscriptPointsExtractor.extract_clickable_points,
|
|
|
353 |
]
|
354 |
|
355 |
+
self.sys_message = """You are a helpful assistant specialized in processing YouTube video transcripts and answering questions about video content.'
|
356 |
+
|
357 |
+
Important instructions:
|
358 |
+
1. Only use the 'extract_clickable_points' tool when the user explicitly asks for clickable points or timestamps from a video.
|
359 |
+
2. For all other queries, including general questions about video content, use the 'get_answer' tool.
|
360 |
+
3. If the user's query is unclear, ask for clarification before using any tools.
|
361 |
+
4. Always provide concise and relevant responses based on the tool outputs.
|
362 |
+
|
363 |
+
Remember to interpret the user's intent carefully and use the appropriate tool for each situation."""
|
364 |
|
365 |
self.functions = [convert_to_openai_function(f) for f in self.tools]
|
366 |
|
app.py
CHANGED
@@ -66,10 +66,8 @@ with gr.Blocks() as demo:
|
|
66 |
API for fetching video transcripts.
|
67 |
|
68 |
Key Features:
|
69 |
-
- Main points
|
70 |
-
- Video content summarization
|
71 |
- Question answering based on video content
|
72 |
-
- Flexible AI agent for handling various YouTube video-related tasks
|
73 |
|
74 |
Simply enter your question or request along with a YouTube video link, and the AI will process and respond accordingly.
|
75 |
Adjust the temperature slider to control the creativity of the AI's responses.
|
|
|
66 |
API for fetching video transcripts.
|
67 |
|
68 |
Key Features:
|
69 |
+
- Main points formatted as youtube comment with clickable timestamps
|
|
|
70 |
- Question answering based on video content
|
|
|
71 |
|
72 |
Simply enter your question or request along with a YouTube video link, and the AI will process and respond accordingly.
|
73 |
Adjust the temperature slider to control the creativity of the AI's responses.
|