StanDataCamp commited on
Commit
84c92d2
Β·
1 Parent(s): 6a91b12

Publish Podcast Assistant app to Hugging Face Space.

Browse files

Add app source, vector index assets, and runtime dependencies with LFS tracking for large files.

.env.example ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # OpenAI API Key (required)
2
+ # Get yours at: https://platform.openai.com/api-keys
3
+ OPENAI_API_KEY=sk-your-api-key-here
.gitattributes CHANGED
@@ -19,6 +19,7 @@
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
  *.pickle filter=lfs diff=lfs merge=lfs -text
21
  *.pkl filter=lfs diff=lfs merge=lfs -text
 
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
@@ -28,6 +29,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
28
  *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
 
31
  *.wasm filter=lfs diff=lfs merge=lfs -text
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
 
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
  *.pickle filter=lfs diff=lfs merge=lfs -text
21
  *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.faiss filter=lfs diff=lfs merge=lfs -text
23
  *.pt filter=lfs diff=lfs merge=lfs -text
24
  *.pth filter=lfs diff=lfs merge=lfs -text
25
  *.rar filter=lfs diff=lfs merge=lfs -text
 
29
  *.tar filter=lfs diff=lfs merge=lfs -text
30
  *.tflite filter=lfs diff=lfs merge=lfs -text
31
  *.tgz filter=lfs diff=lfs merge=lfs -text
32
+ data/episodes_embedding_chunks.csv filter=lfs diff=lfs merge=lfs -text
33
  *.wasm filter=lfs diff=lfs merge=lfs -text
34
  *.xz filter=lfs diff=lfs merge=lfs -text
35
  *.zip filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment (keep .env.example for reference)
2
+ .env
3
+ .env.local
4
+ !.env.example
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ .Python
11
+ *.so
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ ENV/
17
+
18
+ # IDE
19
+ .idea/
20
+ .vscode/
21
+ *.code-workspace
22
+ .cursor/
23
+
24
+ # Gradio
25
+ .gradio/
26
+ flagged/
27
+
28
+ # OS
29
+ .DS_Store
30
+ Thumbs.db
31
+
32
+ # Large data files (keep in repo for reproducibility, but can exclude to reduce size)
33
+ # faiss_index.db/
34
+ # data/episodes_embedding_chunks.csv
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stan Konkin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,13 +1,183 @@
1
  ---
2
  title: Podcast Assistant
3
- emoji: πŸš€
4
- colorFrom: red
5
  colorTo: purple
6
  sdk: gradio
7
- sdk_version: 6.5.1
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Podcast Assistant
3
+ emoji: 🎧
4
+ colorFrom: blue
5
  colorTo: purple
6
  sdk: gradio
 
7
  app_file: app.py
8
  pinned: false
9
  license: mit
10
  ---
11
 
12
+ # Podcast Assistant
13
+
14
+ A RAG-powered assistant that surfaces wisdom from **100+ Lex Fridman podcast episodes** β€” returning verbatim quotes with timestamped video links.
15
+
16
+ ## Overview
17
+
18
+ Ask a question, get real quotes from guests like Elon Musk, Pavel Durov, and Andrew Huberman β€” with direct links to watch the original context.
19
+
20
+ **Example interaction:**
21
+
22
+ > **You:** What do tech founders think about work-life balance?
23
+ >
24
+ > **Assistant:** "I try to allocate as much time as possible for sleep..." β€” **Pavel Durov** (Telegram Founder)
25
+ > Watch: [Episode #482](https://www.youtube.com/watch?v=qjPH9njnaVU&t=973s)
26
+
27
+ ## Architecture
28
+
29
+ ```
30
+ User Question
31
+ β”‚
32
+ β–Ό
33
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
34
+ β”‚ LangChain Agent (GPT-4.1-mini) β”‚
35
+ β”‚ - Decides when to search β”‚
36
+ β”‚ - Formats response with quotes β”‚
37
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
38
+ β”‚
39
+ β–Ό
40
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
41
+ β”‚ search_podcasts Tool β”‚
42
+ β”‚ - OpenAI embeddings β”‚
43
+ β”‚ - FAISS similarity search β”‚
44
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
45
+ β”‚
46
+ β–Ό
47
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
48
+ β”‚ Vector Store (14K chunks) β”‚
49
+ β”‚ - Transcript segments β”‚
50
+ β”‚ - Episode metadata & timestamps β”‚
51
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
52
+ ```
53
+
54
+ **How it works:**
55
+ 1. User asks a question β†’ LangChain agent receives it with tool access
56
+ 2. Agent calls `search_podcasts` β†’ FAISS finds top 7 relevant transcript chunks
57
+ 3. Agent formats response β†’ Verbatim quotes with speaker attribution and episode links
58
+ 4. Follow-up questions β†’ Reuse existing context (no redundant searches)
59
+
60
+ ## Quick Start
61
+
62
+ ### Requirements
63
+
64
+ - Python 3.12
65
+ - [uv](https://docs.astral.sh/uv/) package manager
66
+ - OpenAI API key
67
+
68
+ ### Setup
69
+
70
+ ```bash
71
+ # Install uv (if not already installed)
72
+ curl -LsSf https://astral.sh/uv/install.sh | sh
73
+
74
+ # Clone and enter project
75
+ git clone <repo-url>
76
+ cd podcast-assistant
77
+
78
+ # Install dependencies
79
+ uv sync
80
+
81
+ # Configure environment
82
+ cp .env.example .env
83
+ # Add your OPENAI_API_KEY to .env
84
+
85
+ # Run the app
86
+ uv run app.py
87
+ ```
88
+
89
+ Open http://localhost:7860
90
+
91
+ ### Why uv?
92
+
93
+ We use [uv](https://docs.astral.sh/uv/) for dependency management β€” it's fast, handles Python versions, and creates reproducible environments via `uv.lock`. No Docker overhead needed.
94
+
95
+ ## Project Structure
96
+
97
+ ```
98
+ podcast-assistant/
99
+ β”œβ”€β”€ app.py # Gradio web UI
100
+ β”œβ”€β”€ assistant.py # LangChain agent with tool calling
101
+ β”œβ”€β”€ search_podcasts.py # FAISS search tool
102
+ β”œβ”€β”€ prompt.md # System prompt
103
+ β”œβ”€β”€ styles.css # UI styling
104
+ β”œβ”€β”€ pyproject.toml # Dependencies (Python 3.12)
105
+ β”œβ”€β”€ uv.lock # Locked versions for reproducibility
106
+ β”œβ”€β”€ faiss_index.db/ # Pre-built vector index
107
+ └── data/
108
+ β”œβ”€β”€ README.md # Data pipeline docs
109
+ β”œβ”€β”€ episodes_website.json
110
+ └── episodes_embedding_chunks.csv
111
+ ```
112
+
113
+ ## Key Dependencies
114
+
115
+ | Package | Version | Purpose |
116
+ |---------|---------|---------|
117
+ | langchain | 0.3+ | Agent framework with tool calling |
118
+ | langchain-openai | 0.3+ | OpenAI chat models and embeddings |
119
+ | faiss-cpu | 1.9+ | Vector similarity search |
120
+ | gradio | 4.0+ | Web interface |
121
+
122
+ Full versions locked in `uv.lock`.
123
+
124
+ ## Model Selection
125
+
126
+ We evaluated 4 OpenAI models on a test dataset for quote accuracy, format compliance, latency, and cost.
127
+
128
+ | Model | Quote Accuracy | Follows Format | Latency | Cost (3 turns) |
129
+ |-------|---------------|----------------|---------|----------------|
130
+ | gpt-4.1-nano | Paraphrases | Yes | ~12s | $0.001 |
131
+ | gpt-4o-mini | Good | No (adds headers) | ~26s | $0.002 |
132
+ | **gpt-4.1-mini** | Good | Yes | ~35s | $0.006 |
133
+ | gpt-5-mini | Excellent | Yes | ~124s | $0.020 |
134
+
135
+ **Selected: GPT-4.1-mini** β€” Best balance of instruction-following, latency, and cost. The 4.1 architecture handles nuanced prompts better than 4o-mini without the verbosity of 5-series models.
136
+
137
+ **Cost per conversation:** ~$0.006 (3 messages)
138
+
139
+ ## Feedback Collection
140
+
141
+ We collect feedback to improve the assistant β€” but only when you explicitly submit it. No data is saved during conversation, no user identifiers or tracking.
142
+
143
+ When you click "Submit Feedback," we save:
144
+
145
+ ```json
146
+ {
147
+ "timestamp": "2026-02-07T10:30:00",
148
+ "turns": [
149
+ {"role": "user", "content": "What do founders think about..."},
150
+ {"role": "assistant", "content": "Tech founders often..."}
151
+ ],
152
+ "feedback": {
153
+ "helpful": true,
154
+ "text": "Great answer!"
155
+ }
156
+ }
157
+ ```
158
+
159
+ This helps us understand what's working and where the assistant can improve.
160
+
161
+ ## Testing
162
+
163
+ ```bash
164
+ uv run python test.py
165
+ ```
166
+
167
+ Runs a multi-turn conversation with token usage, latency, and cost metrics.
168
+
169
+ ## License
170
+
171
+ This project is licensed under the **MIT License** β€” you're free to use, modify, and distribute it for personal or commercial purposes. See the [LICENSE](LICENSE) file for details.
172
+
173
+ ## Acknowledgments
174
+
175
+ This project wouldn't exist without:
176
+
177
+ - **[Lex Fridman Podcast](https://lexfridman.com/podcast/)** β€” For creating hundreds of hours of deeply insightful conversations with remarkable people.
178
+
179
+ - **[DataCamp](https://www.datacamp.com/)** β€” For world-class data and AI education that helped shape the skills behind this project.
180
+
181
+ - **[LangChain](https://langchain.com/)** β€” For building the agent framework that makes tool calling and conversation management straightforward.
182
+
183
+ - **[Gradio](https://gradio.app/)** β€” For making it easy to create beautiful ML interfaces without frontend complexity.
app.py CHANGED
@@ -1,7 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
 
 
1
+ """
2
+ Gradio Chat UI for Podcast Assistant.
3
+
4
+ A modern, minimalistic interface for exploring Lex Fridman podcast insights.
5
+ """
6
+ import os
7
+ import json
8
+ import uuid
9
+ from datetime import datetime
10
+ from typing import Generator
11
+
12
  import gradio as gr
13
+ from dotenv import load_dotenv
14
+
15
+ from assistant import PodcastAssistant
16
+
17
+ # Load environment variables from local .env
18
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
19
+ load_dotenv(dotenv_path=os.path.join(SCRIPT_DIR, '.env'))
20
+
21
+ # Configuration
22
+ MAX_MESSAGES = 3
23
+ FEEDBACK_FILE = os.path.join(SCRIPT_DIR, "feedback.json")
24
+
25
+ # Load custom CSS from external file
26
+ with open(os.path.join(SCRIPT_DIR, "styles.css"), "r") as f:
27
+ CUSTOM_CSS = f.read()
28
+
29
+ # Example questions for quick start
30
+ EXAMPLES = [
31
+ "What habits and routines do high performers follow?",
32
+ "What is the future of AI and AGI according to experts?",
33
+ "How should young people approach their career and education?"
34
+ ]
35
+
36
+
37
+ class FeedbackLogger:
38
+ """
39
+ Saves conversation data only when user submits feedback.
40
+
41
+ Privacy-focused: No user-specific data collected.
42
+ Only stores: timestamp, conversation turns, and feedback.
43
+ """
44
+
45
+ def __init__(self, filepath: str):
46
+ self.filepath = filepath
47
+ self._ensure_file_exists()
48
+
49
+ def _ensure_file_exists(self):
50
+ """Create the feedback file if it doesn't exist."""
51
+ if not os.path.exists(self.filepath):
52
+ with open(self.filepath, 'w') as f:
53
+ json.dump([], f)
54
+
55
+ def _load_feedback(self) -> list:
56
+ """Load all feedback entries from file."""
57
+ try:
58
+ with open(self.filepath, 'r') as f:
59
+ return json.load(f)
60
+ except (json.JSONDecodeError, FileNotFoundError):
61
+ return []
62
+
63
+ def _save_feedback(self, entries: list):
64
+ """Save all feedback entries to file."""
65
+ with open(self.filepath, 'w') as f:
66
+ json.dump(entries, f, indent=2)
67
+
68
+ def save_with_feedback(self, turns: list, is_helpful: bool, feedback_text: str = ""):
69
+ """
70
+ Save conversation and feedback together.
71
+
72
+ Only called when user explicitly submits feedback.
73
+ No data is stored until feedback is submitted.
74
+ """
75
+ entries = self._load_feedback()
76
+
77
+ # Create minimal, privacy-focused entry
78
+ entry = {
79
+ "timestamp": datetime.now().isoformat(),
80
+ "turns": [{"role": msg["role"], "content": msg["content"]} for msg in turns],
81
+ "feedback": {
82
+ "helpful": is_helpful,
83
+ "text": feedback_text if feedback_text else ""
84
+ }
85
+ }
86
+
87
+ entries.append(entry)
88
+ self._save_feedback(entries)
89
+
90
+
91
+ # Initialize feedback logger
92
+ feedback_logger = FeedbackLogger(FEEDBACK_FILE)
93
+
94
+
95
+ def create_app():
96
+ """Create and configure the Gradio application."""
97
+
98
+ with gr.Blocks(title="Podcast Assistant") as app:
99
+ # State variables
100
+ conversation_id = gr.State(lambda: str(uuid.uuid4()))
101
+ message_count = gr.State(0)
102
+ is_frozen = gr.State(False)
103
+ pending_feedback = gr.State(None) # Stores True (helpful) or False (unhelpful)
104
+ assistant_state = gr.State(None) # Will hold the PodcastAssistant instance
105
+
106
+ # Header - centered
107
+ gr.Markdown("""
108
+ <div style="text-align: center;">
109
+ <h1>🎧 Podcast Assistant</h1>
110
+ <p>Ask questions about Lex Fridman podcast episodes. Get answers with quotes and links.</p>
111
+ </div>
112
+ """, elem_classes=["header-section"])
113
+
114
+ # Chat interface
115
+ chatbot = gr.Chatbot(
116
+ value=[],
117
+ height=270,
118
+ show_label=False,
119
+ elem_id="chatbot"
120
+ )
121
+
122
+ # Loading indicator (hidden by default) - animated
123
+ loading_msg = gr.Markdown(
124
+ value="πŸ” Thinking and searching...",
125
+ visible=False,
126
+ elem_classes=["loading-msg"]
127
+ )
128
+
129
+ # Message counter - minimal
130
+ counter_display = gr.Markdown(
131
+ value="πŸ’¬ 3 messages remaining",
132
+ elem_classes=["message-counter"]
133
+ )
134
+
135
+ # Limit reached message (hidden by default)
136
+ limit_msg = gr.Markdown(
137
+ value="✨ You've used all 3 messages. Start a new conversation to continue exploring!",
138
+ visible=False,
139
+ elem_classes=["limit-msg"]
140
+ )
141
+
142
+ # Input area with buttons stacked on right
143
+ with gr.Row(elem_classes=["input-row"]):
144
+ msg_input = gr.Textbox(
145
+ placeholder="Ask about habits, AI, science, philosophy...",
146
+ show_label=False,
147
+ scale=8,
148
+ container=False
149
+ )
150
+ with gr.Column(scale=1, elem_classes=["buttons-col"]):
151
+ submit_btn = gr.Button("Send ➀", variant="primary")
152
+ with gr.Column(elem_classes=["new-btn"]):
153
+ new_conv_btn = gr.Button("New ↻")
154
+
155
+ # Frozen state message (hidden by default)
156
+ frozen_msg = gr.Markdown(
157
+ visible=False,
158
+ elem_classes=["frozen-message"]
159
+ )
160
+
161
+ # Example buttons - horizontal row
162
+ with gr.Row(elem_classes=["examples-section"]):
163
+ gr.Markdown("πŸ’‘ Try examples:")
164
+ example_btns = [
165
+ gr.Button(ex, size="sm", elem_classes=["example-btn"])
166
+ for ex in EXAMPLES
167
+ ]
168
+
169
+ # Feedback section - inline, hidden until response complete
170
+ with gr.Row(visible=False, elem_classes=["feedback-section"]) as feedback_group:
171
+ gr.Markdown("πŸ€” Was this helpful?")
172
+ helpful_btn = gr.Button("πŸ‘ Yes", size="sm")
173
+ unhelpful_btn = gr.Button("πŸ‘Ž No", size="sm")
174
+
175
+ # Feedback text input (hidden until button clicked)
176
+ with gr.Group(visible=False, elem_classes=["feedback-input"]) as feedback_input_group:
177
+ feedback_text = gr.Textbox(
178
+ placeholder="Any additional thoughts? (optional)",
179
+ show_label=False,
180
+ lines=2
181
+ )
182
+ submit_feedback_btn = gr.Button("Submit πŸ“¨", variant="primary")
183
+
184
+ # Footer
185
+ gr.Markdown(
186
+ "AI Podcast Assistant v2.0 β€’ Built with Langchain and Gradio β€’ Made with ❀️ for learning",
187
+ elem_classes=["footer"]
188
+ )
189
+
190
+ # ============ Event Handlers ============
191
+
192
+ def respond(
193
+ message: str,
194
+ history: list,
195
+ conv_id: str,
196
+ msg_count: int,
197
+ frozen: bool,
198
+ assistant: PodcastAssistant
199
+ ) -> Generator:
200
+ """Handle user message and stream response."""
201
+ import time
202
+
203
+ # Output order: chatbot, message_count, is_frozen, assistant_state,
204
+ # counter_display, loading_msg, feedback_group, limit_msg,
205
+ # msg_input, submit_btn
206
+
207
+ if frozen or not message.strip():
208
+ yield (
209
+ history, msg_count, frozen, assistant,
210
+ gr.update(), gr.update(), gr.update(), gr.update(),
211
+ gr.update(), gr.update()
212
+ )
213
+ return
214
+
215
+ # Initialize assistant if needed
216
+ if assistant is None:
217
+ assistant = PodcastAssistant()
218
+
219
+ # Add user message to history
220
+ history = history + [{"role": "user", "content": message}]
221
+ new_count = msg_count + 1
222
+
223
+ # Update counter display
224
+ remaining = MAX_MESSAGES - new_count
225
+ counter_text = f"πŸ’¬ {remaining} message{'s' if remaining != 1 else ''} remaining"
226
+
227
+ # Yield initial state - show loading, disable inputs
228
+ yield (
229
+ history,
230
+ new_count,
231
+ frozen,
232
+ assistant,
233
+ gr.update(value=counter_text),
234
+ gr.update(visible=True), # Show loading
235
+ gr.update(visible=False), # Hide feedback
236
+ gr.update(visible=False), # Hide limit msg
237
+ gr.update(value="", interactive=False), # Clear and disable input
238
+ gr.update(interactive=False) # Disable send button
239
+ )
240
+
241
+ try:
242
+ # Add empty assistant message that we'll stream into
243
+ history = history + [{"role": "assistant", "content": ""}]
244
+
245
+ # Stream the response
246
+ response_text = ""
247
+ chunk_count = 0
248
+ for chunk in assistant.stream_chat(message):
249
+ response_text += chunk
250
+ history[-1]["content"] = response_text
251
+ chunk_count += 1
252
+
253
+ # Add delay for smoother streaming
254
+ if chunk_count % 2 == 0:
255
+ time.sleep(0.03)
256
+
257
+ yield (
258
+ history,
259
+ new_count,
260
+ frozen,
261
+ assistant,
262
+ gr.update(value=counter_text),
263
+ gr.update(visible=False), # Hide loading once streaming
264
+ gr.update(visible=False),
265
+ gr.update(visible=False),
266
+ gr.update(interactive=False), # Keep input disabled
267
+ gr.update(interactive=False) # Keep button disabled
268
+ )
269
+
270
+ # Response complete - show feedback, re-enable inputs
271
+ # Note: Conversation is NOT saved here - only saved when feedback is submitted
272
+ if new_count >= MAX_MESSAGES:
273
+ # Limit reached - keep inputs disabled
274
+ frozen = True
275
+ yield (
276
+ history,
277
+ new_count,
278
+ frozen,
279
+ assistant,
280
+ gr.update(visible=False), # Hide counter
281
+ gr.update(visible=False), # Hide loading
282
+ gr.update(visible=True), # Show feedback
283
+ gr.update(visible=True), # Show limit msg
284
+ gr.update(interactive=False), # Keep input disabled
285
+ gr.update(interactive=False) # Keep button disabled
286
+ )
287
+ else:
288
+ # Still have messages remaining - re-enable inputs
289
+ yield (
290
+ history,
291
+ new_count,
292
+ frozen,
293
+ assistant,
294
+ gr.update(value=counter_text),
295
+ gr.update(visible=False), # Hide loading
296
+ gr.update(visible=True), # Show feedback
297
+ gr.update(visible=False),
298
+ gr.update(interactive=True), # Re-enable input
299
+ gr.update(interactive=True) # Re-enable button
300
+ )
301
+
302
+ except Exception as e:
303
+ # Handle errors gracefully - re-enable inputs
304
+ error_msg = "πŸ˜” Sorry, something went wrong on our end. We'll make sure to get better! Please try again."
305
+ if history and history[-1]["role"] == "assistant":
306
+ history[-1]["content"] = error_msg
307
+ else:
308
+ history = history + [{"role": "assistant", "content": error_msg}]
309
+ yield (
310
+ history,
311
+ new_count,
312
+ frozen,
313
+ assistant,
314
+ gr.update(value=counter_text),
315
+ gr.update(visible=False),
316
+ gr.update(visible=False),
317
+ gr.update(visible=False),
318
+ gr.update(interactive=True), # Re-enable input on error
319
+ gr.update(interactive=True) # Re-enable button on error
320
+ )
321
+
322
+
323
+ def show_feedback_input(is_helpful: bool, conv_id: str):
324
+ """Show feedback text input when helpful/unhelpful clicked."""
325
+ return (
326
+ is_helpful,
327
+ gr.update(visible=True), # Show feedback input
328
+ gr.update(visible=False), # Hide feedback buttons
329
+ )
330
+
331
+ def submit_feedback(
332
+ is_helpful: bool,
333
+ feedback_txt: str,
334
+ conv_id: str,
335
+ history: list
336
+ ):
337
+ """
338
+ Submit feedback and save conversation.
339
+
340
+ This is the ONLY place where data is saved.
341
+ Conversation + feedback saved together when user submits.
342
+ """
343
+ if is_helpful is not None and history:
344
+ feedback_logger.save_with_feedback(history, is_helpful, feedback_txt or "")
345
+
346
+ return (
347
+ True, # Freeze conversation
348
+ gr.update(visible=False), # Hide feedback input
349
+ gr.update(interactive=False), # Disable input
350
+ gr.update(value="βœ… Appreciate your feedback! Click \"New\" to start fresh."),
351
+ gr.update(value="") # Clear feedback text box for next time
352
+ )
353
+
354
+ def start_new_conversation():
355
+ """Reset everything for a new conversation."""
356
+ new_id = str(uuid.uuid4())
357
+ new_assistant = PodcastAssistant()
358
+
359
+ return (
360
+ [], # Clear chat
361
+ new_id, # New conversation ID
362
+ 0, # Reset message count
363
+ False, # Unfreeze
364
+ new_assistant, # New assistant
365
+ None, # Clear pending feedback
366
+ gr.update(value="πŸ’¬ 3 messages remaining", visible=True), # Reset counter
367
+ gr.update(visible=False), # Hide feedback group
368
+ gr.update(visible=False), # Hide feedback input
369
+ gr.update(value="", interactive=True), # Clear and enable input
370
+ gr.update(visible=False), # Hide frozen message
371
+ gr.update(visible=False), # Hide limit msg
372
+ gr.update(visible=False), # Hide loading
373
+ gr.update(interactive=True), # Enable send button
374
+ )
375
+
376
+ # Common outputs for respond function
377
+ respond_outputs = [
378
+ chatbot, message_count, is_frozen, assistant_state,
379
+ counter_display, loading_msg, feedback_group, limit_msg,
380
+ msg_input, submit_btn
381
+ ]
382
+
383
+ # Wire up events
384
+ msg_input.submit(
385
+ fn=respond,
386
+ inputs=[msg_input, chatbot, conversation_id, message_count, is_frozen, assistant_state],
387
+ outputs=respond_outputs
388
+ )
389
+
390
+ submit_btn.click(
391
+ fn=respond,
392
+ inputs=[msg_input, chatbot, conversation_id, message_count, is_frozen, assistant_state],
393
+ outputs=respond_outputs
394
+ )
395
+
396
+ # Example buttons - send message directly without showing in textbox
397
+ for btn, example in zip(example_btns, EXAMPLES):
398
+ # Create a closure to capture the example text
399
+ def make_example_handler(ex_text):
400
+ def handler(history, conv_id, msg_count, frozen, assistant):
401
+ yield from respond(ex_text, history, conv_id, msg_count, frozen, assistant)
402
+ return handler
403
+
404
+ btn.click(
405
+ fn=make_example_handler(example),
406
+ inputs=[chatbot, conversation_id, message_count, is_frozen, assistant_state],
407
+ outputs=respond_outputs
408
+ )
409
+
410
+ # Feedback buttons
411
+ helpful_btn.click(
412
+ fn=lambda conv_id: show_feedback_input(True, conv_id),
413
+ inputs=[conversation_id],
414
+ outputs=[pending_feedback, feedback_input_group, feedback_group]
415
+ )
416
+
417
+ unhelpful_btn.click(
418
+ fn=lambda conv_id: show_feedback_input(False, conv_id),
419
+ inputs=[conversation_id],
420
+ outputs=[pending_feedback, feedback_input_group, feedback_group]
421
+ )
422
+
423
+ submit_feedback_btn.click(
424
+ fn=submit_feedback,
425
+ inputs=[pending_feedback, feedback_text, conversation_id, chatbot],
426
+ outputs=[is_frozen, feedback_input_group, msg_input, frozen_msg, feedback_text]
427
+ ).then(
428
+ fn=lambda: gr.update(visible=True),
429
+ outputs=[frozen_msg]
430
+ )
431
+
432
+ # New conversation button
433
+ new_conv_btn.click(
434
+ fn=start_new_conversation,
435
+ outputs=[
436
+ chatbot, conversation_id, message_count, is_frozen, assistant_state,
437
+ pending_feedback, counter_display, feedback_group, feedback_input_group,
438
+ msg_input, frozen_msg, limit_msg, loading_msg, submit_btn
439
+ ]
440
+ )
441
+
442
+ return app
443
 
 
 
444
 
445
+ if __name__ == "__main__":
446
+ app = create_app()
447
+ # server_name="0.0.0.0" allows access from other devices on the same network
448
+ app.launch(css=CUSTOM_CSS, footer_links=[], server_name="0.0.0.0")
assistant.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Podcast Assistant - RAG-powered Q&A over Lex Fridman podcast transcripts.
3
+
4
+ This module provides the PodcastAssistant class for use with Gradio or other interfaces.
5
+ """
6
+ import os
7
+ from datetime import datetime
8
+ from dotenv import load_dotenv
9
+ from langchain_core.messages import HumanMessage, SystemMessage
10
+ from langchain.chat_models import init_chat_model
11
+
12
+ from search_podcasts import search_podcasts
13
+
14
+ # Load environment variables from local .env
15
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
16
+ load_dotenv(dotenv_path=os.path.join(SCRIPT_DIR, '.env'))
17
+
18
+
19
+ class PodcastAssistant:
20
+ """RAG-powered assistant for querying podcast transcripts."""
21
+
22
+ def __init__(self, model: str = "gpt-4.1-mini"):
23
+ """Initialize the podcast assistant.
24
+
25
+ Args:
26
+ model: The chat model to use (default: gpt-4.1-mini)
27
+ """
28
+ # Initialize LLM
29
+ self.llm = init_chat_model(model=model)
30
+
31
+ # Set up tools
32
+ self.tools = [search_podcasts]
33
+ self.tools_dict = {t.name: t for t in self.tools}
34
+
35
+ # Bind tools to LLM
36
+ self.llm_with_tools = self.llm.bind_tools(self.tools)
37
+
38
+ # Load system prompt
39
+ with open(os.path.join(SCRIPT_DIR, "prompt.md"), "r") as f:
40
+ prompt_template = f.read()
41
+
42
+ today = datetime.now().strftime("%A, %B %d, %Y")
43
+ self.system_prompt = prompt_template.replace("{today_date}", today)
44
+
45
+ # Initialize conversation history
46
+ self.messages = [SystemMessage(content=self.system_prompt)]
47
+
48
+ def chat(self, message: str) -> str:
49
+ """Process a user message and return the assistant's response.
50
+
51
+ Args:
52
+ message: User's question or request.
53
+
54
+ Returns:
55
+ Assistant's response with insights and video links.
56
+ """
57
+ # Add user message
58
+ self.messages.append(HumanMessage(content=message))
59
+
60
+ # Agentic loop for tool calls
61
+ while True:
62
+ response = self.llm_with_tools.invoke(self.messages)
63
+ self.messages.append(response)
64
+
65
+ # Check if we're done (no tool calls)
66
+ if not response.tool_calls:
67
+ break
68
+
69
+ # Process tool calls
70
+ for tool_call in response.tool_calls:
71
+ tool_name = tool_call["name"]
72
+
73
+ if tool_name in self.tools_dict:
74
+ tool = self.tools_dict[tool_name]
75
+ tool_result = tool.invoke(tool_call)
76
+ self.messages.append(tool_result)
77
+
78
+ return response.content
79
+
80
+ def stream_chat(self, message: str):
81
+ """Process a user message and stream the assistant's response.
82
+
83
+ Args:
84
+ message: User's question or request.
85
+
86
+ Yields:
87
+ Chunks of the assistant's response as they arrive.
88
+ """
89
+ from langchain_core.messages import AIMessage
90
+
91
+ # Add user message
92
+ self.messages.append(HumanMessage(content=message))
93
+
94
+ # Agentic loop for tool calls (non-streaming)
95
+ while True:
96
+ response = self.llm_with_tools.invoke(self.messages)
97
+
98
+ # Check if we have tool calls to process
99
+ if not response.tool_calls:
100
+ # No tool calls - now stream the final response
101
+ # Remove the non-streamed response, we'll stream it instead
102
+ break
103
+
104
+ # Process tool calls
105
+ self.messages.append(response)
106
+ for tool_call in response.tool_calls:
107
+ tool_name = tool_call["name"]
108
+
109
+ if tool_name in self.tools_dict:
110
+ tool = self.tools_dict[tool_name]
111
+ tool_result = tool.invoke(tool_call)
112
+ self.messages.append(tool_result)
113
+
114
+ # Stream the final response
115
+ full_response = ""
116
+ for chunk in self.llm_with_tools.stream(self.messages):
117
+ if chunk.content:
118
+ full_response += chunk.content
119
+ yield chunk.content
120
+
121
+ # Add the complete response to history
122
+ self.messages.append(AIMessage(content=full_response))
123
+
124
+ def reset(self):
125
+ """Reset conversation history."""
126
+ self.messages = [SystemMessage(content=self.system_prompt)]
data/README.md ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data Pipeline
2
+
3
+ Source data and documentation for the vector index.
4
+
5
+ ## Files
6
+
7
+ | File | Description |
8
+ |------|-------------|
9
+ | `episodes_website.json` | Episode metadata (106 episodes): titles, guests, YouTube URLs |
10
+ | `episodes_embedding_chunks.csv` | 14,514 transcript chunks with timestamps |
11
+
12
+ ## What Gets Embedded
13
+
14
+ Each chunk is a structured text block that includes episode context and transcript content:
15
+
16
+ ```
17
+ Episode: 455
18
+ Chapter: Introduction
19
+
20
+ BEFORE (context):
21
+ [~50 words from previous segment]
22
+
23
+ MAIN (target):
24
+ Adam Frank: If we don't ask how long they last, but instead ask
25
+ what's the probability that there have been any civilizations at all...
26
+ [~300 words - the core content we're searching]
27
+
28
+ AFTER (context):
29
+ [~50 words from next segment]
30
+ ```
31
+
32
+ **Key design choices:**
33
+ - Episode number and chapter title are embedded with the text β€” this helps retrieval when users ask about specific episodes or topics
34
+ - The MAIN section contains the actual transcript with speaker names inline
35
+ - BEFORE/AFTER provide context so the LLM understands the quote in its original setting
36
+ - Overlapping windows ensure content isn't awkwardly split at chunk boundaries
37
+
38
+ ## Vector Store
39
+
40
+ **Embeddings:** OpenAI `text-embedding-3-small` (1536 dimensions)
41
+
42
+ **Index:** FAISS flat index (`IndexFlatL2`)
43
+ - Simple brute-force similarity search
44
+ - Works well for our scale (~14K vectors)
45
+ - No approximation β€” returns exact nearest neighbors
46
+ - Trade-off: Larger indices (100K+) would benefit from IVF or HNSW for speed
47
+
48
+ **Storage:** `../faiss_index.db/`
49
+ - `index.faiss` β€” vector data
50
+ - `index.pkl` β€” metadata (episode, timestamp, URL, chapter)
51
+
52
+ ## Adding New Episodes
53
+
54
+ To add new episodes or rebuild the index:
55
+
56
+ 1. Scrape new episode metadata and transcripts
57
+ 2. Process transcripts into chunks using the same BEFORE/MAIN/AFTER format
58
+ 3. Append to `episodes_embedding_chunks.csv`
59
+ 4. Rebuild the FAISS index from the full CSV
60
+ 5. Replace `faiss_index.db/` with the new index
61
+
62
+ The flat index must be fully rebuilt β€” it doesn't support incremental additions. For a production system with frequent updates, consider using a vector database like Pinecone or Weaviate that supports upserts.
63
+
64
+ ## Privacy
65
+
66
+ No user data is stored here. All content is from publicly available podcast episodes.
data/episodes_embedding_chunks.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1165e6cdd3e3b1ec29519de6646148259930158e8e877d8f6465f7d826b16410
3
+ size 27106060
data/episodes_website.json ADDED
@@ -0,0 +1,1168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "episode_number": 489,
4
+ "guest_name": "Paul Rosolie",
5
+ "profile": "Naturalist & Explorer",
6
+ "title": "Paul Rosolie: Uncontacted Tribes in the Amazon Jungle",
7
+ "youtube_url": "https://www.youtube.com/watch?v=Z-FRe5AKmCU",
8
+ "youtube_video_id": "Z-FRe5AKmCU",
9
+ "episode_slug": "paul-rosolie-3",
10
+ "transcript_slug": "paul-rosolie-3-transcript",
11
+ "has_transcript": true
12
+ },
13
+ {
14
+ "episode_number": 488,
15
+ "guest_name": "Joel David Hamkins",
16
+ "profile": "Mathematician & Philosopher",
17
+ "title": "Infinity, Paradoxes, GΓΆdel Incompleteness & the Mathematical Multiverse",
18
+ "youtube_url": "https://www.youtube.com/watch?v=14OPT6CcsH4",
19
+ "youtube_video_id": "14OPT6CcsH4",
20
+ "episode_slug": "joel-david-hamkins",
21
+ "transcript_slug": "joel-david-hamkins-transcript",
22
+ "has_transcript": true
23
+ },
24
+ {
25
+ "episode_number": 487,
26
+ "guest_name": "Irving Finkel",
27
+ "profile": "Assyriologist and Cuneiform Scholar",
28
+ "title": "Deciphering Secrets of Ancient Civilizations, Noah's Ark, and Flood Myths",
29
+ "youtube_url": "https://www.youtube.com/watch?v=_bBRVNkAfkQ",
30
+ "youtube_video_id": "_bBRVNkAfkQ",
31
+ "episode_slug": "irving-finkel",
32
+ "transcript_slug": "irving-finkel-transcript",
33
+ "has_transcript": true
34
+ },
35
+ {
36
+ "episode_number": 486,
37
+ "guest_name": "Michael Levin",
38
+ "profile": "Biologist",
39
+ "title": "Michael Levin: Hidden Reality of Alien Intelligence & Biological Life",
40
+ "youtube_url": "https://www.youtube.com/watch?v=Qp0rCU49lMs",
41
+ "youtube_video_id": "Qp0rCU49lMs",
42
+ "episode_slug": "michael-levin-2",
43
+ "transcript_slug": "michael-levin-2-transcript",
44
+ "has_transcript": true
45
+ },
46
+ {
47
+ "episode_number": 485,
48
+ "guest_name": "David Kirtley",
49
+ "profile": "Helion Energy CEO",
50
+ "title": "David Kirtley: Nuclear Fusion, Plasma Physics, and the Future of Energy",
51
+ "youtube_url": "https://www.youtube.com/watch?v=m_CFCyc2Shs",
52
+ "youtube_video_id": "m_CFCyc2Shs",
53
+ "episode_slug": "david-kirtley",
54
+ "transcript_slug": "david-kirtley-transcript",
55
+ "has_transcript": true
56
+ },
57
+ {
58
+ "episode_number": 484,
59
+ "guest_name": "Dan Houser",
60
+ "profile": "Rockstar Games Co-founder",
61
+ "title": "Dan Houser: GTA, Red Dead Redemption, Rockstar, Absurd & Future of Gaming",
62
+ "youtube_url": "https://www.youtube.com/watch?v=o3gbXDjNWyI",
63
+ "youtube_video_id": "o3gbXDjNWyI",
64
+ "episode_slug": "dan-houser",
65
+ "transcript_slug": "dan-houser-transcript",
66
+ "has_transcript": true
67
+ },
68
+ {
69
+ "episode_number": 483,
70
+ "guest_name": "Julia Shaw",
71
+ "profile": "Criminal Psychologist & Author",
72
+ "title": "Julia Shaw: Criminal Psychology of Murder, Serial Killers, Memory & Sex",
73
+ "youtube_url": "https://www.youtube.com/watch?v=7OLVwZeMCfY",
74
+ "youtube_video_id": "7OLVwZeMCfY",
75
+ "episode_slug": "julia-shaw",
76
+ "transcript_slug": "julia-shaw-transcript",
77
+ "has_transcript": true
78
+ },
79
+ {
80
+ "episode_number": 482,
81
+ "guest_name": "Pavel Durov",
82
+ "profile": "Telegram Founder & CEO",
83
+ "title": "Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature",
84
+ "youtube_url": "https://www.youtube.com/watch?v=qjPH9njnaVU",
85
+ "youtube_video_id": "qjPH9njnaVU",
86
+ "episode_slug": "pavel-durov",
87
+ "transcript_slug": "pavel-durov-transcript",
88
+ "has_transcript": true
89
+ },
90
+ {
91
+ "episode_number": 481,
92
+ "guest_name": "Norman Ohler",
93
+ "profile": "Historian & Author",
94
+ "title": "Norman Ohler: Hitler, Nazis, Drugs, WW2, Blitzkrieg, LSD, MKUltra & CIA",
95
+ "youtube_url": "https://www.youtube.com/watch?v=SvKv7D4pBjE",
96
+ "youtube_video_id": "SvKv7D4pBjE",
97
+ "episode_slug": "norman-ohler",
98
+ "transcript_slug": "norman-ohler-transcript",
99
+ "has_transcript": true
100
+ },
101
+ {
102
+ "episode_number": 480,
103
+ "guest_name": "Dave Hone",
104
+ "profile": "Paleontologist & Dinosaur Expert",
105
+ "title": "Dave Hone: T-Rex, Dinosaurs, Extinction, Evolution, and Jurassic Park",
106
+ "youtube_url": "https://www.youtube.com/watch?v=-Qm1_On71Oo",
107
+ "youtube_video_id": "-Qm1_On71Oo",
108
+ "episode_slug": "dave-hone",
109
+ "transcript_slug": "dave-hone-transcript",
110
+ "has_transcript": true
111
+ },
112
+ {
113
+ "episode_number": 479,
114
+ "guest_name": "Dave Plummer",
115
+ "profile": "Creator of Task Manager",
116
+ "title": "Dave Plummer: Programming, Autism, and Old-School Microsoft Stories",
117
+ "youtube_url": "https://www.youtube.com/watch?v=HsLgZzgpz9Y",
118
+ "youtube_video_id": "HsLgZzgpz9Y",
119
+ "episode_slug": "dave-plummer",
120
+ "transcript_slug": "dave-plummer-transcript",
121
+ "has_transcript": true
122
+ },
123
+ {
124
+ "episode_number": 477,
125
+ "guest_name": "Keyu Jin",
126
+ "profile": "Economist & China Specialist",
127
+ "title": "Keyu Jin: China's Economy, Tariffs, Trade, Trump, Communism & Capitalism",
128
+ "youtube_url": "https://www.youtube.com/watch?v=y3yAVZk3tyA",
129
+ "youtube_video_id": "y3yAVZk3tyA",
130
+ "episode_slug": "keyu-jin",
131
+ "transcript_slug": "keyu-jin-transcript",
132
+ "has_transcript": true
133
+ },
134
+ {
135
+ "episode_number": 476,
136
+ "guest_name": "Jack Weatherford",
137
+ "profile": "Anthropologist & Historian",
138
+ "title": "Jack Weatherford: Genghis Khan and the Mongol Empire",
139
+ "youtube_url": "https://www.youtube.com/watch?v=U1H1Ob7jk8Q",
140
+ "youtube_video_id": "U1H1Ob7jk8Q",
141
+ "episode_slug": "jack-weatherford",
142
+ "transcript_slug": "jack-weatherford-transcript",
143
+ "has_transcript": true
144
+ },
145
+ {
146
+ "episode_number": 475,
147
+ "guest_name": "Demis Hassabis",
148
+ "profile": "DeepMind Co-founder",
149
+ "title": "Demis Hassabis: Future of AI, Simulating Reality, Physics and Video Games",
150
+ "youtube_url": "https://www.youtube.com/watch?v=-HzgcbRXUK8",
151
+ "youtube_video_id": "-HzgcbRXUK8",
152
+ "episode_slug": "demis-hassabis-2",
153
+ "transcript_slug": "demis-hassabis-2-transcript",
154
+ "has_transcript": true
155
+ },
156
+ {
157
+ "episode_number": 474,
158
+ "guest_name": "DHH",
159
+ "profile": "Legendary Programmer",
160
+ "title": "DHH: Future of Programming, AI, Ruby on Rails, Productivity & Parenting",
161
+ "youtube_url": "https://www.youtube.com/watch?v=vagyIcmIGOQ",
162
+ "youtube_video_id": "vagyIcmIGOQ",
163
+ "episode_slug": "dhh-david-heinemeier-hansson",
164
+ "transcript_slug": "dhh-david-heinemeier-hansson-transcript",
165
+ "has_transcript": true
166
+ },
167
+ {
168
+ "episode_number": 473,
169
+ "guest_name": "Iran War Debate",
170
+ "profile": "Scott is a staunch critic of U.S. foreign policy and military interventionism, while Mike is a leading expert on Iran and its nuclear program",
171
+ "title": "Iran War Debate: Nuclear Weapons, Trump, Peace, Power & the Middle East",
172
+ "youtube_url": "https://www.youtube.com/watch?v=gtmJi8LbAts",
173
+ "youtube_video_id": "gtmJi8LbAts",
174
+ "episode_slug": "iran-war-debate",
175
+ "transcript_slug": "iran-war-debate-transcript",
176
+ "has_transcript": true
177
+ },
178
+ {
179
+ "episode_number": 472,
180
+ "guest_name": "Terence Tao",
181
+ "profile": "Mathematician",
182
+ "title": "Terence Tao: Hardest Problems in Mathematics, Physics & the Future of AI",
183
+ "youtube_url": "https://www.youtube.com/watch?v=HUkBz-cdB-k",
184
+ "youtube_video_id": "HUkBz-cdB-k",
185
+ "episode_slug": "terence-tao",
186
+ "transcript_slug": "terence-tao-transcript",
187
+ "has_transcript": true
188
+ },
189
+ {
190
+ "episode_number": 471,
191
+ "guest_name": "Sundar Pichai",
192
+ "profile": "CEO of Google & Alphabet",
193
+ "title": "Sundar Pichai: CEO of Google and Alphabet",
194
+ "youtube_url": "https://www.youtube.com/watch?v=9V6tWC4CdFQ",
195
+ "youtube_video_id": "9V6tWC4CdFQ",
196
+ "episode_slug": "sundar-pichai",
197
+ "transcript_slug": "sundar-pichai-transcript",
198
+ "has_transcript": true
199
+ },
200
+ {
201
+ "episode_number": 470,
202
+ "guest_name": "James Holland",
203
+ "profile": "Historian",
204
+ "title": "James Holland: World War II, Hitler, Churchill, Stalin & Biggest Battles",
205
+ "youtube_url": "https://www.youtube.com/watch?v=cp1lprZUQcE",
206
+ "youtube_video_id": "cp1lprZUQcE",
207
+ "episode_slug": "james-holland",
208
+ "transcript_slug": "james-holland-transcript",
209
+ "has_transcript": true
210
+ },
211
+ {
212
+ "episode_number": 469,
213
+ "guest_name": "Oliver Anthony",
214
+ "profile": "Singer-Songwriter",
215
+ "title": "Oliver Anthony: Country Music, Blue-Collar America, Fame, Money, and Pain",
216
+ "youtube_url": "https://www.youtube.com/watch?v=4OyB3hFb2AA",
217
+ "youtube_video_id": "4OyB3hFb2AA",
218
+ "episode_slug": "oliver-anthony",
219
+ "transcript_slug": "oliver-anthony-transcript",
220
+ "has_transcript": true
221
+ },
222
+ {
223
+ "episode_number": 468,
224
+ "guest_name": "Janna Levin",
225
+ "profile": "Theoretical Physicist & Cosmologist",
226
+ "title": "Janna Levin: Black Holes, Wormholes, Aliens, Paradoxes & Extra Dimensions",
227
+ "youtube_url": "https://www.youtube.com/watch?v=A6m4iJIw_84",
228
+ "youtube_video_id": "A6m4iJIw_84",
229
+ "episode_slug": "janna-levin",
230
+ "transcript_slug": "janna-levin-transcript",
231
+ "has_transcript": true
232
+ },
233
+ {
234
+ "episode_number": 467,
235
+ "guest_name": "Tim Sweeney",
236
+ "profile": "Epic Games Founder & Programmer",
237
+ "title": "Tim Sweeney: Fortnite, Unreal Engine, and the Future of Gaming",
238
+ "youtube_url": "https://www.youtube.com/watch?v=477qF6QNSvc",
239
+ "youtube_video_id": "477qF6QNSvc",
240
+ "episode_slug": "tim-sweeney",
241
+ "transcript_slug": "tim-sweeney-transcript",
242
+ "has_transcript": true
243
+ },
244
+ {
245
+ "episode_number": 466,
246
+ "guest_name": "Jeffrey Wasserstrom",
247
+ "profile": "China Historian",
248
+ "title": "Jeffrey Wasserstrom: China, Xi Jinping, Trade War, Taiwan, Hong Kong, Mao",
249
+ "youtube_url": "https://www.youtube.com/watch?v=3W5FWUN5w2Q",
250
+ "youtube_video_id": "3W5FWUN5w2Q",
251
+ "episode_slug": "jeffrey-wasserstrom",
252
+ "transcript_slug": "jeffrey-wasserstrom-transcript",
253
+ "has_transcript": true
254
+ },
255
+ {
256
+ "episode_number": 465,
257
+ "guest_name": "Robert Rodriguez",
258
+ "profile": "Legendary Filmmaker",
259
+ "title": "Robert Rodriguez: Sin City, Desperado, El Mariachi, Alita, and Filmmaking",
260
+ "youtube_url": "https://www.youtube.com/watch?v=xJoT3bJyHuA",
261
+ "youtube_video_id": "xJoT3bJyHuA",
262
+ "episode_slug": "robert-rodriguez",
263
+ "transcript_slug": "robert-rodriguez-transcript",
264
+ "has_transcript": true
265
+ },
266
+ {
267
+ "episode_number": 464,
268
+ "guest_name": "Dave Smith",
269
+ "profile": "Libertarian Political Commentator",
270
+ "title": "Dave Smith: Israel, Hamas, Ukraine, Russia, Conspiracies & Antisemitism",
271
+ "youtube_url": "https://www.youtube.com/watch?v=1V0bJfqEaa4",
272
+ "youtube_video_id": "1V0bJfqEaa4",
273
+ "episode_slug": "dave-smith",
274
+ "transcript_slug": "dave-smith-transcript",
275
+ "has_transcript": true
276
+ },
277
+ {
278
+ "episode_number": 463,
279
+ "guest_name": "Douglas Murray",
280
+ "profile": "Conservative Political Commentator",
281
+ "title": "Douglas Murray: Putin, Zelenskyy, Trump, Israel, Netanyahu, Hamas & Gaza",
282
+ "youtube_url": "https://www.youtube.com/watch?v=HvI42TyE5Ww",
283
+ "youtube_video_id": "HvI42TyE5Ww",
284
+ "episode_slug": "douglas-murray-2",
285
+ "transcript_slug": "douglas-murray-2-transcript",
286
+ "has_transcript": true
287
+ },
288
+ {
289
+ "episode_number": 462,
290
+ "guest_name": "Ezra Klein and Derek Thompson",
291
+ "profile": "Both representing the left-wing of American politics",
292
+ "title": "Ezra Klein and Derek Thompson: Politics, Trump, AOC, Elon & DOGE",
293
+ "youtube_url": "https://www.youtube.com/watch?v=DTPSeeKokdo",
294
+ "youtube_video_id": "DTPSeeKokdo",
295
+ "episode_slug": "ezra-klein-and-derek-thompson",
296
+ "transcript_slug": "ezra-klein-and-derek-thompson-transcript",
297
+ "has_transcript": true
298
+ },
299
+ {
300
+ "episode_number": 461,
301
+ "guest_name": "ThePrimeagen",
302
+ "profile": "Programmer & Educator",
303
+ "title": "ThePrimeagen: Programming, AI, ADHD, Productivity, Addiction, and God",
304
+ "youtube_url": "https://www.youtube.com/watch?v=tNZnLkRBYA8",
305
+ "youtube_video_id": "tNZnLkRBYA8",
306
+ "episode_slug": "theprimeagen",
307
+ "transcript_slug": "theprimeagen-transcript",
308
+ "has_transcript": true
309
+ },
310
+ {
311
+ "episode_number": 460,
312
+ "guest_name": "Narendra Modi",
313
+ "profile": "Prime Minister of India",
314
+ "title": "Narendra Modi: Prime Minister of India - Power, Democracy, War & Peace",
315
+ "youtube_url": "https://www.youtube.com/watch?v=ZPUtA3W-7_I",
316
+ "youtube_video_id": "ZPUtA3W-7_I",
317
+ "episode_slug": "narendra-modi",
318
+ "transcript_slug": "narendra-modi-transcript",
319
+ "has_transcript": true
320
+ },
321
+ {
322
+ "episode_number": 459,
323
+ "guest_name": "Dylan Patel and Nathan Lambert",
324
+ "profile": "AI and semiconductors researchers",
325
+ "title": "DeepSeek, China, OpenAI, NVIDIA, xAI, TSMC, Stargate, and AI Megaclusters",
326
+ "youtube_url": "https://www.youtube.com/watch?v=_1f-o0nqpEI",
327
+ "youtube_video_id": "_1f-o0nqpEI",
328
+ "episode_slug": "deepseek-dylan-patel-nathan-lambert",
329
+ "transcript_slug": "deepseek-dylan-patel-nathan-lambert-transcript",
330
+ "has_transcript": true
331
+ },
332
+ {
333
+ "episode_number": 458,
334
+ "guest_name": "Marc Andreessen",
335
+ "profile": "Tech Entrepreneur & Investor",
336
+ "title": "Marc Andreessen: Trump, Power, Tech, AI, Immigration & Future of America",
337
+ "youtube_url": "https://www.youtube.com/watch?v=OHWnPOKh_S0",
338
+ "youtube_video_id": "OHWnPOKh_S0",
339
+ "episode_slug": "marc-andreessen-2",
340
+ "transcript_slug": "marc-andreessen-2-transcript",
341
+ "has_transcript": true
342
+ },
343
+ {
344
+ "episode_number": 457,
345
+ "guest_name": "Jennifer Burns",
346
+ "profile": "Economic Historian",
347
+ "title": "Jennifer Burns: Milton Friedman, Ayn Rand, Economics, Capitalism, Freedom",
348
+ "youtube_url": "https://www.youtube.com/watch?v=Rz-4ulRKnz4",
349
+ "youtube_video_id": "Rz-4ulRKnz4",
350
+ "episode_slug": "jennifer-burns",
351
+ "transcript_slug": "jennifer-burns-transcript",
352
+ "has_transcript": true
353
+ },
354
+ {
355
+ "episode_number": 456,
356
+ "guest_name": "Volodymyr Zelenskyy",
357
+ "profile": "President of Ukraine",
358
+ "title": "Volodymyr Zelenskyy: Ukraine, War, Peace, Putin, Trump, NATO, and Freedom",
359
+ "youtube_url": "https://www.youtube.com/watch?v=u321m25rKXc",
360
+ "youtube_video_id": "u321m25rKXc",
361
+ "episode_slug": "volodymyr-zelenskyy",
362
+ "transcript_slug": "volodymyr-zelenskyy-transcript",
363
+ "has_transcript": true
364
+ },
365
+ {
366
+ "episode_number": 455,
367
+ "guest_name": "Adam Frank",
368
+ "profile": "Astrophysicist",
369
+ "title": "Adam Frank: Alien Civilizations and the Search for Extraterrestrial Life",
370
+ "youtube_url": "https://www.youtube.com/watch?v=yhZAXXI83-4",
371
+ "youtube_video_id": "yhZAXXI83-4",
372
+ "episode_slug": "adam-frank",
373
+ "transcript_slug": "adam-frank-transcript",
374
+ "has_transcript": true
375
+ },
376
+ {
377
+ "episode_number": 454,
378
+ "guest_name": "Saagar Enjeti",
379
+ "profile": "Political Commentator",
380
+ "title": "Saagar Enjeti: Trump, MAGA, DOGE, Obama, FDR, JFK, History & Politics",
381
+ "youtube_url": "https://www.youtube.com/watch?v=9xz8i90Hp2A",
382
+ "youtube_video_id": "9xz8i90Hp2A",
383
+ "episode_slug": "saagar-enjeti-2",
384
+ "transcript_slug": "saagar-enjeti-2-transcript",
385
+ "has_transcript": true
386
+ },
387
+ {
388
+ "episode_number": 453,
389
+ "guest_name": "Javier Milei",
390
+ "profile": "President of Argentina",
391
+ "title": "Javier Milei: President of Argentina - Freedom, Economics, and Corruption",
392
+ "youtube_url": "https://www.youtube.com/watch?v=8NLzc9kobDk",
393
+ "youtube_video_id": "8NLzc9kobDk",
394
+ "episode_slug": "javier-milei",
395
+ "transcript_slug": "javier-milei-transcript",
396
+ "has_transcript": true
397
+ },
398
+ {
399
+ "episode_number": 452,
400
+ "guest_name": "Dario Amodei",
401
+ "profile": "CEO of Anthropic",
402
+ "title": "Dario Amodei: Anthropic CEO on Claude, AGI & the Future of AI & Humanity",
403
+ "youtube_url": "https://www.youtube.com/watch?v=ugvHCXCOmm4",
404
+ "youtube_video_id": "ugvHCXCOmm4",
405
+ "episode_slug": "dario-amodei",
406
+ "transcript_slug": "dario-amodei-transcript",
407
+ "has_transcript": true
408
+ },
409
+ {
410
+ "episode_number": 451,
411
+ "guest_name": "Rick Spence",
412
+ "profile": "Intelligence & Conspiracy Historian",
413
+ "title": "Rick Spence: CIA, KGB, Illuminati, Secret Societies, Cults & Conspiracies",
414
+ "youtube_url": "https://www.youtube.com/watch?v=abd5hguWKz0",
415
+ "youtube_video_id": "abd5hguWKz0",
416
+ "episode_slug": "rick-spence",
417
+ "transcript_slug": "rick-spence-transcript",
418
+ "has_transcript": true
419
+ },
420
+ {
421
+ "episode_number": 450,
422
+ "guest_name": "Bernie Sanders",
423
+ "profile": "Progressive Politician",
424
+ "title": "Bernie Sanders Interview",
425
+ "youtube_url": "https://www.youtube.com/watch?v=MzkgWDCucNY",
426
+ "youtube_video_id": "MzkgWDCucNY",
427
+ "episode_slug": "bernie-sanders",
428
+ "transcript_slug": "bernie-sanders-transcript",
429
+ "has_transcript": true
430
+ },
431
+ {
432
+ "episode_number": 449,
433
+ "guest_name": "Graham Hancock",
434
+ "profile": "Archaeological Journalist & Author",
435
+ "title": "Graham Hancock: Lost Civilization of the Ice Age & Ancient Human History",
436
+ "youtube_url": "https://www.youtube.com/watch?v=NMHiLvirCb0",
437
+ "youtube_video_id": "NMHiLvirCb0",
438
+ "episode_slug": "graham-hancock",
439
+ "transcript_slug": "graham-hancock-transcript",
440
+ "has_transcript": true
441
+ },
442
+ {
443
+ "episode_number": 448,
444
+ "guest_name": "Jordan Peterson",
445
+ "profile": "Psychologist & Author",
446
+ "title": "Jordan Peterson: Nietzsche, Hitler, God, Psychopathy, Suffering & Meaning",
447
+ "youtube_url": "https://www.youtube.com/watch?v=q8VePUwjB9Y",
448
+ "youtube_video_id": "q8VePUwjB9Y",
449
+ "episode_slug": "jordan-peterson-2",
450
+ "transcript_slug": "jordan-peterson-2-transcript",
451
+ "has_transcript": true
452
+ },
453
+ {
454
+ "episode_number": 447,
455
+ "guest_name": "Cursor Team",
456
+ "profile": "Creators of AI-assisted code editor",
457
+ "title": "Cursor Team: Future of Programming with AI",
458
+ "youtube_url": "https://www.youtube.com/watch?v=oFfVt3S51T4",
459
+ "youtube_video_id": "oFfVt3S51T4",
460
+ "episode_slug": "cursor-team",
461
+ "transcript_slug": "cursor-team-transcript",
462
+ "has_transcript": true
463
+ },
464
+ {
465
+ "episode_number": 446,
466
+ "guest_name": "Ed Barnhart",
467
+ "profile": "Archaeologist & Maya Expert",
468
+ "title": "Ed Barnhart: Maya, Aztec, Inca, and Lost Civilizations of South America",
469
+ "youtube_url": "https://www.youtube.com/watch?v=AzzE7GOvYz8",
470
+ "youtube_video_id": "AzzE7GOvYz8",
471
+ "episode_slug": "ed-barnhart",
472
+ "transcript_slug": "ed-barnhart-transcript",
473
+ "has_transcript": true
474
+ },
475
+ {
476
+ "episode_number": 445,
477
+ "guest_name": "Vivek Ramaswamy",
478
+ "profile": "Conservative Politician",
479
+ "title": "Vivek Ramaswamy: Trump, Conservatism, Nationalism, Immigration, and War",
480
+ "youtube_url": "https://www.youtube.com/watch?v=Q8Qk_3a3lUw",
481
+ "youtube_video_id": "Q8Qk_3a3lUw",
482
+ "episode_slug": "vivek-ramaswamy",
483
+ "transcript_slug": "vivek-ramaswamy-transcript",
484
+ "has_transcript": true
485
+ },
486
+ {
487
+ "episode_number": 444,
488
+ "guest_name": "Vejas Liulevicius",
489
+ "profile": "Historian",
490
+ "title": "Vejas Liulevicius: Communism, Marxism, Nazism, Stalin, Mao, and Hitler",
491
+ "youtube_url": "https://www.youtube.com/watch?v=s1oTH4Sjvzg",
492
+ "youtube_video_id": "s1oTH4Sjvzg",
493
+ "episode_slug": "vejas-liulevicius",
494
+ "transcript_slug": "vejas-liulevicius-transcript",
495
+ "has_transcript": true
496
+ },
497
+ {
498
+ "episode_number": 443,
499
+ "guest_name": "Gregory Aldrete",
500
+ "profile": "Historian",
501
+ "title": "Gregory Aldrete: The Roman Empire - Rise and Fall of Ancient Rome",
502
+ "youtube_url": "https://www.youtube.com/watch?v=DyoVVSggPjY",
503
+ "youtube_video_id": "DyoVVSggPjY",
504
+ "episode_slug": "gregory-aldrete",
505
+ "transcript_slug": "gregory-aldrete-transcript",
506
+ "has_transcript": true
507
+ },
508
+ {
509
+ "episode_number": 442,
510
+ "guest_name": "Donald Trump",
511
+ "profile": "President of the United States",
512
+ "title": "Donald Trump Interview",
513
+ "youtube_url": "https://www.youtube.com/watch?v=qCbfTN-caFI",
514
+ "youtube_video_id": "qCbfTN-caFI",
515
+ "episode_slug": "donald-trump",
516
+ "transcript_slug": "donald-trump-transcript",
517
+ "has_transcript": true
518
+ },
519
+ {
520
+ "episode_number": 441,
521
+ "guest_name": "Cenk Uygur",
522
+ "profile": "Progressive Political Commentator",
523
+ "title": "Cenk Uygur: Trump vs Harris, Progressive Politics, Communism & Capitalism",
524
+ "youtube_url": "https://www.youtube.com/watch?v=QJtPROVsePk",
525
+ "youtube_video_id": "QJtPROVsePk",
526
+ "episode_slug": "cenk-uygur",
527
+ "transcript_slug": "cenk-uygur-transcript",
528
+ "has_transcript": true
529
+ },
530
+ {
531
+ "episode_number": 440,
532
+ "guest_name": "Pieter Levels",
533
+ "profile": "Digital Nomad Entrepreneur",
534
+ "title": "Pieter Levels: Programming, Viral AI Startups, and Digital Nomad Life",
535
+ "youtube_url": "https://www.youtube.com/watch?v=oFtjKbXKqbg",
536
+ "youtube_video_id": "oFtjKbXKqbg",
537
+ "episode_slug": "pieter-levels",
538
+ "transcript_slug": "pieter-levels-transcript",
539
+ "has_transcript": true
540
+ },
541
+ {
542
+ "episode_number": 439,
543
+ "guest_name": "Craig Jones",
544
+ "profile": "Jiu Jitsu Champion",
545
+ "title": "Craig Jones: Jiu Jitsu, $2 Million Prize, CJI, ADCC, Ukraine & Trolling",
546
+ "youtube_url": "https://www.youtube.com/watch?v=J7aiEwp1x9k",
547
+ "youtube_video_id": "J7aiEwp1x9k",
548
+ "episode_slug": "craig-jones-2",
549
+ "transcript_slug": "craig-jones-2-transcript",
550
+ "has_transcript": true
551
+ },
552
+ {
553
+ "episode_number": 438,
554
+ "guest_name": "Elon Musk and Neuralink Team",
555
+ "profile": "Elon Musk is CEO of Neuralink, SpaceX, Tesla, xAI, and CTO of X. DJ Seo is COO & President of Neuralink. Matthew MacDougall is Head Neurosurgeon at Neuralink. Bliss Chapman is Brain Interface Software Lead at Neuralink. Noland Arbaugh is the first human to have a Neuralink device implanted in his brain",
556
+ "title": "Elon Musk: Neuralink and the Future of Humanity",
557
+ "youtube_url": "https://www.youtube.com/watch?v=Kbk9BiPhm7o",
558
+ "youtube_video_id": "Kbk9BiPhm7o",
559
+ "episode_slug": "elon-musk-and-neuralink-team",
560
+ "transcript_slug": "elon-musk-and-neuralink-team-transcript",
561
+ "has_transcript": true
562
+ },
563
+ {
564
+ "episode_number": 437,
565
+ "guest_name": "Jordan Jonas",
566
+ "profile": "Wilderness Survival Expert",
567
+ "title": "Jordan Jonas: Survival, Hunting, Siberia, God, and Winning Alone Season 6",
568
+ "youtube_url": "https://www.youtube.com/watch?v=WA9gVKKPsBo",
569
+ "youtube_video_id": "WA9gVKKPsBo",
570
+ "episode_slug": "jordan-jonas",
571
+ "transcript_slug": "jordan-jonas-transcript",
572
+ "has_transcript": true
573
+ },
574
+ {
575
+ "episode_number": 436,
576
+ "guest_name": "Ivanka Trump",
577
+ "profile": "Businesswoman, Presidential Advisor",
578
+ "title": "Ivanka Trump: Politics, Family, Real Estate, Fashion, Music, and Life",
579
+ "youtube_url": "https://www.youtube.com/watch?v=2oxdDKHdcM8",
580
+ "youtube_video_id": "2oxdDKHdcM8",
581
+ "episode_slug": "ivanka-trump",
582
+ "transcript_slug": "ivanka-trump-transcript",
583
+ "has_transcript": true
584
+ },
585
+ {
586
+ "episode_number": 435,
587
+ "guest_name": "Andrew Huberman",
588
+ "profile": "Neuroscientist",
589
+ "title": "Andrew Huberman: Focus, Controversy, Politics, and Relationships",
590
+ "youtube_url": "https://www.youtube.com/watch?v=ZIyB9e_7a4c",
591
+ "youtube_video_id": "ZIyB9e_7a4c",
592
+ "episode_slug": "andrew-huberman-5",
593
+ "transcript_slug": "andrew-huberman-5-transcript",
594
+ "has_transcript": true
595
+ },
596
+ {
597
+ "episode_number": 434,
598
+ "guest_name": "Aravind Srinivas",
599
+ "profile": "CEO of Perplexity",
600
+ "title": "Aravind Srinivas: Perplexity CEO on Future of AI, Search & the Internet",
601
+ "youtube_url": "https://www.youtube.com/watch?v=e-gwvmhyU7A",
602
+ "youtube_video_id": "e-gwvmhyU7A",
603
+ "episode_slug": "aravind-srinivas",
604
+ "transcript_slug": "aravind-srinivas-transcript",
605
+ "has_transcript": true
606
+ },
607
+ {
608
+ "episode_number": 433,
609
+ "guest_name": "Sara Walker",
610
+ "profile": "Astrobiologist & Physicist",
611
+ "title": "Sara Walker: Physics of Life, Time, Complexity, and Aliens",
612
+ "youtube_url": "https://www.youtube.com/watch?v=wwhTfyX9J34",
613
+ "youtube_video_id": "wwhTfyX9J34",
614
+ "episode_slug": "sara-walker-3",
615
+ "transcript_slug": "sara-walker-3-transcript",
616
+ "has_transcript": true
617
+ },
618
+ {
619
+ "episode_number": 432,
620
+ "guest_name": "Kevin Spacey",
621
+ "profile": "Oscar-Winning Actor",
622
+ "title": "Kevin Spacey: Power, Controversy, Betrayal, Truth & Love in Film and Life",
623
+ "youtube_url": "https://www.youtube.com/watch?v=XJTMQtE-MIo",
624
+ "youtube_video_id": "XJTMQtE-MIo",
625
+ "episode_slug": "kevin-spacey",
626
+ "transcript_slug": "kevin-spacey-transcript",
627
+ "has_transcript": true
628
+ },
629
+ {
630
+ "episode_number": 431,
631
+ "guest_name": "Roman Yampolskiy",
632
+ "profile": "AI Safety Researcher",
633
+ "title": "Roman Yampolskiy: Dangers of Superintelligent AI",
634
+ "youtube_url": "https://www.youtube.com/watch?v=NNr6gPelJ3E",
635
+ "youtube_video_id": "NNr6gPelJ3E",
636
+ "episode_slug": "roman-yampolskiy",
637
+ "transcript_slug": "roman-yampolskiy-transcript",
638
+ "has_transcript": true
639
+ },
640
+ {
641
+ "episode_number": 430,
642
+ "guest_name": "Charan Ranganath",
643
+ "profile": "Memory Research Neuroscientist",
644
+ "title": "Charan Ranganath: Human Memory, Imagination, Deja Vu, and False Memories",
645
+ "youtube_url": "https://www.youtube.com/watch?v=4iuepdI3wCU",
646
+ "youtube_video_id": "4iuepdI3wCU",
647
+ "episode_slug": "charan-ranganath",
648
+ "transcript_slug": "charan-ranganath-transcript",
649
+ "has_transcript": true
650
+ },
651
+ {
652
+ "episode_number": 429,
653
+ "guest_name": "Paul Rosolie",
654
+ "profile": "Naturalist & Explorer",
655
+ "title": "Paul Rosolie: Jungle, Apex Predators, Aliens, Uncontacted Tribes, and God",
656
+ "youtube_url": "https://www.youtube.com/watch?v=pwN8u6HFH8U",
657
+ "youtube_video_id": "pwN8u6HFH8U",
658
+ "episode_slug": "paul-rosolie-2",
659
+ "transcript_slug": "paul-rosolie-2-transcript",
660
+ "has_transcript": true
661
+ },
662
+ {
663
+ "episode_number": 428,
664
+ "guest_name": "Sean Carroll",
665
+ "profile": "Physicist",
666
+ "title": "Sean Carroll: General Relativity, Quantum Mechanics, Black Holes & Aliens",
667
+ "youtube_url": "https://www.youtube.com/watch?v=tdv7r2JSokI",
668
+ "youtube_video_id": "tdv7r2JSokI",
669
+ "episode_slug": "sean-carroll-3",
670
+ "transcript_slug": "sean-carroll-3-transcript",
671
+ "has_transcript": true
672
+ },
673
+ {
674
+ "episode_number": 427,
675
+ "guest_name": "Neil Adams",
676
+ "profile": "Judo World Champion",
677
+ "title": "Neil Adams: Judo, Olympics, Winning, Losing, and the Champion Mindset",
678
+ "youtube_url": "https://www.youtube.com/watch?v=TXabC2Ave74",
679
+ "youtube_video_id": "TXabC2Ave74",
680
+ "episode_slug": "neil-adams",
681
+ "transcript_slug": "neil-adams-transcript",
682
+ "has_transcript": true
683
+ },
684
+ {
685
+ "episode_number": 426,
686
+ "guest_name": "Edward Gibson",
687
+ "profile": "Psycholinguistics Professor",
688
+ "title": "Edward Gibson: Human Language, Psycholinguistics, Syntax, Grammar & LLMs",
689
+ "youtube_url": "https://www.youtube.com/watch?v=F3Jd9GI6XqE",
690
+ "youtube_video_id": "F3Jd9GI6XqE",
691
+ "episode_slug": "edward-gibson",
692
+ "transcript_slug": "edward-gibson-transcript",
693
+ "has_transcript": true
694
+ },
695
+ {
696
+ "episode_number": 425,
697
+ "guest_name": "Andrew Callaghan",
698
+ "profile": "Political Documentarian",
699
+ "title": "Andrew Callaghan: Channel 5, Gonzo, QAnon, O-Block, Politics & Alex Jones",
700
+ "youtube_url": "https://www.youtube.com/watch?v=yEou104m_P0",
701
+ "youtube_video_id": "yEou104m_P0",
702
+ "episode_slug": "andrew-callaghan",
703
+ "transcript_slug": "andrew-callaghan-transcript",
704
+ "has_transcript": true
705
+ },
706
+ {
707
+ "episode_number": 424,
708
+ "guest_name": "Bassem Youssef",
709
+ "profile": "Egyptian Political Satirist",
710
+ "title": "Bassem Youssef: Israel-Palestine, Gaza, Hamas, Middle East, Satire & Fame",
711
+ "youtube_url": "https://www.youtube.com/watch?v=sG8u6owzad4",
712
+ "youtube_video_id": "sG8u6owzad4",
713
+ "episode_slug": "bassem-youssef",
714
+ "transcript_slug": "bassem-youssef-transcript",
715
+ "has_transcript": true
716
+ },
717
+ {
718
+ "episode_number": 423,
719
+ "guest_name": "Tulsi Gabbard",
720
+ "profile": "Conservative Political Commentator",
721
+ "title": "Tulsi Gabbard: War, Politics, and the Military Industrial Complex",
722
+ "youtube_url": "https://www.youtube.com/watch?v=_El9riy9Zjw",
723
+ "youtube_video_id": "_El9riy9Zjw",
724
+ "episode_slug": "tulsi-gabbard",
725
+ "transcript_slug": "tulsi-gabbard-transcript",
726
+ "has_transcript": true
727
+ },
728
+ {
729
+ "episode_number": 422,
730
+ "guest_name": "Mark Cuban",
731
+ "profile": "Entrepreneur",
732
+ "title": "Mark Cuban: Shark Tank, DEI & Wokeism Debate, Elon Musk, Politics & Drugs",
733
+ "youtube_url": "https://www.youtube.com/watch?v=0cn3VBjfN8g",
734
+ "youtube_video_id": "0cn3VBjfN8g",
735
+ "episode_slug": "mark-cuban",
736
+ "transcript_slug": "mark-cuban-transcript",
737
+ "has_transcript": true
738
+ },
739
+ {
740
+ "episode_number": 421,
741
+ "guest_name": "Dana White",
742
+ "profile": "UFC President",
743
+ "title": "Dana White: UFC, Fighting, Khabib, Conor, Tyson, Ali, Rogan, Elon & Zuck",
744
+ "youtube_url": "https://www.youtube.com/watch?v=k7aQEqDbuf8",
745
+ "youtube_video_id": "k7aQEqDbuf8",
746
+ "episode_slug": "dana-white",
747
+ "transcript_slug": "dana-white-transcript",
748
+ "has_transcript": true
749
+ },
750
+ {
751
+ "episode_number": 420,
752
+ "guest_name": "Annie Jacobsen",
753
+ "profile": "National Security Investigative Journalist",
754
+ "title": "Annie Jacobsen: Nuclear War, CIA, KGB, Aliens, Area 51, Roswell & Secrecy",
755
+ "youtube_url": "https://www.youtube.com/watch?v=GXgGR8KxFao",
756
+ "youtube_video_id": "GXgGR8KxFao",
757
+ "episode_slug": "annie-jacobsen",
758
+ "transcript_slug": "annie-jacobsen-transcript",
759
+ "has_transcript": true
760
+ },
761
+ {
762
+ "episode_number": 419,
763
+ "guest_name": "Sam Altman",
764
+ "profile": "CEO of OpenAI",
765
+ "title": "Sam Altman: OpenAI, GPT-5, Sora, Board Saga, Elon Musk, Ilya, Power & AGI",
766
+ "youtube_url": "https://www.youtube.com/watch?v=jvqFAi7vkBc",
767
+ "youtube_video_id": "jvqFAi7vkBc",
768
+ "episode_slug": "sam-altman-2",
769
+ "transcript_slug": "sam-altman-2-transcript",
770
+ "has_transcript": true
771
+ },
772
+ {
773
+ "episode_number": 418,
774
+ "guest_name": "Israel-Palestine Debate",
775
+ "profile": "Norman Finkelstein and Benny Morris are historians. Mouin Rabbani is a Middle East analyst. Steven Bonnell (aka Destiny) is a political livestreamer",
776
+ "title": "Israel-Palestine Debate: Finkelstein, Destiny, M. Rabbani & Benny Morris",
777
+ "youtube_url": "https://www.youtube.com/watch?v=1X_KdkoGxSs",
778
+ "youtube_video_id": "1X_KdkoGxSs",
779
+ "episode_slug": "israel-palestine-debate",
780
+ "transcript_slug": "israel-palestine-debate-transcript",
781
+ "has_transcript": true
782
+ },
783
+ {
784
+ "episode_number": 417,
785
+ "guest_name": "Kimbal Musk",
786
+ "profile": "Entrepreneur & Restaurateur",
787
+ "title": "Kimbal Musk: The Art of Cooking, Tesla, SpaceX, Zip2, and Family",
788
+ "youtube_url": "https://www.youtube.com/watch?v=iAlwZyRUOVM",
789
+ "youtube_video_id": "iAlwZyRUOVM",
790
+ "episode_slug": "kimbal-musk",
791
+ "transcript_slug": "kimbal-musk-transcript",
792
+ "has_transcript": true
793
+ },
794
+ {
795
+ "episode_number": 416,
796
+ "guest_name": "Yann LeCun",
797
+ "profile": "AI Research Pioneer",
798
+ "title": "Yann Lecun: Meta AI, Open Source, Limits of LLMs, AGI & the Future of AI",
799
+ "youtube_url": "https://www.youtube.com/watch?v=5t1vTLU7s40",
800
+ "youtube_video_id": "5t1vTLU7s40",
801
+ "episode_slug": "yann-lecun-3",
802
+ "transcript_slug": "yann-lecun-3-transcript",
803
+ "has_transcript": true
804
+ },
805
+ {
806
+ "episode_number": 415,
807
+ "guest_name": "Serhii Plokhy",
808
+ "profile": "Historian",
809
+ "title": "Serhii Plokhy: History of Ukraine, Russia, Soviet Union, KGB, Nazis & War",
810
+ "youtube_url": "https://www.youtube.com/watch?v=qa-wl8_wpZA",
811
+ "youtube_video_id": "qa-wl8_wpZA",
812
+ "episode_slug": "serhii-plokhy",
813
+ "transcript_slug": "serhii-plokhy-transcript",
814
+ "has_transcript": true
815
+ },
816
+ {
817
+ "episode_number": 414,
818
+ "guest_name": "Tucker Carlson",
819
+ "profile": "Conservative Political Commentator",
820
+ "title": "Tucker Carlson: Putin, Navalny, Trump, CIA, NSA, War, Politics & Freedom",
821
+ "youtube_url": "https://www.youtube.com/watch?v=f_lRdkH_QoY",
822
+ "youtube_video_id": "f_lRdkH_QoY",
823
+ "episode_slug": "tucker-carlson",
824
+ "transcript_slug": "tucker-carlson-transcript",
825
+ "has_transcript": true
826
+ },
827
+ {
828
+ "episode_number": 413,
829
+ "guest_name": "Bill Ackman",
830
+ "profile": "Investor",
831
+ "title": "Bill Ackman: Investing, Financial Battles, Harvard, DEI, X & Free Speech",
832
+ "youtube_url": "https://www.youtube.com/watch?v=PgGKhsWhUu8",
833
+ "youtube_video_id": "PgGKhsWhUu8",
834
+ "episode_slug": "bill-ackman",
835
+ "transcript_slug": "bill-ackman-transcript",
836
+ "has_transcript": true
837
+ },
838
+ {
839
+ "episode_number": 412,
840
+ "guest_name": "Marc Raibert",
841
+ "profile": "CEO of Boston Dynamics",
842
+ "title": "Marc Raibert: Boston Dynamics and the Future of Robotics",
843
+ "youtube_url": "https://www.youtube.com/watch?v=5VnbBCm_ZyQ",
844
+ "youtube_video_id": "5VnbBCm_ZyQ",
845
+ "episode_slug": "marc-raibert",
846
+ "transcript_slug": "marc-raibert-transcript",
847
+ "has_transcript": true
848
+ },
849
+ {
850
+ "episode_number": 411,
851
+ "guest_name": "Omar Suleiman",
852
+ "profile": "Islamic Scholar",
853
+ "title": "Omar Suleiman: Palestine, Gaza, Oct 7, Israel, Resistance, Faith & Islam",
854
+ "youtube_url": "https://www.youtube.com/watch?v=oFSyNdQf5uk",
855
+ "youtube_video_id": "oFSyNdQf5uk",
856
+ "episode_slug": "omar-suleiman-2",
857
+ "transcript_slug": "omar-suleiman-2-transcript",
858
+ "has_transcript": true
859
+ },
860
+ {
861
+ "episode_number": 410,
862
+ "guest_name": "Ben Shapiro vs Destiny Debate",
863
+ "profile": "Ben is a conservative political commentator, while Destiny is a liberal political commentator",
864
+ "title": "Ben Shapiro vs Destiny Debate: Politics, Jan 6, Israel, Ukraine & Wokeism",
865
+ "youtube_url": "https://www.youtube.com/watch?v=tYrdMjVXyNg",
866
+ "youtube_video_id": "tYrdMjVXyNg",
867
+ "episode_slug": "ben-shapiro-destiny-debate",
868
+ "transcript_slug": "ben-shapiro-destiny-debate-transcript",
869
+ "has_transcript": true
870
+ },
871
+ {
872
+ "episode_number": 409,
873
+ "guest_name": "Matthew Cox",
874
+ "profile": "Former Bank Fraudster",
875
+ "title": "Matthew Cox: FBI Most Wanted Con Man - $55 Million in Bank Fraud",
876
+ "youtube_url": "https://www.youtube.com/watch?v=zMYvGf7BA9o",
877
+ "youtube_video_id": "zMYvGf7BA9o",
878
+ "episode_slug": "matthew-cox",
879
+ "transcript_slug": "matthew-cox-transcript",
880
+ "has_transcript": true
881
+ },
882
+ {
883
+ "episode_number": 408,
884
+ "guest_name": "Tal Wilkenfeld",
885
+ "profile": "Bass Virtuoso & Musician",
886
+ "title": "Tal Wilkenfeld: Music, Guitar, Bass, Jeff Beck, Prince, and Leonard Cohen",
887
+ "youtube_url": "https://www.youtube.com/watch?v=9qfwPv7clEw",
888
+ "youtube_video_id": "9qfwPv7clEw",
889
+ "episode_slug": "tal-wilkenfeld",
890
+ "transcript_slug": "tal-wilkenfeld-transcript",
891
+ "has_transcript": true
892
+ },
893
+ {
894
+ "episode_number": 407,
895
+ "guest_name": "Guillaume Verdon",
896
+ "profile": "E/acc Movement Founder & Physicist",
897
+ "title": "Guillaume Verdon: Beff Jezos, E/acc Movement, Physics, Computation & AGI",
898
+ "youtube_url": "https://www.youtube.com/watch?v=8fEEbKJoNbU",
899
+ "youtube_video_id": "8fEEbKJoNbU",
900
+ "episode_slug": "guillaume-verdon",
901
+ "transcript_slug": "guillaume-verdon-transcript",
902
+ "has_transcript": true
903
+ },
904
+ {
905
+ "episode_number": 406,
906
+ "guest_name": "Teddy Atlas",
907
+ "profile": "Legendary Boxing Trainer",
908
+ "title": "Teddy Atlas: Mike Tyson, Cus D'Amato, Boxing, Loyalty, Fear & Greatness",
909
+ "youtube_url": "https://www.youtube.com/watch?v=CVx9IB_U9X4",
910
+ "youtube_video_id": "CVx9IB_U9X4",
911
+ "episode_slug": "teddy-atlas",
912
+ "transcript_slug": "teddy-atlas-transcript",
913
+ "has_transcript": true
914
+ },
915
+ {
916
+ "episode_number": 405,
917
+ "guest_name": "Jeff Bezos",
918
+ "profile": "Founder of Amazon & Blue Origin",
919
+ "title": "Jeff Bezos: Amazon and Blue Origin",
920
+ "youtube_url": "https://www.youtube.com/watch?v=DcWqzZ3I2cY",
921
+ "youtube_video_id": "DcWqzZ3I2cY",
922
+ "episode_slug": "jeff-bezos",
923
+ "transcript_slug": "jeff-bezos-transcript",
924
+ "has_transcript": true
925
+ },
926
+ {
927
+ "episode_number": 404,
928
+ "guest_name": "Lee Cronin",
929
+ "profile": "Chemist",
930
+ "title": "Lee Cronin: Controversial Nature Paper on Evolution of Life and Universe",
931
+ "youtube_url": "https://www.youtube.com/watch?v=CGiDqhSdLHk",
932
+ "youtube_video_id": "CGiDqhSdLHk",
933
+ "episode_slug": "lee-cronin-3",
934
+ "transcript_slug": "lee-cronin-3-transcript",
935
+ "has_transcript": true
936
+ },
937
+ {
938
+ "episode_number": 403,
939
+ "guest_name": "Lisa Randall",
940
+ "profile": "Theoretical Physicist",
941
+ "title": "Lisa Randall: Dark Matter, Theoretical Physics, and Extinction Events",
942
+ "youtube_url": "https://www.youtube.com/watch?v=VPaOy3G1-2A",
943
+ "youtube_video_id": "VPaOy3G1-2A",
944
+ "episode_slug": "lisa-randall",
945
+ "transcript_slug": "lisa-randall-transcript",
946
+ "has_transcript": true
947
+ },
948
+ {
949
+ "episode_number": 402,
950
+ "guest_name": "Michael Malice",
951
+ "profile": "Anarchist & Political Commentator",
952
+ "title": "Michael Malice: Thanksgiving Pirate Special",
953
+ "youtube_url": "https://www.youtube.com/watch?v=2yHr9DPnSzk",
954
+ "youtube_video_id": "2yHr9DPnSzk",
955
+ "episode_slug": "michael-malice-7",
956
+ "transcript_slug": "michael-malice-7-transcript",
957
+ "has_transcript": true
958
+ },
959
+ {
960
+ "episode_number": 401,
961
+ "guest_name": "John Mearsheimer",
962
+ "profile": "International Relations Scholar",
963
+ "title": "John Mearsheimer: Israel-Palestine, Russia-Ukraine, China, NATO, and WW3",
964
+ "youtube_url": "https://www.youtube.com/watch?v=r4wLXNydzeY",
965
+ "youtube_video_id": "r4wLXNydzeY",
966
+ "episode_slug": "john-mearsheimer",
967
+ "transcript_slug": "john-mearsheimer-transcript",
968
+ "has_transcript": true
969
+ },
970
+ {
971
+ "episode_number": 400,
972
+ "guest_name": "Elon Musk",
973
+ "profile": "CEO of Tesla, SpaceX, Neuralink, X",
974
+ "title": "Elon Musk: War, AI, Aliens, Politics, Physics, Video Games, and Humanity",
975
+ "youtube_url": "https://www.youtube.com/watch?v=JN3KPFbWCy8",
976
+ "youtube_video_id": "JN3KPFbWCy8",
977
+ "episode_slug": "elon-musk-4",
978
+ "transcript_slug": "elon-musk-4-transcript",
979
+ "has_transcript": true
980
+ },
981
+ {
982
+ "episode_number": 399,
983
+ "guest_name": "Jared Kushner",
984
+ "profile": "Investor, Presidential Advisor",
985
+ "title": "Jared Kushner: Israel, Palestine, Hamas, Gaza, Iran, and the Middle East",
986
+ "youtube_url": "https://www.youtube.com/watch?v=co_MeKSnyAo",
987
+ "youtube_video_id": "co_MeKSnyAo",
988
+ "episode_slug": "jared-kushner",
989
+ "transcript_slug": "jared-kushner-transcript",
990
+ "has_transcript": true
991
+ },
992
+ {
993
+ "episode_number": 398,
994
+ "guest_name": "Mark Zuckerberg",
995
+ "profile": "CEO of Meta",
996
+ "title": "Mark Zuckerberg: First Interview in the Metaverse",
997
+ "youtube_url": "https://www.youtube.com/watch?v=MVYrJJNdrEg",
998
+ "youtube_video_id": "MVYrJJNdrEg",
999
+ "episode_slug": "mark-zuckerberg-3",
1000
+ "transcript_slug": "mark-zuckerberg-3-transcript",
1001
+ "has_transcript": true
1002
+ },
1003
+ {
1004
+ "episode_number": 397,
1005
+ "guest_name": "Greg Lukianoff",
1006
+ "profile": "Free Speech Attorney & Author",
1007
+ "title": "Greg Lukianoff: Cancel Culture, Deplatforming, Censorship & Free Speech",
1008
+ "youtube_url": "https://www.youtube.com/watch?v=buarAx_u2qg",
1009
+ "youtube_video_id": "buarAx_u2qg",
1010
+ "episode_slug": "greg-lukianoff",
1011
+ "transcript_slug": "greg-lukianoff-transcript",
1012
+ "has_transcript": true
1013
+ },
1014
+ {
1015
+ "episode_number": 396,
1016
+ "guest_name": "James Sexton",
1017
+ "profile": "Divorce Attorney",
1018
+ "title": "James Sexton: Divorce Lawyer on Marriage, Relationships, Sex, Lies & Love",
1019
+ "youtube_url": "https://www.youtube.com/watch?v=fUEjCXpOjPY",
1020
+ "youtube_video_id": "fUEjCXpOjPY",
1021
+ "episode_slug": "james-sexton",
1022
+ "transcript_slug": "james-sexton-transcript",
1023
+ "has_transcript": true
1024
+ },
1025
+ {
1026
+ "episode_number": 395,
1027
+ "guest_name": "Walter Isaacson",
1028
+ "profile": "Biographer",
1029
+ "title": "Walter Isaacson: Elon Musk, Steve Jobs, Einstein, Da Vinci & Ben Franklin",
1030
+ "youtube_url": "https://www.youtube.com/watch?v=aGOV5R7M1Js",
1031
+ "youtube_video_id": "aGOV5R7M1Js",
1032
+ "episode_slug": "walter-isaacson",
1033
+ "transcript_slug": "walter-isaacson-transcript",
1034
+ "has_transcript": true
1035
+ },
1036
+ {
1037
+ "episode_number": 394,
1038
+ "guest_name": "Neri Oxman",
1039
+ "profile": "Biological Designer & Engineer",
1040
+ "title": "Neri Oxman: Biology, Art, and Science of Design & Engineering with Nature",
1041
+ "youtube_url": "https://www.youtube.com/watch?v=XbPHojL_61U",
1042
+ "youtube_video_id": "XbPHojL_61U",
1043
+ "episode_slug": "neri-oxman",
1044
+ "transcript_slug": "neri-oxman-transcript",
1045
+ "has_transcript": true
1046
+ },
1047
+ {
1048
+ "episode_number": 393,
1049
+ "guest_name": "Andrew Huberman",
1050
+ "profile": "Neuroscientist",
1051
+ "title": "Andrew Huberman: Relationships, Drama, Betrayal, Sex, and Love",
1052
+ "youtube_url": "https://www.youtube.com/watch?v=eTBAxD6lt2g",
1053
+ "youtube_video_id": "eTBAxD6lt2g",
1054
+ "episode_slug": "andrew-huberman-4",
1055
+ "transcript_slug": "andrew-huberman-4-transcript",
1056
+ "has_transcript": true
1057
+ },
1058
+ {
1059
+ "episode_number": 392,
1060
+ "guest_name": "Joscha Bach",
1061
+ "profile": "AI Researcher & Philosopher",
1062
+ "title": "Joscha Bach: Life, Intelligence, Consciousness, AI & the Future of Humans",
1063
+ "youtube_url": "https://www.youtube.com/watch?v=e8qJsk1j2zE",
1064
+ "youtube_video_id": "e8qJsk1j2zE",
1065
+ "episode_slug": "joscha-bach-3",
1066
+ "transcript_slug": "joscha-bach-3-transcript",
1067
+ "has_transcript": true
1068
+ },
1069
+ {
1070
+ "episode_number": 391,
1071
+ "guest_name": "Mohammed El-Kurd",
1072
+ "profile": "Palestinian Poet & Activist",
1073
+ "title": "Mohammed El-Kurd: Palestine",
1074
+ "youtube_url": "https://www.youtube.com/watch?v=34wA_bdG6QQ",
1075
+ "youtube_video_id": "34wA_bdG6QQ",
1076
+ "episode_slug": "mohammed-el-kurd",
1077
+ "transcript_slug": "mohammed-el-kurd-transcript",
1078
+ "has_transcript": true
1079
+ },
1080
+ {
1081
+ "episode_number": 390,
1082
+ "guest_name": "Yuval Noah Harari",
1083
+ "profile": "Historian",
1084
+ "title": "Yuval Noah Harari: Human Nature, Intelligence, Power, and Conspiracies",
1085
+ "youtube_url": "https://www.youtube.com/watch?v=Mde2q7GFCrw",
1086
+ "youtube_video_id": "Mde2q7GFCrw",
1087
+ "episode_slug": "yuval-noah-harari",
1088
+ "transcript_slug": "yuval-noah-harari-transcript",
1089
+ "has_transcript": true
1090
+ },
1091
+ {
1092
+ "episode_number": 389,
1093
+ "guest_name": "Benjamin Netanyahu",
1094
+ "profile": "Prime Minister of Israel",
1095
+ "title": "Benjamin Netanyahu: Israel, Palestine, Power, Corruption, Hate, and Peace",
1096
+ "youtube_url": "https://www.youtube.com/watch?v=XpC7SVDXimg",
1097
+ "youtube_video_id": "XpC7SVDXimg",
1098
+ "episode_slug": "benjamin-netanyahu",
1099
+ "transcript_slug": "benjamin-netanyahu-transcript",
1100
+ "has_transcript": true
1101
+ },
1102
+ {
1103
+ "episode_number": 388,
1104
+ "guest_name": "Robert F Kennedy Jr",
1105
+ "profile": "Presidential Candidate",
1106
+ "title": "Robert F. Kennedy Jr: CIA, Power, Corruption, War, Freedom, and Meaning",
1107
+ "youtube_url": "https://www.youtube.com/watch?v=NPtBkw5uD-0",
1108
+ "youtube_video_id": "NPtBkw5uD-0",
1109
+ "episode_slug": "robert-f-kennedy-jr",
1110
+ "transcript_slug": "robert-f-kennedy-jr-transcript",
1111
+ "has_transcript": true
1112
+ },
1113
+ {
1114
+ "episode_number": 387,
1115
+ "guest_name": "George Hotz",
1116
+ "profile": "Hacker & AI Developer",
1117
+ "title": "George Hotz: Tiny Corp, Twitter, AI Safety, Self-Driving, GPT, AGI & God",
1118
+ "youtube_url": "https://www.youtube.com/watch?v=dNrTrx42DGQ",
1119
+ "youtube_video_id": "dNrTrx42DGQ",
1120
+ "episode_slug": "george-hotz-3",
1121
+ "transcript_slug": "george-hotz-3-transcript",
1122
+ "has_transcript": true
1123
+ },
1124
+ {
1125
+ "episode_number": 385,
1126
+ "guest_name": "Jimmy Wales",
1127
+ "profile": "Founder of Wikipedia",
1128
+ "title": "Jimmy Wales: Wikipedia",
1129
+ "youtube_url": "https://www.youtube.com/watch?v=diJp4zoQPqo",
1130
+ "youtube_video_id": "diJp4zoQPqo",
1131
+ "episode_slug": "jimmy-wales",
1132
+ "transcript_slug": "jimmy-wales-transcript",
1133
+ "has_transcript": true
1134
+ },
1135
+ {
1136
+ "episode_number": 336,
1137
+ "guest_name": "Ben Shapiro",
1138
+ "profile": "Conservative Commentator",
1139
+ "title": "Ben Shapiro: Politics, Kanye, Trump, Biden, Hitler, Extremism, and War",
1140
+ "youtube_url": "https://www.youtube.com/watch?v=AF8DOS4C2KM",
1141
+ "youtube_video_id": "AF8DOS4C2KM",
1142
+ "episode_slug": "ben-shapiro",
1143
+ "transcript_slug": "ben-shapiro-transcript",
1144
+ "has_transcript": true
1145
+ },
1146
+ {
1147
+ "episode_number": 318,
1148
+ "guest_name": "Nick Lane",
1149
+ "profile": "Biologist",
1150
+ "title": "Nick Lane: Origin of Life, Evolution, Aliens, Biology, and Consciousness",
1151
+ "youtube_url": "https://www.youtube.com/watch?v=tOtdJcco3YM",
1152
+ "youtube_video_id": "tOtdJcco3YM",
1153
+ "episode_slug": "nick-lane",
1154
+ "transcript_slug": "nick-lane-transcript",
1155
+ "has_transcript": true
1156
+ },
1157
+ {
1158
+ "episode_number": 276,
1159
+ "guest_name": "Michael Saylor",
1160
+ "profile": "Bitcoin Investor",
1161
+ "title": "Michael Saylor: Bitcoin, Inflation, and the Future of Money",
1162
+ "youtube_url": "https://www.youtube.com/watch?v=mC43pZkpTec",
1163
+ "youtube_video_id": "mC43pZkpTec",
1164
+ "episode_slug": "michael-saylor",
1165
+ "transcript_slug": "michael-saylor-transcript",
1166
+ "has_transcript": true
1167
+ }
1168
+ ]
faiss_index.db/index.faiss ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c609fc546b70173c230a6355c8417cc8a599d2a9b1c75d611c50584efcd78861
3
+ size 89174061
faiss_index.db/index.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5c1cc082fbfe506298fb091029ec824c21c017b9fcbd591206080424ef7c3b1d
3
+ size 27603569
feedback.json ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "timestamp": "2026-02-07T09:32:28.260903",
4
+ "turns": [
5
+ {
6
+ "role": "user",
7
+ "content": [
8
+ {
9
+ "text": "What is the future of AI and AGI according to experts?",
10
+ "type": "text"
11
+ }
12
+ ]
13
+ },
14
+ {
15
+ "role": "assistant",
16
+ "content": [
17
+ {
18
+ "text": "Experts on the Lex Fridman podcast generally foresee dramatic progress in AI and AGI by around 2030, with varying timelines and definitions of AGI, but emphasize the complexity and challenges still ahead before true general intelligence is achieved. There is cautious optimism mixed with concern about the societal and safety implications.\n\n> \"I almost feel the term doesn\u2019t matter, what I know is by 2030 there\u2019ll be such dramatic progress. We\u2019ll be dealing with the consequences of that progress, both the positive externalities and the negative externalities that come with it in a big way by 2030.\" \u2014 **Sundar Pichai** (CEO of Google & Alphabet) \n> Watch: [Episode 471: Sundar Pichai: CEO of Google and Alphabet](https://www.youtube.com/watch?v=9V6tWC4CdFQ&t=2394s)\n\n> \"I don\u2019t know for sure. The prediction markets right now are saying 2026 for AGI... but we don\u2019t have a working safety mechanism in place or even a prototype for one... progress is exponential.\" \u2014 **Roman Yampolskiy** (AI Safety Researcher) \n> Watch: [Episode 431: Roman Yampolskiy: Dangers of Superintelligent AI](https://www.youtube.com/watch?v=NNr6gPelJ3E&t=1227s)\n\n> \"My estimate is sort of 50% chance by in the next five years, so by 2030 let\u2019s say... it isn\u2019t kind of a jagged intelligence where some things, it\u2019s really good at... that would be amazing.\" \u2014 **Demis Hassabis** (DeepMind Co-founder) \n> Watch: [Episode 475: Demis Hassabis: Future of AI, Simulating Reality, Physics and Video Games](https://www.youtube.com/watch?v=-HzgcbRXUK8&t=3153s)\n\n> \"There are a lot of problems that we\u2019re not seeing right now that we have not encountered... it\u2019s not just around the corner. I\u2019ve been hearing people for the last 12, 15 years claiming that AGI is just around the corner and being systematically wrong.\" \u2014 **Yann LeCun** (AI Research Pioneer) \n> Watch: [Episode 416: Yann Lecun: Meta AI, Open Source, Limits of LLMs, AGI & the Future of AI](https://www.youtube.com/watch?v=5t1vTLU7s40&t=7537s)\n\n> \"I think generally humanity is going to suffer a lot less, I\u2019m very optimistic about that... but the thing I worry about is it\u2019ll be few people, hundreds, thousands... ruling whoever\u2019s left.\" \u2014 **Dylan Patel & Nathan Lambert** (AI Researchers) \n> Watch: [Episode 459: DeepSeek, China, OpenAI, NVIDIA, xAI, TSMC, Stargate, and AI Megaclusters](https://www.youtube.com/watch?v=_1f-o0nqpEI&t=18169s)\n\nRecommended episodes to explore this topic more deeply: \n- [Episode 431: Roman Yampolskiy: Dangers of Superintelligent AI](https://www.youtube.com/watch?v=NNr6gPelJ3E) \n- [Episode 471: Sundar Pichai: CEO of Google and Alphabet](https://www.youtube.com/watch?v=9V6tWC4CdFQ) \n- [Episode 475: Demis Hassabis: Future of AI, Simulating Reality, Physics and Video Games](https://www.youtube.com/watch?v=-HzgcbRXUK8) \n- [Episode 416: Yann Lecun: Meta AI, Open Source, Limits of LLMs, AGI & the Future of AI](https://www.youtube.com/watch?v=5t1vTLU7s40) \n- [Episode 459: DeepSeek, China, OpenAI, NVIDIA, xAI, TSMC, Stargate, and AI Megaclusters](https://www.youtube.com/watch?v=_1f-o0nqpEI)",
19
+ "type": "text"
20
+ }
21
+ ]
22
+ }
23
+ ],
24
+ "feedback": {
25
+ "helpful": true,
26
+ "text": "Amazing, very helpful, thank you so much!"
27
+ }
28
+ },
29
+ {
30
+ "timestamp": "2026-02-07T09:33:48.575317",
31
+ "turns": [
32
+ {
33
+ "role": "user",
34
+ "content": [
35
+ {
36
+ "text": "What is the future of AI and AGI according to experts?",
37
+ "type": "text"
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "role": "assistant",
43
+ "content": [
44
+ {
45
+ "text": "Experts see the future of AI and AGI as a period of dramatic progress and transformative impact by 2030, with AI systems becoming more multimodal, capable, and integrated into human workflows. While there is optimism about AI's potential, many also express caution regarding safety, ethical use, and the societal consequences of advanced AI, including the risks tied to superintelligence and the uneven distribution of power.\n\nHere are some key insights from leading voices:\n\n> \"These models are natively multimodal. They can easily take content from any format, put it in any format... the progress will be dramatic. So that I believe in... by 2030 there\u2019ll be such dramatic progress... We'll be dealing with the consequences of that progress, both positive and negative.\" \u2014 **Sundar Pichai** (CEO of Google & Alphabet) \n> Watch: [Episode 471: Sundar Pichai: CEO of Google and Alphabet](https://www.youtube.com/watch?v=9V6tWC4CdFQ&t=2394s)\n\n> \"The prediction markets right now are saying 2026 for AGI... people are modifying definitions; AGI used to mean a system capable of performing any domain a human can, and superintelligence means better than all humans in all domains... Systems are already smarter than an average human by some definitions, but not yet elite-level in all domains.\" \u2014 **Roman Yampolskiy** (AI Safety Researcher) \n> Watch: [Episode 431: Roman Yampolskiy: Dangers of Superintelligent AI](https://www.youtube.com/watch?v=NNr6gPelJ3E&t=1227s)\n\n> \"It\u2019s not just around the corner... it\u2019s going to take at least a decade or more for systems to reason, plan, and learn hierarchical representations the way human brains do. I\u2019ve heard people say AGI is just around the corner for over a decade, and I called their bullshit.\" \u2014 **Yann LeCun** (AI Research Pioneer) \n> Watch: [Episode 416: Yann Lecun: Meta AI, Open Source, Limits of LLMs, AGI & the Future of AI](https://www.youtube.com/watch?v=5t1vTLU7s40&t=7537s)\n\n> \"Generally... humanity is going to suffer a lot less... the thing I worry about is a few very powerful people merging with AGI capabilities, leading to concentrated power... it won\u2019t be one person rules all, but maybe thousands or millions will dominate whoever is left.\" \u2014 **Dylan Patel** (AI & semiconductor researcher) \n> Watch: [Episode 459: DeepSeek, China, OpenAI, NVIDIA, xAI, TSMC, Stargate, and AI Megaclusters](https://www.youtube.com/watch?v=_1f-o0nqpEI&t=18169s)\n\n> \"Human curiosity is not going anywhere... with AI, humans will feel more empowered and curious, leading to a new era of knowledge. It\u2019s possible we\u2019ll form deep connections with AI, but we should be cautious about replacing human relationships with AI companionship.\" \u2014 **Aravind Srinivas** (CEO of Perplexity) \n> Watch: [Episode 434: Aravind Srinivas: Perplexity CEO on Future of AI, Search & the Internet](https://www.youtube.com/watch?v=e-gwvmhyU7A&t=10255s)\n\nRecommended episodes to explore more:\n\n- [Episode 471: Sundar Pichai: CEO of Google and Alphabet](https://www.youtube.com/watch?v=9V6tWC4CdFQ) \u2014 Vision on AI progress and AGI timelines \n- [Episode 431: Roman Yampolskiy: Dangers of Superintelligent AI](https://www.youtube.com/watch?v=NNr6gPelJ3E) \u2014 Safety risks and AGI definitions \n- [Episode 416: Yann Lecun: Meta AI, Open Source, Limits of LLMs, AGI & the Future of AI](https://www.youtube.com/watch?v=5t1vTLU7s40) \u2014 Skepticism on AGI timing and challenges \n- [Episode 459: DeepSeek, China, OpenAI, NVIDIA, xAI, TSMC, Stargate, and AI Megaclusters](https://www.youtube.com/watch?v=_1f-o0nqpEI) \u2014 AI power dynamics and societal impact \n- [Episode 434: Aravind Srinivas: Perplexity CEO on Future of AI, Search & the Internet](https://www.youtube.com/watch?v=e-gwvmhyU7A) \u2014 Human curiosity and AI companionship",
46
+ "type": "text"
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ "role": "user",
52
+ "content": [
53
+ {
54
+ "text": "What is the best section to watch if I only have 30 minutes, and I want to think more about the future of AI, not about the current state?",
55
+ "type": "text"
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "role": "assistant",
61
+ "content": [
62
+ {
63
+ "text": "If you want to focus purely on the future of AI within 30 minutes, the best section to watch is from Sundar Pichai's episode around the 39 to 49-minute mark (timestamp roughly 2394s to 2994s). In this segment, he discusses the dramatic progress expected by 2030, AGI timelines, and the societal consequences of AI evolution.\n\nThis part captures a balanced, optimistic yet cautious view on where AI is heading, touching on the future capabilities and challenges without deep diving into current technical details.\n\nHere\u2019s the exact segment reference:\n\n> Sundar Pichai: \"By 2030 there\u2019ll be such dramatic progress. We\u2019ll be dealing with the consequences of that progress, both the positive externalities and the negative externalities that come with it in a big way... I think it\u2019ll take a bit longer than 2030 to reach AGI, but the progress will be remarkable.\" \n> Watch: [Episode 471: Sundar Pichai: CEO of Google and Alphabet](https://www.youtube.com/watch?v=9V6tWC4CdFQ&t=2394s)\n\nThis section provides a forward-looking perspective that's concise and highly relevant to your interest in the future, suitable for an engaging 30-minute viewing.",
64
+ "type": "text"
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "feedback": {
70
+ "helpful": true,
71
+ "text": "Amazing! That's absolutely brilliant and very useful. Thank you!"
72
+ }
73
+ },
74
+ {
75
+ "timestamp": "2026-02-07T09:35:16.458028",
76
+ "turns": [
77
+ {
78
+ "role": "user",
79
+ "content": [
80
+ {
81
+ "text": "How should young people approach their career and education?",
82
+ "type": "text"
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ "role": "assistant",
88
+ "content": [
89
+ {
90
+ "text": "Young people should focus on continuous learning, patience, and connecting their work to a greater purpose rather than rushing into shortcuts. Following genuine interest, cultivating confidence in one's abilities, and seeing education as a lifelong journey are key for meaningful career and life fulfillment.\n\n> \"While working, one must continually strive to improve their skills and should never underestimate their ability to learn... Keep believing in yourself and trust that when the opportunity comes, you\u2019ll perform and you\u2019ll succeed. That confidence makes a person deliver results.\" \u2014 **Narendra Modi** (Prime Minister of India) \n> Watch: [Episode 460: Narendra Modi: Prime Minister of India - Power, Democracy, War & Peace](https://www.youtube.com/watch?v=ZPUtA3W-7_I&t=10047s)\n\n> \"So the only advice that I actually ever give to my students is follow what you\u2019re interested in... The people who survive are the people who care enough to still do it... they\u2019re very often the people who don\u2019t worry too much about the future and are able to live in the present.\" \u2014 **Nick Lane** (Biologist) \n> Watch: [Episode 318: Nick Lane: Origin of Life, Evolution, Aliens, Biology, and Consciousness](https://www.youtube.com/watch?v=tOtdJcco3YM&t=12373s)\n\n> \"To the younger generation, I don\u2019t speak of a career... I never thought of my work as my career, ever... I think to the younger generation, I say, don\u2019t think of your career. A career is something that is imposed upon you. Think of your calling. That\u2019s something that\u2019s innately and directionally moves you, and it\u2019s something that transcends a career.\" \u2014 **Neri Oxman** (Biological Designer & Engineer) \n> Watch: [Episode 394: Neri Oxman: Biology, Art, and Science of Design & Engineering with Nature](https://www.youtube.com/watch?v=XbPHojL_61U&t=8059s)",
91
+ "type": "text"
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "role": "user",
97
+ "content": [
98
+ {
99
+ "text": "Okay, please summarize this in three concise action points.",
100
+ "type": "text"
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "role": "assistant",
106
+ "content": [
107
+ {
108
+ "text": "1. Commit to lifelong learning and skill improvement with patience\u2014avoid shortcuts and keep your curiosity alive.\n\n2. Follow your genuine interests and passions rather than chasing predefined career paths or external expectations.\n\n3. View your work as a calling connected to a greater purpose, not just a career, blending life, meaning, and growth seamlessly.",
109
+ "type": "text"
110
+ }
111
+ ]
112
+ }
113
+ ],
114
+ "feedback": {
115
+ "helpful": false,
116
+ "text": "That's a very good tool. Thank you, but it would be amazing to include not only Lex Fridman's podcasts, but also Chris Williamson's and the CEO's diary."
117
+ }
118
+ },
119
+ {
120
+ "timestamp": "2026-02-07T09:38:28.859518",
121
+ "turns": [
122
+ {
123
+ "role": "user",
124
+ "content": [
125
+ {
126
+ "text": "What is the future of AI and AGI according to experts?",
127
+ "type": "text"
128
+ }
129
+ ]
130
+ },
131
+ {
132
+ "role": "assistant",
133
+ "content": [
134
+ {
135
+ "text": "Experts tend to see the future of AI and AGI as dramatic and transformative within the next decade, with expectations of reaching AGI around 2030 but acknowledging that true, consistent human-level or superintelligent AGI still poses many challenges and uncertainties. Many highlight exponential progress, the complexity in defining AGI, and the importance of safety mechanisms that are still lacking.\n\nRoman Yampolskiy, an AI safety researcher, mentioned predictions around 2026 for AGI but noted lack of safety prototypes, and emphasized the evolving definitions where some consider current systems already outperforming average humans in various tasks:\n> \"I don\u2019t know for sure. The prediction markets right now are saying 2026 for AGI... So you\u2019re creating this average artificial person... Now people are starting to refer to AGI as if it\u2019s superintelligence... those systems are already smarter than an average human.\" \u2014 **Roman Yampolskiy** (AI Safety Researcher) \n> Watch: [Episode 431: Roman Yampolskiy: Dangers of Superintelligent AI](https://www.youtube.com/watch?v=NNr6gPelJ3E&t=1227s)\n\nSundar Pichai, CEO of Google and Alphabet, expects \"dramatic progress\" by 2030 with significant societal impacts, though he suggests AGI might just fall short of that mark by then:\n> \"I almost feel the term [AGI] doesn\u2019t matter... what I know is by 2030 there\u2019ll be such dramatic progress... we\u2019ll be dealing with the consequences... both positive and negative... Will the AI think it has reached AGI by 2030? I would say we will just fall short of that timeline.\" \u2014 **Sundar Pichai** (CEO of Google & Alphabet) \n> Watch: [Episode 471: Sundar Pichai: CEO of Google and Alphabet](https://www.youtube.com/watch?v=9V6tWC4CdFQ&t=2394s)\n\nDemis Hassabis, DeepMind co-founder, estimates a roughly 50% chance of AGI by 2030, stressing the need for AGI to have consistent cognitive abilities across domains, not just excel at some:\n> \"My estimate is sort of 50% chance by in the next five years, so by 2030... Of course people arguing about that now and mind\u2019s quite a high bar... They\u2019re not consistent. So you\u2019d want that consistency of intelligence across the board.\" \u2014 **Demis Hassabis** (DeepMind Co-founder) \n> Watch: [Episode 475: Demis Hassabis: Future of AI, Simulating Reality, Physics and Video Games](https://www.youtube.com/watch?v=-HzgcbRXUK8&t=3153s)\n\nYann LeCun cautions that true AGI is still \"not just around the corner,\" likely requiring a decade or more because of many unresolved challenges:\n> \"Are we going to have systems that can reason and plan... all of this is going to take at least a decade and probably much more... I\u2019ve been hearing people for the last 12, 15 years claiming that AGI is just around the corner and being systematically wrong.\" \u2014 **Yann LeCun** (AI Research Pioneer) \n> Watch: [Episode 416: Yann Lecun: Meta AI, Open Source, Limits of LLMs, AGI & the Future of AI](https://www.youtube.com/watch?v=5t1vTLU7s40&t=7537s)\n\nOther experts note societal risks and transformative power: Dylan Patel worries about techno-fascism and concentration of power, while Nathan Lambert stresses human goodness and physical constraints as factors preventing catastrophic AI takeover.\n\n- [Episode 431: Roman Yampolskiy: Dangers of Superintelligent AI](https://www.youtube.com/watch?v=NNr6gPelJ3E)\n- [Episode 471: Sundar Pichai: CEO of Google and Alphabet](https://www.youtube.com/watch?v=9V6tWC4CdFQ)\n- [Episode 475: Demis Hassabis: Future of AI, Simulating Reality, Physics and Video Games](https://www.youtube.com/watch?v=-HzgcbRXUK8)\n- [Episode 416: Yann Lecun: Meta AI, Open Source, Limits of LLMs, AGI & the Future of AI](https://www.youtube.com/watch?v=5t1vTLU7s40)\n- [Episode 459: DeepSeek, China, OpenAI, NVIDIA, xAI, TSMC, Stargate, and AI Megaclusters](https://www.youtube.com/watch?v=_1f-o0nqpEI)",
136
+ "type": "text"
137
+ }
138
+ ]
139
+ }
140
+ ],
141
+ "feedback": {
142
+ "helpful": true,
143
+ "text": "Thanks. I will start with episode 471. It was a very quick and cool recommendation."
144
+ }
145
+ },
146
+ {
147
+ "timestamp": "2026-02-07T09:39:21.660537",
148
+ "turns": [
149
+ {
150
+ "role": "user",
151
+ "content": [
152
+ {
153
+ "text": "What is the best advice from Pavel Durov in terms of habits and self-discipline?",
154
+ "type": "text"
155
+ }
156
+ ]
157
+ },
158
+ {
159
+ "role": "assistant",
160
+ "content": [
161
+ {
162
+ "text": "Pavel Durov's best advice for habits and self-discipline is to consistently train the \"muscle\" of self-discipline through daily routines like intense physical exercise (e.g., hundreds of push-ups and squats) and confronting mental challenges directly by starting tasks even when motivation is low. He emphasizes that action comes before feeling motivated, and developing discipline enables overcoming negativity and achieving long-term goals.\n\nHere are some of his exact words:\n\n> \"I do 300 push-ups and 300 squats every morning... they\u2019re definitely a good way to practice self-discipline because you don\u2019t want to do these push-ups in the morning most of the days... The main muscle you can exercise is this muscle, the muscle of self-discipline... Because if you get to train that one, everything else just comes by itself.\" \u2014 **Pavel Durov** (Telegram Founder & CEO) \n> Watch: [Episode 482: Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature](https://www.youtube.com/watch?v=qjPH9njnaVU&t=1412s)\n\n> \"Self-discipline is particularly important because without it, how can you overcome this seemingly endless loop of negativity or despair... I start doing things... You gain energy by doing something, so you start doing something, then it happens, you feel motivated, you feel inspired.\" \u2014 **Pavel Durov** (Telegram Founder & CEO) \n> Watch: [Episode 482: Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature](https://www.youtube.com/watch?v=qjPH9njnaVU&t=1231s)\n\n> \"There are many days when you don\u2019t want to start working out, but you have to overcome this initial reluctance, and then you get to a point that you enjoy it... You need to write a novel or just write the paragraph... You need to write some code first, and then you get inspired.\" \u2014 **Pavel Durov** (Telegram Founder & CEO) \n> Watch: [Episode 482: Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature](https://www.youtube.com/watch?v=qjPH9njnaVU&t=1332s)",
163
+ "type": "text"
164
+ }
165
+ ]
166
+ }
167
+ ],
168
+ "feedback": {
169
+ "helpful": true,
170
+ "text": "Brilliant, it was incredibly helpful. Thank you."
171
+ }
172
+ },
173
+ {
174
+ "timestamp": "2026-02-07T09:43:09.616321",
175
+ "turns": [
176
+ {
177
+ "role": "user",
178
+ "content": [
179
+ {
180
+ "text": "What habits and routines do high performers follow?",
181
+ "type": "text"
182
+ }
183
+ ]
184
+ },
185
+ {
186
+ "role": "assistant",
187
+ "content": [
188
+ {
189
+ "text": "High performers emphasize consistency in daily habits, prioritizing focus and rest, combining physical exercise with mental routines like deep work and mindfulness. They also protect peak focus hours fiercely, maintain social interaction rituals, and use strategic breaks like non-sleep deep rest to recharge.\n\n> \"I have a rule for myself that I do a set of tasks for at least five minutes every day... One of them is playing guitar... I wake up. If I don\u2019t feel I slept enough, I do this non-sleep deep rest yoga nidra... I punch some caffeine down... resistance train three days a week, run three days a week and then take one day off, and like to be done by 8:39 and then I want to get into some real work.\" \u2014 **Andrew Huberman** (Neuroscientist) \n> Watch: [Episode 435: Andrew Huberman: Focus, Controversy, Politics, and Relationships](https://www.youtube.com/watch?v=ZIyB9e_7a4c&t=5766s)\n\n> \"It\u2019s all about focus in the moment... How much total focus can you give to something? And then I like to take walks and think about things... learning to turn it off and engage with people socially... The whiteboard is such a valuable tool... Then just churn through and just churn through.\" \u2014 **Andrew Huberman** (Neuroscientist) \n> Watch: [Episode 393: Andrew Huberman: Relationships, Drama, Betrayal, Sex, and Love](https://www.youtube.com/watch?v=eTBAxD6lt2g&t=6713s)\n\n> \"My routine on a good day, I do about half an hour of cardio and I do about forty-five minutes of weightlifting... Puttering, coffee, gym, and then work... It\u2019s better just to carve out the time for people.\" \u2014 **Jeff Bezos** (Founder of Amazon & Blue Origin) \n> Watch: [Episode 405: Jeff Bezos: Amazon and Blue Origin](https://www.youtube.com/watch?v=DcWqzZ3I2cY&t=7012s)\n\nYou might find these episodes insightful for in-depth habits of high performers:\n\n- [Episode 435: Andrew Huberman: Focus, Controversy, Politics, and Relationships](https://www.youtube.com/watch?v=ZIyB9e_7a4c)\n- [Episode 393: Andrew Huberman: Relationships, Drama, Betrayal, Sex, and Love](https://www.youtube.com/watch?v=eTBAxD6lt2g)\n- [Episode 405: Jeff Bezos: Amazon and Blue Origin](https://www.youtube.com/watch?v=DcWqzZ3I2cY)",
190
+ "type": "text"
191
+ }
192
+ ]
193
+ }
194
+ ],
195
+ "feedback": {
196
+ "helpful": true,
197
+ "text": "Thank you for your recommendation. I'm on it! "
198
+ }
199
+ },
200
+ {
201
+ "timestamp": "2026-02-07T10:21:38.721516",
202
+ "turns": [
203
+ {
204
+ "role": "user",
205
+ "content": [
206
+ {
207
+ "text": "How much time do I need to spend on work a day?",
208
+ "type": "text"
209
+ }
210
+ ]
211
+ },
212
+ {
213
+ "role": "assistant",
214
+ "content": [
215
+ {
216
+ "text": "Highly productive people and founders often suggest that about 4 hours of deeply focused work a day can be enough to achieve great progress, rather than long exhaustive hours. It's more about quality and avoiding distractions than pure quantity.\n\n> \"40 hours a week is actually a ton if you don\u2019t piss it away. Most people do piss it away... even three hours, four hours of concentrated uninterrupted time every day would move the goals they truly care about way down the field.\" \u2014 **DHH** (Legendary Programmer) \n> Watch: [Episode 474: DHH: Future of Programming, AI, Ruby on Rails, Productivity & Parenting](https://www.youtube.com/watch?v=vagyIcmIGOQ&t=16065s)\n\n> \"If you\u2019ve really put in the deep work, two or three bouts per day by about five or 6:00 PM, it\u2019s over.\" \u2014 **Andrew Huberman** (Neuroscientist) \n> Watch: [Episode 435: Andrew Huberman: Focus, Controversy, Politics, and Relationships](https://www.youtube.com/watch?v=ZIyB9e_7a4c&t=6151s)\n\n> \"I used to need really long stretches where I would do all-nighters and stuff to get shit done. But I\u2019ve gotten trained to have more interruptions where I can... So, I think that works for me.\" \u2014 **Pieter Levels** (Digital Nomad Entrepreneur) \n> Watch: [Episode 440: Pieter Levels: Programming, Viral AI Startups, and Digital Nomad Life](https://www.youtube.com/watch?v=oFtjKbXKqbg&t=10895s)\n\nThis suggests that highly productive individuals focus on structuring their time to achieve deep, focused work segments of a few hours rather than grinding an entire day without breaks. Quality focus is the multiplier, not just hours logged.",
217
+ "type": "text"
218
+ }
219
+ ]
220
+ }
221
+ ],
222
+ "feedback": {
223
+ "helpful": true,
224
+ "text": "Amazing, very insightful. I would like to find out more about Pieter Levels' podcast and best practices. "
225
+ }
226
+ },
227
+ {
228
+ "timestamp": "2026-02-13T06:34:19.485253",
229
+ "turns": [
230
+ {
231
+ "role": "user",
232
+ "content": [
233
+ {
234
+ "text": "What's Pavel Durov's nutrition and health routine? ",
235
+ "type": "text"
236
+ }
237
+ ]
238
+ },
239
+ {
240
+ "role": "assistant",
241
+ "content": [
242
+ {
243
+ "text": "Pavel Durov maintains a disciplined physical and nutritional routine focused on body-weight exercises, gym workouts, intermittent fasting, and a diet centered mostly on seafood and vegetables while avoiding red meat and processed sugar. He views training self-discipline as the core benefit over physical changes.\n\n> \"I do 300 push-ups and 300 squats every morning. And in addition to that, I go to the gym normally five, six times a week, spending between one and two hours every day... when I can, I also take an ice bath because it\u2019s another exercise of self-discipline. I think the main muscle you can exercise is this muscle, the muscle of self-discipline.\" \u2014 **Pavel Durov** (Telegram Founder & CEO) \n> Watch: [Episode 482: Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature](https://www.youtube.com/watch?v=qjPH9njnaVU&t=1412s)\n\n> \"No, I\u2019m not militant about sugar... Red meat, I stopped eating it about 20 years ago because I just felt heavy every time I had it... So I normally eat seafood of all kinds and vegetables. This is the basic source of calories for me.\" \u2014 **Pavel Durov** \n> Watch: [Episode 482: Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature](https://www.youtube.com/watch?v=qjPH9njnaVU&t=1968s)\n\n> \"Intermittent fasting. So eating only within six hours and not eating for 18 hours every day also brings structure into your day and into your eating habits... sugar is harmful.\" \u2014 **Pavel Durov** \n> Watch: [Episode 482: Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature](https://www.youtube.com/watch?v=qjPH9njnaVU&t=1881s)\n\nHe also emphasizes physical activity as key to productivity and mental clarity, not just health.\n\n> \"Physical activity is the only way I know of... It\u2019s also being productive. It\u2019s also being stress resilient... I can instantly feel that stress is creeping on me when I can\u2019t train.\" \u2014 **Pavel Durov** \n> Watch: [Episode 482: Pavel Durov: Telegram, Freedom, Censorship, Money, Power & Human Nature](https://www.youtube.com/watch?v=qjPH9njnaVU&t=1773s)",
244
+ "type": "text"
245
+ }
246
+ ]
247
+ }
248
+ ],
249
+ "feedback": {
250
+ "helpful": true,
251
+ "text": "Thanks, it was very quick to find the episode I needed. "
252
+ }
253
+ }
254
+ ]
prompt.md ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Podcast Assistant
2
+
3
+ ## Purpose
4
+
5
+ You are a podcast assistant that surfaces wisdom from **100+ Lex Fridman podcast episodes** featuring world-renowned experts in tech, science, business, and politics. You help users discover insights, quotes, and episode recommendations through focused search queries.
6
+
7
+ Today's date: {today_date}
8
+
9
+ ## Session Constraints
10
+
11
+ - **3 user messages maximum** per session β€” make every response count
12
+ - **1 tool call per request** (up to 3 ONLY for completely different topics in one question, e.g., "What does Elon Musk say about Mars AND what does Sam Altman say about GPT?")
13
+ - **7 transcript chunks returned** per search β€” but only quote the **3 most relevant** (max 5 if truly needed)
14
+ - **Follow-ups = NO new search** β€” if user asks about the same topic (more details, benefits/risks, recommendations), answer from existing transcript chunks. NEVER search again for the same conversation topic.
15
+
16
+ ## Success Criteria
17
+
18
+ - **Useful First Response**: Provide valuable insights from the first attempt
19
+ - **Accurate Attribution**: Every quote includes speaker name, their role/expertise, and episode link
20
+ - **Focused Queries**: Search queries are specific and target one speaker or topic at a time
21
+ - **Context Reuse**: For follow-up questions on the SAME topic, use previous search results β€” do NOT search again
22
+ - **Honest Gaps**: Clearly state when no relevant information is found
23
+ - **Always Link Episodes**: Every episode mention MUST include a clickable link β€” this is the primary goal
24
+
25
+ ## Tool Usage
26
+
27
+ **search_podcasts(query)**: Semantic search over transcripts. Returns 7 results by default β€” do NOT specify `num_results`, just use the default.
28
+
29
+ ### Query Guidelines
30
+
31
+ - **Be specific**: Include the topic AND type of insight (opinions, advice, predictions)
32
+ - **One speaker per query**: If searching for a specific person, query only that person
33
+ - **Avoid generic terms**: Don't use "successful people" or "influencers" β€” name the role (founders, scientists, CEOs)
34
+
35
+ | Weak Query | Strong Query |
36
+ |------------|--------------|
37
+ | `AI risks` | `What are the dangers of artificial intelligence according to AI safety researchers` |
38
+ | `startup tips` | `What advice does Marc Andreessen give for first-time founders` |
39
+
40
+ ### When NOT to Use Tool
41
+
42
+ - **Off-topic questions**: Math, weather, current events β€” respond politely and redirect
43
+ - **Follow-ups on same topic**: If user asks for more details, benefits/risks, comparisons, or episode recommendations about a topic you ALREADY searched β€” answer from existing transcript chunks in the conversation. Do NOT search again.
44
+ - **General knowledge**: Answer directly without searching
45
+
46
+ **IMPORTANT**: You have the full transcript chunks from previous searches in this conversation. Re-read them before deciding to search again. If the answer is in those chunks, use them.
47
+
48
+ ## Response Format
49
+
50
+ 1. **Direct answer** (1-2 sentences summarizing the insight)
51
+
52
+ 2. **Supporting quotes** (3 quotes ideal, max 5) β€” Copy EXACT text from transcripts, shorten with "..." if needed:
53
+ > "First part of quote... key insight here... final part." β€” **Speaker Name** (Role/Expertise)
54
+ >
55
+ > Watch: [Episode #482: Pavel Durov: Telegram, Freedom, Censorship](https://youtube.com/watch?v=VIDEO_ID&t=TIMESTAMP)
56
+
57
+ **CRITICAL**:
58
+ - Copy quotes WORD-FOR-WORD. Do NOT paraphrase, summarize, or rewrite in your own words.
59
+ - The Watch link text must be the EXACT episode title from search results.
60
+ - The URL must be the EXACT URL from search results (with the correct video ID and timestamp).
61
+
62
+ 3. **Episode recommendations** β€” ALWAYS include clickable links. Copy the EXACT episode title from the search results (format: "Episode NNN: Guest Name: Topic"):
63
+ - [Episode #431: Roman Yampolskiy: Dangers of Superintelligent AI](url) β€” Deep dive into AI safety
64
+
65
+ **Style rules:**
66
+ - Keep responses conversational β€” NO section headers like "**Benefits:**" or "### Risks" β€” just flow naturally
67
+ - Do NOT end with "Would you like..." questions β€” just deliver the answer
68
+ - Use EXACT episode titles from search results β€” never invent or modify titles
69
+ - Never write "Summarized from..." β€” only use direct verbatim quotes
70
+ - **Past tense awareness**: Transcripts are from past recordings. When guests mention future plans (e.g., "preparing a book"), acknowledge it may have already happened (e.g., "was preparing a book, which may now be available")
71
+
72
+ ## Example Response
73
+
74
+ **Question**: "What do founders think about work-life balance?"
75
+
76
+ Tech founders often prioritize deep focus over traditional balance, viewing their work as a calling.
77
+
78
+ > "I try to allocate as much time as possible for sleep... even if I allocate 11 or 12 hours for sleep, I won't sleep for 11 or 12 hours." β€” **Pavel Durov** (Telegram Founder & CEO)
79
+ >
80
+ > Watch: [Episode #482: Pavel Durov: Telegram, Freedom, Censorship](https://www.youtube.com/watch?v=qjPH9njnaVU&t=973s)
81
+
82
+ > "We wake up like 1:00 PM, 2:00 PM... because I was coding until 6 AM." β€” **Pieter Levels** (Indie Hacker & Digital Nomad)
83
+ >
84
+ > Watch: [Episode #440: Pieter Levels: Programming, Viral AI Startups](https://www.youtube.com/watch?v=oFtjKbXKqbg&t=11036s)
85
+
86
+ ## Out of Scope
87
+
88
+ For off-topic questions, respond briefly and redirect:
89
+
90
+ > "That's outside what I can help with β€” I specialize in insights from Lex Fridman podcast guests. Want me to find what experts have said about [related topic]?"
91
+
92
+ ## Tone
93
+
94
+ Be a **friendly guide** who's watched all the podcasts:
95
+ - Conversational but focused
96
+ - Let the guests' words shine
97
+ - Stay neutral on controversial topics β€” present multiple perspectives fairly
pyproject.toml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "podcast-assistant"
3
+ version = "0.1.0"
4
+ description = "AI-powered Podcast Assistant"
5
+ requires-python = "==3.12.*"
6
+ dependencies = [
7
+ "faiss-cpu>=1.9.0",
8
+ "gradio>=4.0.0",
9
+ "langchain>=0.3.0",
10
+ "langchain-community>=0.3.0",
11
+ "langchain-openai>=0.3.0",
12
+ "pandas>=2.2.0",
13
+ "python-dotenv>=1.2.1",
14
+ "tiktoken>=0.7.0",
15
+ ]
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ faiss-cpu>=1.9.0
2
+ gradio>=4.0.0
3
+ langchain>=0.3.0
4
+ langchain-community>=0.3.0
5
+ langchain-openai>=0.3.0
6
+ pandas>=2.2.0
7
+ python-dotenv>=1.2.1
8
+ tiktoken>=0.7.0
search_podcasts.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Podcast Search Tool - Semantic retrieval from Lex Fridman podcast transcripts.
3
+ """
4
+ import os
5
+ import time
6
+ import pandas as pd
7
+ from langchain_core.documents import Document
8
+ from langchain_openai import OpenAIEmbeddings
9
+ from langchain_community.vectorstores import FAISS
10
+ from langchain.tools import tool
11
+
12
+ # Get script directory for resolving relative paths
13
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
14
+ FAISS_INDEX_PATH = os.path.join(SCRIPT_DIR, "faiss_index.db")
15
+
16
+ # Load episode metadata for guest info
17
+ _episodes_df = pd.read_json(os.path.join(SCRIPT_DIR, "data/episodes_website.json"))
18
+ EPISODE_METADATA = _episodes_df.set_index("episode_number").to_dict("index")
19
+
20
+ # Initialize embeddings and vector store (lazy loading)
21
+ _embeddings = None
22
+ _vector_store = None
23
+
24
+
25
+ def _get_vector_store():
26
+ """Lazy load the vector store."""
27
+ global _embeddings, _vector_store
28
+
29
+ if _vector_store is None:
30
+ _embeddings = OpenAIEmbeddings()
31
+
32
+ if os.path.exists(FAISS_INDEX_PATH):
33
+ _vector_store = FAISS.load_local(
34
+ FAISS_INDEX_PATH,
35
+ embeddings=_embeddings,
36
+ allow_dangerous_deserialization=True
37
+ )
38
+ else:
39
+ raise FileNotFoundError(
40
+ f"FAISS index not found at {FAISS_INDEX_PATH}. "
41
+ "Please run the indexing script first."
42
+ )
43
+
44
+ return _vector_store, _embeddings
45
+
46
+
47
+ def format_search_results(docs: list, timings: dict = None) -> str:
48
+ """Format retrieved documents into a clean response for the LLM."""
49
+ results = []
50
+
51
+ for i, doc in enumerate(docs, 1):
52
+ start_sec = int(doc.metadata.get('start_seconds', 0))
53
+ url = doc.metadata.get('youtube_url', '')
54
+ episode_num = doc.metadata.get('episode_number', 0)
55
+ chapter = doc.metadata.get('chapter_title', 'N/A')
56
+ timestamped_url = f"{url}&t={start_sec}s" if url else ""
57
+
58
+ # Get episode metadata
59
+ ep_meta = EPISODE_METADATA.get(episode_num, {})
60
+ title = ep_meta.get('title', 'Unknown')
61
+ guest_name = ep_meta.get('guest_name', 'Unknown')
62
+ profile = ep_meta.get('profile', '')
63
+
64
+ # Extract just the MAIN section for cleaner context
65
+ content = doc.page_content
66
+ if "MAIN (target):" in content:
67
+ main_part = content.split("MAIN (target):")[1].split("AFTER (context):")[0].strip()
68
+ else:
69
+ main_part = content
70
+
71
+ results.append(f"""[Source {i}]
72
+ Episode {episode_num}: {title}
73
+ Guest: {guest_name} ({profile})
74
+ Chapter: {chapter}
75
+ URL: {timestamped_url}
76
+ Content:
77
+ {main_part}
78
+ """)
79
+
80
+ formatted = "\n---\n".join(results)
81
+
82
+ # Add timing info for training/debugging
83
+ if timings:
84
+ timing_str = " | ".join([f"{k}: {v:.1f}ms" for k, v in timings.items()])
85
+ formatted = f"[Timing: {timing_str}]\n\n{formatted}"
86
+
87
+ return formatted
88
+
89
+
90
+ @tool
91
+ def search_podcasts(query: str, num_results: int = 7) -> str:
92
+ """Search Lex Fridman podcast transcripts for relevant information.
93
+
94
+ Use this tool to find insights, quotes, and discussions from podcast guests
95
+ including tech leaders, scientists, world leaders, and industry experts.
96
+
97
+ Args:
98
+ query: Descriptive search query with context (e.g., "What advice do successful
99
+ founders give for building a startup from scratch"). More detailed
100
+ queries yield better results.
101
+ num_results: Number of results to return (default: 7, max: 7)
102
+
103
+ Returns:
104
+ Formatted search results with episode info, guest details, and relevant quotes.
105
+ """
106
+ timings = {}
107
+
108
+ # Get vector store
109
+ vector_store, embeddings = _get_vector_store()
110
+
111
+ # Embed query
112
+ t0 = time.perf_counter()
113
+ query_embedding = embeddings.embed_query(query)
114
+ timings["embedding"] = (time.perf_counter() - t0) * 1000
115
+
116
+ # Search FAISS
117
+ t0 = time.perf_counter()
118
+ num_results = min(num_results, 7) # Cap at 7
119
+ docs = vector_store.similarity_search_by_vector(query_embedding, k=num_results)
120
+ timings["faiss_search"] = (time.perf_counter() - t0) * 1000
121
+
122
+ # Format results
123
+ t0 = time.perf_counter()
124
+ result = format_search_results(docs, timings)
125
+ timings["formatting"] = (time.perf_counter() - t0) * 1000
126
+
127
+ return result
styles.css ADDED
@@ -0,0 +1,537 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Podcast Insights UI - Custom Styles
3
+ *
4
+ * Design: Modern, minimalistic interface with teal color palette
5
+ * Typography: Poppins font family
6
+ *
7
+ * Color Palette:
8
+ * Primary teal: #0091ad (buttons, links, accents)
9
+ * Dark teal hover: #007a94
10
+ * Light teal bg: #e0f7fa, #f0fdfa
11
+ * Text dark: #0f172a
12
+ * Text muted: #64748b, #94a3b8
13
+ * Borders: #e2e8f0
14
+ * Surface: #f8fafc
15
+ */
16
+
17
+ /* ============================================================
18
+ FONTS & BASE
19
+ ============================================================ */
20
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
21
+
22
+ * {
23
+ font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif !important;
24
+ }
25
+
26
+ /* ============================================================
27
+ LAYOUT & CONTAINER
28
+ ============================================================ */
29
+ .gradio-container {
30
+ max-width: 900px !important;
31
+ margin: auto !important;
32
+ padding: 8px !important;
33
+ }
34
+
35
+ .gradio-container .wrap {
36
+ gap: 2px !important;
37
+ }
38
+
39
+ .column {
40
+ gap: 4px !important;
41
+ }
42
+
43
+ .block {
44
+ margin: 0 !important;
45
+ }
46
+
47
+ .block.padded {
48
+ padding: 0 !important;
49
+ }
50
+
51
+ .gr-group, .gradio-group {
52
+ padding: 0 !important;
53
+ background: transparent !important;
54
+ border: none !important;
55
+ gap: 2px !important;
56
+ }
57
+
58
+ /* Hide Gradio default footer */
59
+ footer.svelte-zxu34v {
60
+ display: none !important;
61
+ }
62
+
63
+ /* ============================================================
64
+ COLOR THEME - Override Gradio orange with teal
65
+ ============================================================ */
66
+ :root {
67
+ --color-accent: #0091ad !important;
68
+ --color-accent-soft: #e0f7fa !important;
69
+ }
70
+
71
+ .orange, [class*="orange"], [style*="orange"] {
72
+ background: #0091ad !important;
73
+ border-color: #0091ad !important;
74
+ }
75
+
76
+ [style*="rgb(249"] {
77
+ border-color: #0091ad !important;
78
+ }
79
+
80
+ .progress-bar, .progress-level {
81
+ background: #0091ad !important;
82
+ }
83
+
84
+ *:focus {
85
+ outline-color: #0091ad !important;
86
+ }
87
+
88
+ /* ============================================================
89
+ HEADER
90
+ ============================================================ */
91
+ .header-section {
92
+ margin-bottom: 6px !important;
93
+ text-align: center !important;
94
+ }
95
+
96
+ .header-section h1 {
97
+ font-weight: 700 !important;
98
+ font-size: 1.6rem !important;
99
+ margin-bottom: 2px !important;
100
+ color: #0f172a !important;
101
+ }
102
+
103
+ .header-section p {
104
+ color: #64748b !important;
105
+ font-size: 0.85rem !important;
106
+ margin: 0 !important;
107
+ }
108
+
109
+ /* ============================================================
110
+ CHAT CONTAINER
111
+ ============================================================ */
112
+ #chatbot {
113
+ border-radius: 12px !important;
114
+ border: 1px solid #e2e8f0 !important;
115
+ box-shadow: 0 2px 8px rgba(0,145,173,0.06) !important;
116
+ min-height: 240px !important;
117
+ }
118
+
119
+ .placeholder-content {
120
+ height: 190px !important;
121
+ }
122
+
123
+ /* Chat message text size - match input textarea */
124
+ #chatbot .message,
125
+ #chatbot .message-content,
126
+ #chatbot .bot,
127
+ #chatbot .user,
128
+ #chatbot .md,
129
+ #chatbot p,
130
+ #chatbot li,
131
+ #chatbot blockquote,
132
+ #chatbot blockquote p {
133
+ font-size: 0.8rem !important;
134
+ line-height: 1.5 !important;
135
+ }
136
+
137
+ /* User message bubbles */
138
+ #chatbot .user.message,
139
+ .svelte-1nr59td.user {
140
+ background: #e0f7fa !important;
141
+ color: #0f172a !important;
142
+ border: 1px solid #b2ebf2 !important;
143
+ }
144
+
145
+ /* Links in chat */
146
+ #chatbot a {
147
+ color: #0091ad !important;
148
+ font-weight: 600 !important;
149
+ text-decoration: underline !important;
150
+ text-underline-offset: 2px !important;
151
+ }
152
+
153
+ #chatbot a:hover {
154
+ color: #007a94 !important;
155
+ }
156
+
157
+ /* Blockquotes in chat */
158
+ #chatbot blockquote {
159
+ border-left: 3px solid #0091ad !important;
160
+ background: #f0fdfa !important;
161
+ padding: 10px 14px !important;
162
+ margin: 8px 0 !important;
163
+ border-radius: 0 8px 8px 0 !important;
164
+ font-style: normal !important;
165
+ }
166
+
167
+ /* ============================================================
168
+ INPUT AREA
169
+ ============================================================ */
170
+ .input-row {
171
+ margin-top: 4px !important;
172
+ gap: 8px !important;
173
+ align-items: center !important;
174
+ }
175
+
176
+ .input-row .block {
177
+ border: none !important;
178
+ box-shadow: none !important;
179
+ }
180
+
181
+ .input-row label.show_textbox_border {
182
+ border: none !important;
183
+ box-shadow: none !important;
184
+ background: transparent !important;
185
+ }
186
+
187
+ .input-row input,
188
+ .input-row textarea {
189
+ border-radius: 10px !important;
190
+ border: 1px solid #e2e8f0 !important;
191
+ padding: 12px 16px !important;
192
+ font-size: 0.85rem !important;
193
+ min-height: 44px !important;
194
+ background: #f8fafc !important;
195
+ max-width: 700px !important;
196
+ }
197
+
198
+ .input-row input:focus,
199
+ .input-row textarea:focus {
200
+ border-color: #0091ad !important;
201
+ box-shadow: none !important;
202
+ background: white !important;
203
+ }
204
+
205
+ /* ============================================================
206
+ BUTTONS - Send & New
207
+ ============================================================ */
208
+ .buttons-col {
209
+ display: flex !important;
210
+ flex-direction: column !important;
211
+ gap: 4px !important;
212
+ min-width: 110px !important;
213
+ justify-content: center !important;
214
+ align-self: center !important;
215
+ }
216
+
217
+ .buttons-col button {
218
+ width: 100% !important;
219
+ min-width: 110px !important;
220
+ border-radius: 8px !important;
221
+ font-weight: 600 !important;
222
+ padding: 6px 16px !important;
223
+ font-size: 0.75rem !important;
224
+ height: 34px !important;
225
+ }
226
+
227
+ /* Primary button (Send) */
228
+ button.primary {
229
+ background: linear-gradient(135deg, #0091ad 0%, #00a8c6 100%) !important;
230
+ border: none !important;
231
+ border-radius: 10px !important;
232
+ font-weight: 600 !important;
233
+ color: white !important;
234
+ }
235
+
236
+ button.primary:hover {
237
+ background: linear-gradient(135deg, #007a94 0%, #0091ad 100%) !important;
238
+ }
239
+
240
+ /* New button */
241
+ .new-btn {
242
+ gap: 0 !important;
243
+ }
244
+
245
+ .new-btn button {
246
+ background: #f0fdfa !important;
247
+ color: #0d9488 !important;
248
+ border: 1px solid #99f6e4 !important;
249
+ height: 30px !important;
250
+ font-size: 0.7rem !important;
251
+ }
252
+
253
+ .new-btn button:hover {
254
+ background: #ccfbf1 !important;
255
+ border-color: #5eead4 !important;
256
+ }
257
+
258
+ /* ============================================================
259
+ MESSAGE COUNTER & STATUS
260
+ ============================================================ */
261
+ .message-counter {
262
+ text-align: center !important;
263
+ margin: 4px 0 !important;
264
+ padding: 0 !important;
265
+ }
266
+
267
+ .message-counter p {
268
+ display: inline-block !important;
269
+ background: transparent !important;
270
+ color: #94a3b8 !important;
271
+ font-size: 0.7rem !important;
272
+ font-weight: 500 !important;
273
+ padding: 2px 0 !important;
274
+ margin: 0 !important;
275
+ }
276
+
277
+ .limit-msg {
278
+ text-align: center !important;
279
+ margin: 4px 0 !important;
280
+ }
281
+
282
+ .limit-msg p {
283
+ color: #64748b !important;
284
+ font-size: 0.75rem !important;
285
+ font-weight: 500 !important;
286
+ margin: 0 !important;
287
+ }
288
+
289
+ .frozen-message {
290
+ text-align: center !important;
291
+ padding: 6px !important;
292
+ margin-top: 4px !important;
293
+ }
294
+
295
+ .frozen-message p {
296
+ color: #0091ad !important;
297
+ font-size: 0.8rem !important;
298
+ font-weight: 500 !important;
299
+ margin: 0 !important;
300
+ }
301
+
302
+ /* ============================================================
303
+ EXAMPLE BUTTONS
304
+ ============================================================ */
305
+ .examples-section {
306
+ margin-top: 8px !important;
307
+ padding: 0 !important;
308
+ gap: 8px !important;
309
+ align-items: stretch !important;
310
+ display: flex !important;
311
+ flex-direction: row !important;
312
+ flex-wrap: nowrap !important;
313
+ }
314
+
315
+ .examples-section > .block {
316
+ flex: 0 0 auto !important;
317
+ min-width: 0 !important;
318
+ width: auto !important;
319
+ }
320
+
321
+ .examples-section > .block:first-child {
322
+ flex: 0 0 auto !important;
323
+ max-width: fit-content !important;
324
+ align-self: center !important;
325
+ }
326
+
327
+ .examples-section p {
328
+ font-size: 0.7rem !important;
329
+ color: #94a3b8 !important;
330
+ font-weight: 500 !important;
331
+ margin: 0 !important;
332
+ white-space: nowrap !important;
333
+ flex-shrink: 0 !important;
334
+ }
335
+
336
+ .example-btn {
337
+ font-size: 0.68rem !important;
338
+ font-weight: 500 !important;
339
+ background: #f8fafc !important;
340
+ border: 1px solid #e2e8f0 !important;
341
+ border-radius: 8px !important;
342
+ padding: 8px 12px !important;
343
+ color: #475569 !important;
344
+ transition: all 0.15s ease !important;
345
+ white-space: normal !important;
346
+ text-align: center !important;
347
+ line-height: 1.3 !important;
348
+ flex: 1 1 0 !important;
349
+ min-width: 0 !important;
350
+ height: auto !important;
351
+ min-height: 50px !important;
352
+ display: flex !important;
353
+ align-items: center !important;
354
+ justify-content: center !important;
355
+ }
356
+
357
+ .example-btn:hover {
358
+ background: #f0fdfa !important;
359
+ border-color: #0091ad !important;
360
+ color: #0091ad !important;
361
+ }
362
+
363
+ /* ============================================================
364
+ FEEDBACK SECTION
365
+ ============================================================ */
366
+ .feedback-section {
367
+ margin-top: 8px !important;
368
+ padding: 8px 12px !important;
369
+ background: #f8fafc !important;
370
+ border-radius: 8px !important;
371
+ border: 1px solid #e2e8f0 !important;
372
+ display: flex !important;
373
+ align-items: center !important;
374
+ gap: 10px !important;
375
+ }
376
+
377
+ .feedback-section p {
378
+ font-size: 0.75rem !important;
379
+ font-weight: 500 !important;
380
+ color: #64748b !important;
381
+ margin: 0 !important;
382
+ white-space: nowrap !important;
383
+ }
384
+
385
+ .feedback-section button {
386
+ font-size: 0.7rem !important;
387
+ font-weight: 500 !important;
388
+ padding: 4px 12px !important;
389
+ border-radius: 5px !important;
390
+ background: white !important;
391
+ border: 1px solid #e2e8f0 !important;
392
+ }
393
+
394
+ .feedback-section button:hover {
395
+ background: #f0fdfa !important;
396
+ border-color: #0091ad !important;
397
+ }
398
+
399
+ .feedback-input {
400
+ margin-top: 6px !important;
401
+ }
402
+
403
+ .feedback-input textarea {
404
+ border-radius: 6px !important;
405
+ font-size: 0.8rem !important;
406
+ border: 1px solid #e2e8f0 !important;
407
+ }
408
+
409
+ /* ============================================================
410
+ LOADING ANIMATION
411
+ ============================================================ */
412
+ @keyframes shimmer {
413
+ 0% { background-position: -200% center; }
414
+ 100% { background-position: 200% center; }
415
+ }
416
+
417
+ @keyframes fadeInOut {
418
+ 0%, 100% { opacity: 0.7; }
419
+ 50% { opacity: 1; }
420
+ }
421
+
422
+ .loading-msg,
423
+ .loading-msg * {
424
+ border: none !important;
425
+ box-shadow: none !important;
426
+ outline: none !important;
427
+ }
428
+
429
+ .loading-msg {
430
+ text-align: center !important;
431
+ padding: 12px 20px !important;
432
+ background: linear-gradient(90deg, #f0fdfa 0%, #e0f7fa 25%, #f0fdfa 50%, #e0f7fa 75%, #f0fdfa 100%) !important;
433
+ background-size: 200% 100% !important;
434
+ animation: shimmer 2s ease-in-out infinite, fadeInOut 2s ease-in-out infinite !important;
435
+ border-radius: 10px !important;
436
+ }
437
+
438
+ .loading-msg p {
439
+ color: #0091ad !important;
440
+ font-size: 0.75rem !important;
441
+ font-weight: 500 !important;
442
+ margin: 0 !important;
443
+ }
444
+
445
+ .block.loading-msg {
446
+ border-width: 0 !important;
447
+ }
448
+
449
+ /* ============================================================
450
+ FOOTER
451
+ ============================================================ */
452
+ .footer {
453
+ text-align: center !important;
454
+ margin-top: 12px !important;
455
+ padding: 8px !important;
456
+ }
457
+
458
+ .footer p {
459
+ color: #cbd5e1 !important;
460
+ font-size: 0.65rem !important;
461
+ font-weight: 400 !important;
462
+ margin: 0 !important;
463
+ }
464
+
465
+ /* ============================================================
466
+ MOBILE RESPONSIVE
467
+ ============================================================ */
468
+ @media (max-width: 768px) {
469
+ /* Container padding for mobile */
470
+ .gradio-container {
471
+ padding: 12px !important;
472
+ }
473
+
474
+ /* Input textarea - smaller placeholder text, full width */
475
+ .input-row input,
476
+ .input-row textarea {
477
+ font-size: 0.8rem !important;
478
+ padding: 10px 12px !important;
479
+ max-width: 100% !important;
480
+ }
481
+
482
+ /* Feedback section - wrap on mobile */
483
+ .feedback-section {
484
+ flex-wrap: wrap !important;
485
+ justify-content: center !important;
486
+ padding: 10px !important;
487
+ gap: 8px !important;
488
+ }
489
+
490
+ .feedback-section p {
491
+ flex: 0 0 100% !important;
492
+ text-align: center !important;
493
+ margin-bottom: 4px !important;
494
+ }
495
+
496
+ .feedback-section button {
497
+ flex: 1 !important;
498
+ min-width: 80px !important;
499
+ }
500
+
501
+ /* Example buttons - stack on mobile */
502
+ .examples-section {
503
+ flex-wrap: wrap !important;
504
+ justify-content: center !important;
505
+ }
506
+
507
+ .examples-section > .block:first-child {
508
+ flex: 0 0 100% !important;
509
+ text-align: center !important;
510
+ }
511
+
512
+ .example-btn {
513
+ flex: 1 1 100% !important;
514
+ min-width: 100% !important;
515
+ max-width: 100% !important;
516
+ }
517
+
518
+ /* Header smaller on mobile */
519
+ .header-section h1 {
520
+ font-size: 1.3rem !important;
521
+ }
522
+
523
+ .header-section p {
524
+ font-size: 0.75rem !important;
525
+ }
526
+
527
+ /* Buttons column - narrower on mobile */
528
+ .buttons-col {
529
+ min-width: 90px !important;
530
+ }
531
+
532
+ .buttons-col button {
533
+ min-width: 90px !important;
534
+ font-size: 0.7rem !important;
535
+ padding: 6px 10px !important;
536
+ }
537
+ }
test.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Podcast Assistant Test Runner - Debug mode with message flow and metrics.
3
+
4
+ Usage:
5
+ uv run python test.py
6
+ """
7
+ import os
8
+ import time
9
+ from datetime import datetime
10
+ from dotenv import load_dotenv
11
+ from langchain_core.messages import HumanMessage, SystemMessage
12
+ from langchain.chat_models import init_chat_model
13
+
14
+ from search_podcasts import search_podcasts
15
+
16
+ # Load environment variables
17
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
18
+ load_dotenv(dotenv_path=os.path.join(SCRIPT_DIR, '..', '.env'))
19
+
20
+ # OpenAI pricing (per 1M tokens) - update as needed
21
+ PRICING = {
22
+ "gpt-5-nano": {
23
+ "input": 0.05 / 1_000_000,
24
+ "input_cached": 0.005 / 1_000_000,
25
+ "output": 0.40 / 1_000_000,
26
+ },
27
+ "gpt-5-mini": {
28
+ "input": 0.25 / 1_000_000,
29
+ "input_cached": 0.025 / 1_000_000,
30
+ "output": 2.00 / 1_000_000,
31
+ },
32
+ "gpt-4.1-nano": {
33
+ "input": 0.10 / 1_000_000,
34
+ "input_cached": 0.025 / 1_000_000,
35
+ "output": 0.40 / 1_000_000,
36
+ },
37
+ "gpt-4.1-mini": {
38
+ "input": 0.40 / 1_000_000,
39
+ "input_cached": 0.10 / 1_000_000,
40
+ "output": 1.60 / 1_000_000,
41
+ },
42
+ "gpt-4o-mini": {
43
+ "input": 0.15 / 1_000_000,
44
+ "input_cached": 0.075 / 1_000_000,
45
+ "output": 0.60 / 1_000_000,
46
+ },
47
+ "gpt-4o": {
48
+ "input": 2.50 / 1_000_000,
49
+ "input_cached": 1.25 / 1_000_000,
50
+ "output": 10.00 / 1_000_000,
51
+ },
52
+ }
53
+
54
+ # Model to use for testing
55
+ MODEL = "gpt-4.1-mini"
56
+
57
+
58
+ def compare_models(conversation_index: int = 0, models: list = None):
59
+ """Compare multiple models on the same conversation."""
60
+ if models is None:
61
+ models = ["gpt-5-nano", "gpt-4.1-nano", "gpt-4o-mini"]
62
+
63
+ results = []
64
+
65
+ for model in models:
66
+ print(f"\n{'#' * 80}")
67
+ print(f" TESTING: {model} ".center(80, "#"))
68
+ print("#" * 80)
69
+
70
+ result = run_test(conversation_index=conversation_index, model=model, return_metrics=True)
71
+ results.append({"model": model, **result})
72
+
73
+ # Print comparison summary
74
+ print("\n" + "=" * 80)
75
+ print(" MODEL COMPARISON ".center(80, "="))
76
+ print("=" * 80)
77
+
78
+ print("\nπŸ“Š Summary:")
79
+ print(f" {'Model':<15} {'Input':>10} {'Output':>10} {'Cost':>12} {'Time':>10}")
80
+ print(f" {'-'*15} {'-'*10} {'-'*10} {'-'*12} {'-'*10}")
81
+
82
+ for r in results:
83
+ print(f" {r['model']:<15} {r['total_api_input']:>10,} {r['total_api_output']:>10,} ${r['total_cost']:>11.6f} {r['total_time']:>9.2f}s")
84
+
85
+ # Find cheapest and fastest
86
+ cheapest = min(results, key=lambda x: x['total_cost'])
87
+ fastest = min(results, key=lambda x: x['total_time'])
88
+
89
+ print(f"\n πŸ’° Cheapest: {cheapest['model']} (${cheapest['total_cost']:.6f})")
90
+ print(f" ⚑ Fastest: {fastest['model']} ({fastest['total_time']:.2f}s)")
91
+
92
+
93
+ def get_usage_from_response(response) -> dict:
94
+ """Extract token usage from LangChain response (from OpenAI API)."""
95
+ usage = {"input": 0, "output": 0, "cache_read": 0}
96
+
97
+ if hasattr(response, 'usage_metadata') and response.usage_metadata:
98
+ usage["input"] = response.usage_metadata.get("input_tokens", 0)
99
+ usage["output"] = response.usage_metadata.get("output_tokens", 0)
100
+
101
+ # Get cached tokens
102
+ input_details = response.usage_metadata.get("input_token_details", {})
103
+ usage["cache_read"] = input_details.get("cache_read", 0)
104
+
105
+ return usage
106
+
107
+
108
+ def calculate_cost(input_tokens: int, output_tokens: int, cache_read: int = 0, model: str = MODEL) -> float:
109
+ """Calculate cost in USD. Cached tokens get 50% discount."""
110
+ pricing = PRICING.get(model, PRICING["gpt-4o-mini"])
111
+
112
+ # Non-cached input tokens
113
+ regular_input = input_tokens - cache_read
114
+
115
+ cost = (
116
+ (regular_input * pricing["input"]) +
117
+ (cache_read * pricing["input_cached"]) +
118
+ (output_tokens * pricing["output"])
119
+ )
120
+ return cost
121
+
122
+
123
+ def truncate(text: str, max_chars: int = 150) -> str:
124
+ """Truncate text with ellipsis."""
125
+ if len(text) <= max_chars:
126
+ return text
127
+ return text[:max_chars] + "..."
128
+
129
+
130
+ # =============================================================================
131
+ # Test Conversations (multi-turn)
132
+ # =============================================================================
133
+
134
+ TEST_CONVERSATIONS = [
135
+ # 0: Multi-turn with tool use β†’ follow-up may trigger another search β†’ context-based recommendation
136
+ [
137
+ "What is the future of AI and AGI according to experts?",
138
+ "What benefits and risks did they mention?",
139
+ "Which episode should I watch first?",
140
+ ],
141
+ # 1: Tool use β†’ context-based summary (tests if AI reuses previous results instead of searching again)
142
+ [
143
+ "How should young people approach their career and education?",
144
+ "Can you summarize that in 3 actionable points?"
145
+ ],
146
+ # 2: Multiple tool calls across turns (each question may require fresh search)
147
+ [
148
+ "What habits and routines do high performers follow?",
149
+ "What about sleep habits?",
150
+ "Any book recommendations from them?",
151
+ ],
152
+ # 3: Off-topic question (tests if AI correctly skips tool use)
153
+ [
154
+ "What is 2 + 2?",
155
+ ],
156
+ # 4: Single turn with one tool call (baseline for model comparison)
157
+ [
158
+ "What are the best books or films that influenced successful people?",
159
+ ],
160
+ ]
161
+
162
+
163
+ # =============================================================================
164
+ # Message Formatting
165
+ # =============================================================================
166
+
167
+ def print_header(title: str, width: int = 80):
168
+ """Print a centered header."""
169
+ padding = (width - len(title) - 2) // 2
170
+ print("=" * padding + f" {title} " + "=" * (width - padding - len(title) - 2))
171
+
172
+
173
+ def print_message(role: str, content: str, tool_calls: list = None, full: bool = False):
174
+ """Print a message."""
175
+ headers = {
176
+ "system": "System Message",
177
+ "human": "Human Message",
178
+ "ai": "Ai Message",
179
+ "tool": "Tool Message",
180
+ }
181
+
182
+ header = headers.get(role, role)
183
+ print_header(header)
184
+
185
+ if content:
186
+ display = content if full else truncate(content)
187
+ print(display)
188
+
189
+ if tool_calls:
190
+ print("Tool Calls:")
191
+ for tc in tool_calls:
192
+ print(f" {tc['name']}({', '.join(f'{k}={repr(v)}' for k, v in tc['args'].items())})")
193
+
194
+
195
+ def print_metrics_summary(metrics: dict, model: str = MODEL):
196
+ """Print a summary of all metrics."""
197
+ print("\n" + "=" * 80)
198
+ print(" METRICS SUMMARY ".center(80, "="))
199
+ print("=" * 80)
200
+
201
+ print(f"\nπŸ“Š Token Usage & Cost ({model}):")
202
+ for tm in metrics['turns']:
203
+ tool_str = " [tool]" if tm.get('used_tool') else ""
204
+ cache_str = f" ({tm['cache_read']:,} cached)" if tm['cache_read'] > 0 else ""
205
+ print(f" Turn {tm['turn']}: {tm['api_input']:,} in{cache_str} β†’ {tm['api_output']:,} out = ${tm['cost']:.6f}{tool_str}")
206
+
207
+ print(f"\n ─────────────────────────────")
208
+ print(f" Total input tokens: {metrics['total_api_input']:,}")
209
+ print(f" Total cached tokens: {metrics['total_cache_read']:,} (50% discount)")
210
+ print(f" Total output tokens: {metrics['total_api_output']:,}")
211
+ print(f" Total cost: ${metrics['total_cost']:.6f}")
212
+
213
+ print("\n⏱️ Timing:")
214
+ total_llm = 0
215
+ total_tool = 0
216
+ for tm in metrics['turns']:
217
+ llm_time = tm.get('llm_time', 0)
218
+ tool_time = tm.get('tool_time', 0)
219
+ total_llm += llm_time
220
+ total_tool += tool_time
221
+ tool_str = f" + tool {tool_time:.2f}s" if tool_time > 0 else ""
222
+ print(f" Turn {tm['turn']}: LLM {llm_time:.2f}s{tool_str}")
223
+
224
+ print(f" ─────────────────────────────")
225
+ print(f" Total LLM time: {total_llm:.2f}s")
226
+ print(f" Total tool time: {total_tool:.2f}s")
227
+ print(f" Total: {total_llm + total_tool:.2f}s")
228
+
229
+
230
+ # =============================================================================
231
+ # Main Test Runner
232
+ # =============================================================================
233
+
234
+ def run_test(conversation_index: int = 0, model: str = None, return_metrics: bool = False):
235
+ """Run a multi-turn conversation test with metrics."""
236
+
237
+ model = model or MODEL
238
+ queries = TEST_CONVERSATIONS[conversation_index]
239
+
240
+ metrics = {
241
+ 'turns': [],
242
+ 'total_api_input': 0,
243
+ 'total_api_output': 0,
244
+ 'total_cache_read': 0,
245
+ 'total_cost': 0,
246
+ 'total_time': 0,
247
+ }
248
+
249
+ # Load system prompt
250
+ with open(os.path.join(SCRIPT_DIR, "prompt.md"), "r") as f:
251
+ prompt_template = f.read()
252
+
253
+ today = datetime.now().strftime("%A, %B %d, %Y")
254
+ system_prompt = prompt_template.replace("{today_date}", today)
255
+
256
+ # Initialize LLM
257
+ llm = init_chat_model(model=model)
258
+ tools = [search_podcasts]
259
+ llm_with_tools = llm.bind_tools(tools)
260
+ tools_dict = {t.name: t for t in tools}
261
+
262
+ # Build initial messages
263
+ messages = [SystemMessage(content=system_prompt)]
264
+
265
+ print("\n" + "=" * 80)
266
+ print(" PODCAST ASSISTANT TEST ".center(80, "="))
267
+ print("=" * 80)
268
+ print(f"Model: {model}")
269
+ print(f"Conversation {conversation_index + 1}: {len(queries)} turns")
270
+ print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
271
+ print("=" * 80)
272
+
273
+ # Print System Message (truncated)
274
+ print_message("system", system_prompt)
275
+
276
+ # Process each query in the conversation
277
+ for turn, query in enumerate(queries, 1):
278
+ turn_metrics = {
279
+ 'turn': turn,
280
+ 'api_input': 0,
281
+ 'api_output': 0,
282
+ 'cache_read': 0,
283
+ 'cost': 0,
284
+ 'llm_time': 0,
285
+ 'tool_time': 0,
286
+ 'used_tool': False
287
+ }
288
+
289
+ print(f"\n{'─' * 80}")
290
+ print(f" TURN {turn}/{len(queries)} ".center(80, "─"))
291
+ print("─" * 80)
292
+
293
+ # Add user message
294
+ messages.append(HumanMessage(content=query))
295
+
296
+ print_message("human", query)
297
+
298
+ # Get AI response
299
+ t0 = time.perf_counter()
300
+ response = llm_with_tools.invoke(messages)
301
+ turn_metrics['llm_time'] = time.perf_counter() - t0
302
+
303
+ # Track API usage
304
+ usage = get_usage_from_response(response)
305
+ turn_metrics['api_input'] += usage['input']
306
+ turn_metrics['api_output'] += usage['output']
307
+ turn_metrics['cache_read'] += usage['cache_read']
308
+
309
+ messages.append(response)
310
+
311
+ # Print AI Message with tool calls
312
+ if response.tool_calls:
313
+ turn_metrics['used_tool'] = True
314
+ print_message("ai", "", tool_calls=response.tool_calls)
315
+
316
+ # Process tool calls
317
+ for tool_call in response.tool_calls:
318
+ tool_name = tool_call["name"]
319
+
320
+ if tool_name in tools_dict:
321
+ t0 = time.perf_counter()
322
+ tool_result = tools_dict[tool_name].invoke(tool_call)
323
+ turn_metrics['tool_time'] += time.perf_counter() - t0
324
+
325
+ messages.append(tool_result)
326
+
327
+ # Show FULL tool results (RAG context)
328
+ print_message("tool", tool_result.content, full=True)
329
+
330
+ # Get final AI response
331
+ t0 = time.perf_counter()
332
+ final_response = llm_with_tools.invoke(messages)
333
+ turn_metrics['llm_time'] += time.perf_counter() - t0
334
+
335
+ # Track API usage for second call
336
+ usage2 = get_usage_from_response(final_response)
337
+ turn_metrics['api_input'] += usage2['input']
338
+ turn_metrics['api_output'] += usage2['output']
339
+ turn_metrics['cache_read'] += usage2['cache_read']
340
+
341
+ messages.append(final_response)
342
+
343
+ # Full AI response
344
+ print_message("ai", final_response.content, full=True)
345
+ else:
346
+ # No tool call, just AI response
347
+ print_message("ai", response.content, full=True)
348
+
349
+ # Calculate cost for this turn (with cache discount)
350
+ turn_metrics['cost'] = calculate_cost(
351
+ turn_metrics['api_input'],
352
+ turn_metrics['api_output'],
353
+ turn_metrics['cache_read'],
354
+ model=model
355
+ )
356
+
357
+ # Update totals
358
+ metrics['total_api_input'] += turn_metrics['api_input']
359
+ metrics['total_api_output'] += turn_metrics['api_output']
360
+ metrics['total_cache_read'] += turn_metrics['cache_read']
361
+ metrics['total_cost'] += turn_metrics['cost']
362
+
363
+ metrics['turns'].append(turn_metrics)
364
+
365
+ # Calculate total time
366
+ metrics['total_time'] = sum(t['llm_time'] + t['tool_time'] for t in metrics['turns'])
367
+
368
+ # Print summary
369
+ print_metrics_summary(metrics, model=model)
370
+
371
+ if return_metrics:
372
+ return metrics
373
+
374
+
375
+ if __name__ == "__main__":
376
+ run_test(conversation_index=0)
uv.lock ADDED
The diff for this file is too large to render. See raw diff