shawhin commited on
Commit
b0b8612
Β·
1 Parent(s): bcd4ece
functions.py CHANGED
@@ -3,7 +3,7 @@ import re
3
  from agents import function_tool
4
 
5
  @function_tool
6
- def fetch_youtube_transcript(url: str) -> str:
7
  """
8
  Extract transcript with timestamps from a YouTube video URL and format it for LLM consumption
9
 
 
3
  from agents import function_tool
4
 
5
  @function_tool
6
+ def fetch_video_transcript(url: str) -> str:
7
  """
8
  Extract transcript with timestamps from a YouTube video URL and format it for LLM consumption
9
 
main.py CHANGED
@@ -1,32 +1,144 @@
1
- from agents import Agent, function_tool, Runner
2
- from openai.types.responses import ResponseTextDeltaEvent
3
  from dotenv import load_dotenv
 
 
4
  import asyncio
5
- import os
 
 
 
6
  from functions import fetch_video_transcript, fetch_intstructions
7
 
8
- # try to import OPENAI_API_KEY from .env file, if not found, take user input
9
- if not os.getenv("OPENAI_API_KEY"):
10
- OPENAI_API_KEY = input("Enter your OpenAI API key: ")
11
- with open(".env", "w") as f:
12
- f.write(f"OPENAI_API_KEY={OPENAI_API_KEY}")
13
- else:
14
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
15
-
16
- # load system instructions
17
- with open("prompts/system_instructions.md", "r") as f:
18
- system_instructions = f.read()
19
-
20
- # create agent
21
- agent = Agent(
22
- name="YouTube Agent",
23
- model="gpt-5",
24
- system_instructions=system_instructions,
25
- tools=[fetch_video_transcript, fetch_intstructions],
26
- )
27
-
28
- async def main():
29
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  if __name__ == "__main__":
32
- asyncio.run(main())
 
1
+ import os
 
2
  from dotenv import load_dotenv
3
+ load_dotenv()
4
+
5
  import asyncio
6
+ from agents import Agent, Runner
7
+ from openai.types.responses import ResponseTextDeltaEvent
8
+ import streamlit as st
9
+
10
  from functions import fetch_video_transcript, fetch_intstructions
11
 
12
+ async def run_streamlit_app():
13
+ st.set_page_config(
14
+ page_title="YouTube Agent",
15
+ page_icon="πŸ€–",
16
+ layout="wide"
17
+ )
18
+
19
+ st.title("YouTube Agent")
20
+ st.markdown("Ask me anything about YouTube videos!")
21
+
22
+ # Get API key from environment or user input
23
+ env_api_key = os.getenv("OPENAI_API_KEY")
24
+
25
+ # Sidebar for API key (only show input if no env key)
26
+ with st.sidebar:
27
+ st.header("Configuration")
28
+
29
+ if env_api_key:
30
+ st.success("βœ… API key loaded from .env file")
31
+ api_key = env_api_key
32
+ else:
33
+ api_key = st.text_input(
34
+ "OpenAI API Key",
35
+ type="password",
36
+ help="Enter your OpenAI API key to use the agent"
37
+ )
38
+
39
+ if st.button("Clear Chat"):
40
+ st.session_state.messages = []
41
+ st.session_state.input_items = []
42
+ st.rerun()
43
+
44
+ # Initialize session state
45
+ if "messages" not in st.session_state:
46
+ st.session_state.messages = []
47
+ if "input_items" not in st.session_state:
48
+ st.session_state.input_items = []
49
+ if "agent" not in st.session_state:
50
+ st.session_state.agent = None
51
+
52
+ # Create agent if API key is provided
53
+ if api_key and st.session_state.agent is None:
54
+ os.environ["OPENAI_API_KEY"] = api_key
55
+
56
+ # Load system instructions
57
+ try:
58
+ with open("prompts/system_instructions.md", "r") as f:
59
+ system_instructions = f.read()
60
+
61
+ st.session_state.agent = Agent(
62
+ name="YouTube Agent",
63
+ instructions=system_instructions,
64
+ tools=[fetch_video_transcript, fetch_intstructions],
65
+ )
66
+ except Exception as e:
67
+ st.error(f"Error initializing agent: {str(e)}")
68
+ return
69
+
70
+ # Display chat messages
71
+ for message in st.session_state.messages:
72
+ with st.chat_message(message["role"]):
73
+ st.markdown(message["content"])
74
+
75
+ # Chat input
76
+ if prompt := st.chat_input("Type your message here..."):
77
+ if not api_key:
78
+ st.error("Please enter your OpenAI API key in the sidebar.")
79
+ return
80
+
81
+ if st.session_state.agent is None:
82
+ st.error("Agent not initialized. Please check your API key.")
83
+ return
84
+
85
+ # Add user message to chat
86
+ st.session_state.messages.append({"role": "user", "content": prompt})
87
+ st.session_state.input_items.append({"content": prompt, "role": "user"})
88
+
89
+ with st.chat_message("user"):
90
+ st.markdown(prompt)
91
+
92
+ # Generate response
93
+ with st.chat_message("assistant"):
94
+ response_placeholder = st.empty()
95
+ full_response = ""
96
+
97
+ try:
98
+ # Run the agent
99
+ result = Runner.run_streamed(
100
+ st.session_state.agent,
101
+ input=st.session_state.input_items,
102
+ )
103
+
104
+ # Process streaming events with await
105
+ async for event in result.stream_events():
106
+ if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
107
+ full_response += event.data.delta
108
+ response_placeholder.markdown(full_response + "β–Œ")
109
+ elif event.type == "run_item_stream_event":
110
+ if event.item.type == "tool_call_item":
111
+ # Get tool name and show appropriate status message
112
+ tool_name = event.item.raw_item.name
113
+ if tool_name == "fetch_video_transcript":
114
+ status_msg = f"\n\n-- Fetching transcript..."
115
+ elif tool_name == "fetch_intstructions":
116
+ status_msg = f"\n\n-- Fetching instructions..."
117
+ else:
118
+ status_msg = f"\n\n-- Calling {tool_name}..."
119
+ response_placeholder.markdown(full_response + status_msg + "β–Œ")
120
+ elif event.item.type == "tool_call_output_item":
121
+ # Use generic handling for tool outputs
122
+ formatted_content = f"Tool output:\n{event.item.output}"
123
+ completion_msg = f"\n\n-- Tool completed."
124
+
125
+ # Add tool output as user role to input_items
126
+ st.session_state.input_items.append({
127
+ "content": formatted_content,
128
+ "role": "user"
129
+ })
130
+ response_placeholder.markdown(full_response + completion_msg + "β–Œ")
131
+
132
+ # Final response without cursor
133
+ response_placeholder.markdown(full_response)
134
+
135
+ # Add assistant response to session state
136
+ st.session_state.messages.append({"role": "assistant", "content": full_response})
137
+ st.session_state.input_items.append({"content": full_response, "role": "assistant"})
138
+
139
+ except Exception as e:
140
+ st.error(f"Error: {str(e)}")
141
+
142
 
143
  if __name__ == "__main__":
144
+ asyncio.run(run_streamlit_app())
prompts/system_instructions.md CHANGED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are a YouTube Video Agent. You help users with requests related to YouTube videos.
2
+
3
+ ## Core Rule
4
+ Always cite and link to the specific part(s) of the video used in your answer.
5
+
6
+ ## Tools
7
+
8
+ ### fetch_video_transcript
9
+ Use this tool whenever a user provides a YouTube URL. It retrieves the full transcript.
10
+
11
+ ### fetch_instructions
12
+ Use this tool to get **specialized instructions** for common user requests, including:
13
+
14
+ - Writing a blog post
15
+ - Writing a social media post
16
+ - Extracting video chapters
17
+
18
+ To fetch the correct instructions, pass one of the following **exact** prompts:
19
+ - write_blog_post
20
+ - write_social_post
21
+ - write_video_chapters
22
+
23
+ Important: Do **not** guess how to complete these tasks. Always fetch the instructions and follow them exactly.
prompts/write_blog_post.md CHANGED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## How to Write a Blog Post
2
+
3
+ ### Blog Structure
4
+
5
+ - Short engaging title (format with markdown `#`)
6
+ - Short engaging subtitle that compliments title (format with markdown `###`)
7
+ - 2–3 sentence hook that clearly states the reader benefit, no header.
8
+ - `---` (section break)
9
+ - **Introduction**: Three short paragraphs, no header.
10
+ - **Body**: Multiple sections with clear, punchy headers.
11
+ - Note: Each section in body should have 2 paragraphs (3 max when necessary).
12
+ - **Ending**: A final section titled *Conclusion*, *Takeaways*, or *What's Next?*
13
+
14
+ ### Writing Guidelines
15
+
16
+ - Most paragraphs should be 2 sentences (3 max when necessary).
17
+ - Use one of these story structures for posts, sections, and even paragraphs:
18
+ - *Status quo β†’ Problem β†’ Solution*
19
+ - *What? β†’ Why? β†’ How?*
20
+ - *What? β†’ So what? β†’ What now?*
21
+ - Use plain language that’s easy for most people to understand.
prompts/write_social_post.md CHANGED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## How to Write Social Media Posts
2
+
3
+ ### Post Structure
4
+
5
+ - **Hook**: Opening line that grabs attention and creates curiosity.
6
+ - **Value**: Core message that provides benefit, insight, or entertainment.
7
+ - **Call to Action**: Clear next step for the reader (optional but recommended).
8
+
9
+ ### Platform Guidelines
10
+
11
+ - **Twitter/X**: 280 characters max.
12
+ - **LinkedIn**: 1,300 characters max, professional tone, longer-form content welcome.
13
+ - **Instagram**: 2,200 characters max, use emojis and line breaks for readability.
14
+ - **Facebook**: 63,206 characters max, conversational tone, encourage engagement.
15
+
16
+ ### Writing Guidelines
17
+
18
+ - Start with a strong hook that makes people want to read more.
19
+ - Use short sentences and paragraphs for easy mobile reading.
20
+ - Do not include relevant hashtags
21
+ - Ask questions or use prompts to encourage engagement.
22
+ - Use emojis strategically to add personality and break up text.
23
+ - End with a clear call to action when appropriate.
24
+
25
+ ### Engagement Tips
26
+
27
+ - Use storytelling elements: *Problem β†’ Solution β†’ Result*
28
+ - Include numbers, statistics, or specific examples for credibility.
29
+ - Create urgency or FOMO when relevant.
30
+ - Tag relevant people or brands to increase reach.
31
+ - Use trending topics or current events when appropriate.
prompts/write_video_chapters.md CHANGED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## How to Write Video Chapters
2
+
3
+ ### Steps
4
+ 1. Watch or review the video to understand its content and flow.
5
+ 2. Identify natural breakpoints in the content (e.g., topic changes, new sections).
6
+ 3. Create a list of chapters with timestamps and optional links.
7
+
8
+ ### Example Output
9
+ {chapter 1 name} – [0:00](chapter-link)
10
+ {chapter 2 name} – [0:45](chapter-link)
11
+ {chapter 3 name} – [2:00](chapter-link)
12
+ {chapter 4 name} – [6:00](chapter-link)
13
+ ...
14
+
15
+ ### Guidelines
16
+ - Each chapter must be **at least 20 seconds** long.
17
+ - The first 1–3 chapters can be shorter due to introductions or quick transitions.
18
+ - Avoid creating too many chapters β€” aim for clarity over granularity.
19
+ - Chapter titles should be concise and descriptive of the content.