Mubin1917 commited on
Commit
857dbaf
1 Parent(s): 17e8e9f
Files changed (3) hide show
  1. app.py +97 -59
  2. requirements.txt +12 -1
  3. youtube_FC_14.py +443 -0
app.py CHANGED
@@ -1,63 +1,101 @@
 
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
- """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
6
- """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
-
9
-
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
-
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
-
26
- messages.append({"role": "user", "content": message})
27
-
28
- response = ""
29
-
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
38
-
39
- response += token
40
- yield response
41
-
42
- """
43
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
44
- """
45
- demo = gr.ChatInterface(
46
- respond,
47
- additional_inputs=[
48
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
49
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
50
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
51
- gr.Slider(
52
- minimum=0.1,
53
- maximum=1.0,
54
- value=0.95,
55
- step=0.05,
56
- label="Top-p (nucleus sampling)",
57
- ),
58
- ],
59
- )
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  if __name__ == "__main__":
63
- demo.launch()
 
1
+ import os
2
+ import openai
3
  import gradio as gr
4
+ from youtube_transcript_api import YouTubeTranscriptApi
5
+ from langchain_openai import ChatOpenAI
6
+ from langchain.agents import AgentExecutor
7
+ from langchain.memory import ConversationBufferWindowMemory
8
+ from youtube_FC_14 import YouTubeTranscriptTool, MainPointsExtractor, SummaryExtractor, YouTubeAgent
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ import logging
11
+ logging.getLogger().setLevel(logging.ERROR)
12
+
13
+ import warnings
14
+ warnings.filterwarnings("ignore")
15
+
16
+ class ChatBot:
17
+ def __init__(self):
18
+ self.youtube_agent = None
19
+ self.api_key = None
20
+
21
+ def initialize_agent(self, api_key):
22
+ if api_key:
23
+ os.environ['OPENAI_API_KEY'] = api_key
24
+ openai.api_key = api_key
25
+ self.api_key = api_key
26
+ self.youtube_agent = YouTubeAgent()
27
+ return "API key set successfully. Agent initialized."
28
+ else:
29
+ return "Please provide a valid API key."
30
+
31
+ def chat(self, message, history):
32
+ if not self.youtube_agent:
33
+ return "Please set your OpenAI API key first."
34
+
35
+ try:
36
+ response = self.youtube_agent.invoke(message)
37
+ return response
38
+ except Exception as e:
39
+ return f"An error occurred: {str(e)}"
40
+
41
+ chatbot = ChatBot() # Create an instance of ChatBot
42
+
43
+ def set_api_key(api_key):
44
+ return chatbot.initialize_agent(api_key)
45
+
46
+ def user_message(message, history):
47
+ return "", history + [[message, None]]
48
+
49
+ def bot_message(history):
50
+ user_message = history[-1][0]
51
+ bot_response = chatbot.chat(user_message, history)
52
+ history[-1][1] = bot_response
53
+ return history
54
+
55
+ def use_example(example, text_input):
56
+ return example
57
+
58
+ # Example messages
59
+ example_messages = [
60
+ "What tools are available for use?",
61
+ "What is the following video about? https://www.youtube.com/watch?v=dZxbVGhpEkI",
62
+ "Can you summarize this video? https://www.youtube.com/watch?v=hM8unyUM6KA",
63
+ "Extract the main points from this video: https://www.youtube.com/watch?v=UF8uR6Z6KLc"
64
+ ]
65
+
66
+ with gr.Blocks() as demo:
67
+ gr.Markdown("# YouTube Video Analysis Chatbot")
68
+
69
+ with gr.Row():
70
+ api_key_input = gr.Textbox(type="password", label="Enter your OpenAI API key")
71
+ api_key_button = gr.Button("Set API Key")
72
+
73
+ api_key_status = gr.Textbox(label="API Key Status", interactive=False)
74
+
75
+ chatbot_interface = gr.Chatbot()
76
+ msg = gr.Textbox(label="Message")
77
+
78
+ with gr.Row():
79
+ submit_btn = gr.Button("Submit")
80
+ clear_btn = gr.Button("Clear")
81
+
82
+ gr.Markdown("## Example Messages")
83
+ example_btns = [gr.Button(i) for i in example_messages]
84
+
85
+ api_key_button.click(set_api_key, inputs=api_key_input, outputs=api_key_status)
86
+
87
+ submit_btn.click(user_message, [msg, chatbot_interface], [msg, chatbot_interface], queue=False).then(
88
+ bot_message, chatbot_interface, chatbot_interface
89
+ )
90
+
91
+ msg.submit(user_message, [msg, chatbot_interface], [msg, chatbot_interface], queue=False).then(
92
+ bot_message, chatbot_interface, chatbot_interface
93
+ )
94
+
95
+ clear_btn.click(lambda: None, None, chatbot_interface, queue=False)
96
+
97
+ for btn, example in zip(example_btns, example_messages):
98
+ btn.click(use_example, inputs=[gr.Textbox(value=example, visible=False)], outputs=msg)
99
 
100
  if __name__ == "__main__":
101
+ demo.launch()
requirements.txt CHANGED
@@ -1 +1,12 @@
1
- huggingface_hub==0.22.2
 
 
 
 
 
 
 
 
 
 
 
 
1
+ huggingface_hub==0.22.2
2
+ youtube-transcript-api==0.6.2
3
+ gradio==4.38.1
4
+ tiktoken==0.7.
5
+ pytube==15.0.0
6
+ langchain==0.2.8
7
+ langchain-community==0.2.7
8
+ langchain-core==0.2.19
9
+ langchain-openai==0.1.16
10
+ langchain-text-splitters==0.2.2
11
+ pyperclip==1.9.0
12
+ openai==1.35.13
youtube_FC_14.py ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ YouTube Video Analysis Module
3
+
4
+ This module provides tools for analyzing YouTube videos, including transcript extraction
5
+ and main points summarization. It uses the LangChain library for natural language
6
+ processing tasks and the YouTube Transcript API for fetching video transcripts.
7
+
8
+ Classes:
9
+ YouTubeTranscriptTool: Handles fetching and processing of YouTube video transcripts.
10
+ MainPointsExtractor: Extracts and formats main points from YouTube video transcripts.
11
+ YouTubeAgent: Manages the overall agent setup for interacting with YouTube videos.
12
+ SummaryExtractor: Extracts summaries from YouTube video transcripts.
13
+
14
+ Usage:
15
+ youtube_agent = YouTubeAgent()
16
+ video_link = "https://www.youtube.com/watch?v=VIDEO_ID"
17
+ results = process_video(video_link, youtube_agent)
18
+ """
19
+
20
+ import os
21
+ import openai
22
+ from typing import List, Dict, Any
23
+ from youtube_transcript_api import YouTubeTranscriptApi
24
+ from langchain_core.pydantic_v1 import BaseModel, Field
25
+ from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
26
+ from langchain_openai import ChatOpenAI
27
+ from langchain.schema.runnable import RunnableLambda, RunnablePassthrough
28
+ from langchain.agents import tool, AgentExecutor
29
+ from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser
30
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
31
+ from langchain_core.utils.function_calling import convert_to_openai_function
32
+ from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
33
+ from langchain.agents.format_scratchpad import format_to_openai_functions
34
+ from langchain.memory import ConversationBufferWindowMemory
35
+ from functools import wraps
36
+ import functools
37
+ import logging
38
+ import traceback
39
+
40
+ # Set up logging with more detailed format
41
+ logging.basicConfig(level=logging.INFO,
42
+ format='%(asctime)s - %(levelname)s - %(name)s - %(filename)s:%(lineno)d - %(message)s')
43
+ logger = logging.getLogger(__name__)
44
+
45
+ # Define a decorator for error logging
46
+ def log_errors(func):
47
+ @wraps(func)
48
+ def wrapper(*args, **kwargs):
49
+ try:
50
+ return func(*args, **kwargs)
51
+ except Exception as e:
52
+ logger.error(f"Error in {func.__name__}: {str(e)}")
53
+ logger.error(f"Traceback: {traceback.format_exc()}")
54
+ raise
55
+ return wrapper
56
+
57
+ class YouTubeTranscriptTool:
58
+ """
59
+ A tool for fetching and processing YouTube video transcripts.
60
+
61
+ This class provides methods to retrieve transcripts with or without timestamps,
62
+ and to split transcripts into manageable chunks.
63
+ """
64
+
65
+ @staticmethod
66
+ @tool(return_direct=True)
67
+ def get_transcript_with_timestamps(youtube_video_id: str, chunk_number: int = 0) -> str:
68
+ """
69
+ Retrieves a YouTube video transcript with timestamps.
70
+
71
+ Args:
72
+ youtube_video_id (str): The ID of the YouTube video.
73
+ chunk_number (int): The index of the transcript chunk to retrieve.
74
+
75
+ Returns:
76
+ str: The requested transcript chunk with timestamps.
77
+ """
78
+ return YouTubeTranscriptTool._get_transcript(youtube_video_id, chunk_number, include_timestamps=True)
79
+
80
+ @staticmethod
81
+ @tool(return_direct=True)
82
+ def get_transcript_without_timestamps(youtube_video_id: str, chunk_number: int = 0) -> str:
83
+ """
84
+ Retrieves a YouTube video transcript without timestamps.
85
+
86
+ Args:
87
+ youtube_video_id (str): The ID of the YouTube video.
88
+ chunk_number (int): The index of the transcript chunk to retrieve.
89
+
90
+ Returns:
91
+ str: The requested transcript chunk without timestamps.
92
+ """
93
+ return YouTubeTranscriptTool._get_transcript(youtube_video_id, chunk_number, include_timestamps=False)
94
+
95
+ @staticmethod
96
+ @log_errors
97
+ def _get_transcript(youtube_video_id: str, chunk_number: int, include_timestamps: bool) -> str:
98
+ """
99
+ Internal method to fetch and process the transcript.
100
+
101
+ Args:
102
+ youtube_video_id (str): The ID of the YouTube video.
103
+ chunk_number (int): The index of the transcript chunk to retrieve.
104
+ include_timestamps (bool): Whether to include timestamps in the transcript.
105
+
106
+ Returns:
107
+ str: The processed transcript chunk.
108
+
109
+ Raises:
110
+ ValueError: If the requested chunk number is out of range.
111
+ """
112
+ try:
113
+ transcript_json = YouTubeTranscriptApi.get_transcript(youtube_video_id)
114
+ text_splitter = RecursiveCharacterTextSplitter(
115
+ chunk_size=8192,
116
+ chunk_overlap=0,
117
+ separators=[f" {char}" for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
118
+ )
119
+
120
+ if include_timestamps:
121
+ transcript_data = [f"{entry['start']:.2f}: {entry['text']} " for entry in transcript_json]
122
+ else:
123
+ transcript_data = [entry['text'] for entry in transcript_json]
124
+
125
+ transcript_text = " ".join(transcript_data)
126
+ transcript_splits = text_splitter.split_text(transcript_text)
127
+
128
+ if chunk_number >= len(transcript_splits):
129
+ raise ValueError(f"Chunk number {chunk_number} is out of range. Total chunks: {len(transcript_splits)}")
130
+
131
+ chunked_text = transcript_splits[chunk_number]
132
+
133
+ return YouTubeTranscriptTool._format_response(transcript_splits, chunk_number, chunked_text)
134
+ except Exception as e:
135
+ logger.error(f"Error in _get_transcript: {str(e)}")
136
+ return f"Error fetching transcript: {str(e)}"
137
+
138
+ @staticmethod
139
+ def _format_response(transcript_splits: List[str], chunk_number: int, chunked_text: str) -> str:
140
+ """
141
+ Formats the transcript chunk response.
142
+
143
+ Args:
144
+ transcript_splits (List[str]): All transcript chunks.
145
+ chunk_number (int): The index of the current chunk.
146
+ chunked_text (str): The text of the current chunk.
147
+
148
+ Returns:
149
+ str: Formatted response string.
150
+ """
151
+ if len(transcript_splits) == 1:
152
+ return f"Note: Complete subtitles returned.\n\nSubtitles:{chunked_text}"
153
+ elif chunk_number == len(transcript_splits) - 1:
154
+ return f"Note: Last chunk of subtitles returned.\n\nSubtitles:{chunked_text}"
155
+ else:
156
+ return f"Note: Partial subtitles returned. To get the next chunk, use chunk_number = {chunk_number + 1}.\n\nSubtitles:{chunked_text}"
157
+
158
+ class Points(BaseModel):
159
+ """Pydantic model for representing extracted points."""
160
+ point: str = Field(description="The main topic, theme, or subject extracted from the subtitle.")
161
+ context: str = Field(description="The context or brief explanation of the main point.")
162
+ emoji: str = Field(description="An emoji that represents or summarizes the main point.")
163
+ timestamp: float = Field(description="The timestamp (in floating-point number) from the video where the main point is mentioned.")
164
+
165
+ class MainPointsExtractor:
166
+ """
167
+ A tool for extracting and formatting main points from YouTube video transcripts.
168
+
169
+ This class provides methods to process transcripts and identify key points
170
+ using natural language processing techniques.
171
+ """
172
+
173
+ class Info(BaseModel):
174
+ """Pydantic model for representing a collection of points."""
175
+ points: List[Points]
176
+
177
+ @staticmethod
178
+ @tool(return_direct=True)
179
+ @log_errors
180
+ def get_youtube_video_main_points(youtube_video_id: str) -> str:
181
+ """
182
+ Extracts and formats main points from a YouTube video transcript.
183
+
184
+ Args:
185
+ youtube_video_id (str): The ID of the YouTube video.
186
+
187
+ Returns:
188
+ str: Formatted string of main points extracted from the video.
189
+ """
190
+ try:
191
+ transcript = MainPointsExtractor._get_youtube_video_transcript(youtube_video_id)
192
+ main_points = MainPointsExtractor._extract_main_points(transcript)
193
+ return MainPointsExtractor._format_youtube_comment(main_points)
194
+ except Exception as e:
195
+ logger.error(f"Error in get_youtube_video_main_points: {str(e)}")
196
+ return f"Error extracting main points: {str(e)}"
197
+
198
+ @staticmethod
199
+ @log_errors
200
+ def _get_youtube_video_transcript(youtube_video_id: str) -> str:
201
+ """
202
+ Fetches the transcript for a YouTube video.
203
+
204
+ Args:
205
+ youtube_video_id (str): The ID of the YouTube video.
206
+
207
+ Returns:
208
+ str: The full transcript of the video.
209
+
210
+ Raises:
211
+ Exception: If there's an error fetching the transcript.
212
+ """
213
+ try:
214
+ transcript_json = YouTubeTranscriptApi.get_transcript(youtube_video_id)
215
+ transcript_data = [f"{entry['start']:.2f}: {entry['text']} " for entry in transcript_json]
216
+ return "".join(transcript_data)
217
+ except Exception as e:
218
+ logger.error(f"Error fetching transcript: {str(e)}")
219
+ raise
220
+
221
+ @staticmethod
222
+ @functools.lru_cache(maxsize=16)
223
+ def _extract_main_points(transcript: str) -> List[Dict[str, Any]]:
224
+ """
225
+ Extracts main points from the transcript using NLP techniques.
226
+
227
+ This method is cached to improve performance for repeated calls.
228
+
229
+ Args:
230
+ transcript (str): The full transcript of the video.
231
+
232
+ Returns:
233
+ List[Dict[str, Any]]: A list of dictionaries containing extracted main points.
234
+ """
235
+ main_points_extraction_function = [convert_to_openai_function(MainPointsExtractor.Info)]
236
+
237
+ model = ChatOpenAI(temperature=0)
238
+ extraction_model = model.bind(functions=main_points_extraction_function, function_call={"name": "Info"})
239
+
240
+ prompt = ChatPromptTemplate.from_messages([("human", "{input}")])
241
+ extraction_chain = prompt | extraction_model | JsonKeyOutputFunctionsParser(key_name="points")
242
+
243
+ text_splitter = RecursiveCharacterTextSplitter(chunk_overlap=0, chunk_size=8192, separators=[f" {char}" for char in "123456789"])
244
+
245
+ prep = RunnableLambda(lambda x: [{"input": doc} for doc in text_splitter.split_text(x)])
246
+
247
+ chain = prep | extraction_chain.map() | MainPointsExtractor._flatten
248
+
249
+ return chain.invoke(transcript)
250
+
251
+ @staticmethod
252
+ @log_errors
253
+ def _flatten(matrix):
254
+ """Flattens a 2D list into a 1D list."""
255
+ return [item for row in matrix for item in row]
256
+
257
+ @staticmethod
258
+ @log_errors
259
+ def _format_youtube_comment(json_data: List[Dict[str, Any]]) -> str:
260
+ """
261
+ Formats extracted main points into a YouTube-style comment.
262
+
263
+ Args:
264
+ json_data (List[Dict[str, Any]]): List of dictionaries containing main points.
265
+
266
+ Returns:
267
+ str: Formatted string representing the main points as a YouTube comment.
268
+ """
269
+ def _format_timestamp(seconds):
270
+ hours = int(seconds // 3600)
271
+ minutes = int((seconds % 3600) // 60)
272
+ seconds = int(seconds % 60)
273
+ return f"{hours:02}:{minutes:02}:{seconds:02}"
274
+
275
+ formatted_comment = ""
276
+ for entry in json_data:
277
+ timestamp = _format_timestamp(entry['timestamp'])
278
+ emoji = entry['emoji']
279
+ point = entry['point']
280
+ context = entry['context']
281
+ formatted_comment += f"{timestamp} {emoji} {point}: {context}\n"
282
+
283
+ return formatted_comment.strip()
284
+
285
+ class Summary(BaseModel):
286
+ """Pydantic model for representing extracted summary."""
287
+ summary: str = Field(description="Extract detailed information from the content.")
288
+
289
+ class SummaryExtractor:
290
+ """
291
+ A tool for extracting and formatting summaries from YouTube video transcripts.
292
+
293
+ This class provides methods to process transcripts and generate concise summaries
294
+ using natural language processing techniques.
295
+ """
296
+
297
+ class Info(BaseModel):
298
+ """Pydantic model for representing a collection of summaries."""
299
+ summary: List[Summary]
300
+
301
+ @staticmethod
302
+ @tool(return_direct=False)
303
+ @log_errors
304
+ def get_youtube_video_summary(youtube_video_id: str) -> str:
305
+ """
306
+ Extracts and formats a summary from a YouTube video transcript.
307
+
308
+ Args:
309
+ youtube_video_id (str): The ID of the YouTube video.
310
+
311
+ Returns:
312
+ str: Formatted string of the summary extracted from the video.
313
+ """
314
+ try:
315
+ transcript = SummaryExtractor._get_youtube_video_transcript(youtube_video_id)
316
+ summary = SummaryExtractor._extract_summary(transcript)
317
+ return SummaryExtractor._format_summary(summary)
318
+ except Exception as e:
319
+ logger.error(f"Error in get_youtube_video_summary: {str(e)}")
320
+ return f"Error extracting summary: {str(e)}"
321
+
322
+ @staticmethod
323
+ @log_errors
324
+ def _get_youtube_video_transcript(youtube_video_id: str) -> str:
325
+ """
326
+ Fetches the transcript for a YouTube video.
327
+
328
+ Args:
329
+ youtube_video_id (str): The ID of the YouTube video.
330
+
331
+ Returns:
332
+ str: The full transcript of the video.
333
+
334
+ Raises:
335
+ Exception: If there's an error fetching the transcript.
336
+ """
337
+ try:
338
+ transcript_json = YouTubeTranscriptApi.get_transcript(youtube_video_id)
339
+ transcript_data = [entry['text'] for entry in transcript_json]
340
+ return " ".join(transcript_data)
341
+ except Exception as e:
342
+ logger.error(f"Error fetching transcript: {str(e)}")
343
+ raise
344
+
345
+ @staticmethod
346
+ @functools.lru_cache(maxsize=16)
347
+ def _extract_summary(transcript: str) -> List[Summary]:
348
+ """
349
+ Extracts a summary from a YouTube video transcript.
350
+
351
+ Args:
352
+ transcript (str): The full transcript of the video.
353
+
354
+ Returns:
355
+ Summary: A Summary object containing the extracted summary.
356
+ """
357
+ summary_extraction_function = [convert_to_openai_function(SummaryExtractor.Info)]
358
+
359
+ model = ChatOpenAI(temperature=0)
360
+ extraction_model = model.bind(functions=summary_extraction_function, function_call={"name": "Info"})
361
+
362
+ prompt = ChatPromptTemplate.from_messages([("human", "{input}")])
363
+
364
+ extraction_chain = prompt | extraction_model | JsonKeyOutputFunctionsParser(key_name="summary")
365
+
366
+ text_splitter = RecursiveCharacterTextSplitter(chunk_overlap=0, chunk_size=8192, separators=[f" {char}" for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"])
367
+
368
+ prep = RunnableLambda(lambda x: [{"input": doc} for doc in text_splitter.split_text(x)])
369
+
370
+ chain = prep | extraction_chain.map() | MainPointsExtractor._flatten
371
+
372
+ return chain.invoke(transcript)
373
+
374
+ @staticmethod
375
+ def _format_summary(summaries: List[Summary]) -> str:
376
+ """
377
+ Formats the list of summaries into a single string.
378
+ Args:
379
+ summaries (List[Summary]): List of Summary objects.
380
+ Returns:
381
+ str: A formatted string containing all summaries.
382
+ """
383
+ return "\n\n".join([s["summary"] for s in summaries])
384
+
385
+ class YouTubeAgent:
386
+ """
387
+ An agent for interacting with YouTube videos and processing user queries.
388
+
389
+ This class sets up the necessary components for an AI agent that can understand
390
+ and respond to user queries about YouTube videos.
391
+ """
392
+
393
+ def __init__(self):
394
+ """Initializes the YouTubeAgent with necessary tools and components."""
395
+ self.tools = [
396
+ YouTubeTranscriptTool.get_transcript_with_timestamps,
397
+ YouTubeTranscriptTool.get_transcript_without_timestamps,
398
+ MainPointsExtractor.get_youtube_video_main_points,
399
+ SummaryExtractor.get_youtube_video_summary
400
+ ]
401
+ self.sys_message = "You are a helpful assistant."
402
+ self.functions = [convert_to_openai_function(f) for f in self.tools]
403
+ self.model = ChatOpenAI(temperature=0).bind(functions=self.functions)
404
+ self.prompt = ChatPromptTemplate.from_messages([
405
+ ("system", self.sys_message),
406
+ MessagesPlaceholder(variable_name="history"),
407
+ ("user", "{input}"),
408
+ MessagesPlaceholder(variable_name="agent_scratchpad")
409
+ ])
410
+ self.agent_chain = RunnablePassthrough.assign(
411
+ agent_scratchpad= lambda x: format_to_openai_functions(x["intermediate_steps"])
412
+ ) | self.prompt | self.model | OpenAIFunctionsAgentOutputParser()
413
+ self.memory = ConversationBufferWindowMemory(k=3, return_messages=True, memory_key="history")
414
+ self.agent_executor = AgentExecutor(agent=self.agent_chain, tools=self.tools, memory=self.memory)
415
+
416
+ @log_errors
417
+ def invoke(self, input_text: str) -> str:
418
+ """
419
+ Processes a user input and returns the agent's response.
420
+
421
+ Args:
422
+ input_text (str): The user's input query.
423
+
424
+ Returns:
425
+ str: The agent's response to the user's query.
426
+ """
427
+ try:
428
+ result = self.agent_executor.invoke({"input": input_text})
429
+ return result['output']
430
+ except Exception as e:
431
+ logger.error(f"Error in YouTubeAgent.invoke: {str(e)}")
432
+ return f"An error occurred: {str(e)}"
433
+
434
+ # # Usage example
435
+ # if __name__ == "__main__":
436
+ # youtube_agent = YouTubeAgent()
437
+ # video_link = "https://www.youtube.com/watch?v=dZxbVGhpEkI"
438
+ # try:
439
+ # main_points = youtube_agent.invoke(f"Can you get summary of the following video {video_link}")
440
+ # except Exception as e:
441
+ # logger.error(f"An error occurred during processing: {str(e)}")
442
+ # print(f"An error occurred: {str(e)}")
443
+