StanDataCamp commited on
Commit
368f3e0
·
1 Parent(s): 1fc3ed6

Adding 2 latest podcasts

Browse files
build_index.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Build/rebuild the FAISS index from embedding chunks.
3
+
4
+ Usage:
5
+ python build_index.py # Full rebuild
6
+ python build_index.py --check # Check index status only
7
+ """
8
+ import os
9
+ import sys
10
+ import time
11
+ import argparse
12
+ import pandas as pd
13
+ from dotenv import load_dotenv
14
+
15
+ # Load environment
16
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
17
+ load_dotenv(dotenv_path=os.path.join(SCRIPT_DIR, '.env'))
18
+
19
+ FAISS_INDEX_PATH = os.path.join(SCRIPT_DIR, "faiss_index.db")
20
+ CHUNKS_PATH = os.path.join(SCRIPT_DIR, "data/episodes_embedding_chunks.csv")
21
+ EPISODES_PATH = os.path.join(SCRIPT_DIR, "data/episodes_website.json")
22
+
23
+
24
+ def check_index_status():
25
+ """Check the current status of data and index."""
26
+ print("=" * 60)
27
+ print("INDEX STATUS CHECK")
28
+ print("=" * 60)
29
+
30
+ # Check data files
31
+ if os.path.exists(CHUNKS_PATH):
32
+ chunks_df = pd.read_csv(CHUNKS_PATH)
33
+ num_chunks = len(chunks_df)
34
+ num_episodes = chunks_df["episode_number"].nunique()
35
+ newest_episode = chunks_df["episode_number"].max()
36
+ print(f"\nData file: {CHUNKS_PATH}")
37
+ print(f" Chunks: {num_chunks}")
38
+ print(f" Episodes: {num_episodes}")
39
+ print(f" Newest episode: #{newest_episode}")
40
+ else:
41
+ print(f"\n❌ Data file not found: {CHUNKS_PATH}")
42
+ return
43
+
44
+ # Check FAISS index
45
+ if os.path.exists(FAISS_INDEX_PATH):
46
+ from langchain_openai import OpenAIEmbeddings
47
+ from langchain_community.vectorstores import FAISS
48
+
49
+ embeddings = OpenAIEmbeddings()
50
+ vector_store = FAISS.load_local(
51
+ FAISS_INDEX_PATH,
52
+ embeddings=embeddings,
53
+ allow_dangerous_deserialization=True
54
+ )
55
+ index_size = vector_store.index.ntotal
56
+ print(f"\nFAISS index: {FAISS_INDEX_PATH}")
57
+ print(f" Vectors: {index_size}")
58
+
59
+ if index_size == num_chunks:
60
+ print("\n✅ Index is up to date!")
61
+ else:
62
+ print(f"\n⚠️ Index out of date: {index_size} vectors vs {num_chunks} chunks")
63
+ print(" Run 'python build_index.py' to rebuild")
64
+ else:
65
+ print(f"\n❌ FAISS index not found: {FAISS_INDEX_PATH}")
66
+ print(" Run 'python build_index.py' to create")
67
+
68
+
69
+ def rebuild_index():
70
+ """Rebuild the FAISS index from scratch."""
71
+ from langchain_core.documents import Document
72
+ from langchain_openai import OpenAIEmbeddings
73
+ from langchain_community.vectorstores import FAISS
74
+
75
+ print("=" * 60)
76
+ print("REBUILDING FAISS INDEX")
77
+ print("=" * 60)
78
+
79
+ # Load embedding chunks
80
+ print(f"\nLoading chunks from: {CHUNKS_PATH}")
81
+ chunks_df = pd.read_csv(CHUNKS_PATH)
82
+
83
+ num_chunks = len(chunks_df)
84
+ num_episodes = chunks_df["episode_number"].nunique()
85
+ newest = chunks_df["episode_number"].max()
86
+
87
+ print(f" Chunks: {num_chunks}")
88
+ print(f" Episodes: {num_episodes}")
89
+ print(f" Newest episode: #{newest}")
90
+
91
+ # Convert to LangChain Documents
92
+ print("\nConverting to documents...")
93
+ documents = []
94
+ for _, row in chunks_df.iterrows():
95
+ doc = Document(
96
+ page_content=row["embedding_text"],
97
+ metadata={
98
+ "chunk_id": row["chunk_id"],
99
+ "episode_number": row["episode_number"],
100
+ "episode_slug": row["episode_slug"],
101
+ "chapter_title": row["chapter_title"],
102
+ "start_seconds": row["start_seconds"],
103
+ "youtube_url": row["youtube_url"],
104
+ }
105
+ )
106
+ documents.append(doc)
107
+
108
+ # Create embeddings and index
109
+ print("\nCalling OpenAI API for embeddings...")
110
+ print("(This may take a few minutes for many chunks)")
111
+
112
+ embeddings = OpenAIEmbeddings()
113
+
114
+ t0 = time.perf_counter()
115
+ vector_store = FAISS.from_documents(documents, embeddings)
116
+ elapsed = time.perf_counter() - t0
117
+
118
+ # Save index
119
+ vector_store.save_local(FAISS_INDEX_PATH)
120
+
121
+ print(f"\n✅ FAISS index created successfully!")
122
+ print(f" Vectors: {vector_store.index.ntotal}")
123
+ print(f" Time: {elapsed:.1f}s")
124
+ print(f" Saved to: {FAISS_INDEX_PATH}")
125
+
126
+
127
+ if __name__ == "__main__":
128
+ parser = argparse.ArgumentParser(description="Build FAISS index for podcast search")
129
+ parser.add_argument("--check", action="store_true", help="Check index status only")
130
+
131
+ args = parser.parse_args()
132
+
133
+ if args.check:
134
+ check_index_status()
135
+ else:
136
+ rebuild_index()
data/README.md CHANGED
@@ -6,8 +6,8 @@ Source data and documentation for the vector index.
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
 
@@ -41,7 +41,7 @@ AFTER (context):
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
 
@@ -56,11 +56,19 @@ To add new episodes or rebuild the index:
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.
 
6
 
7
  | File | Description |
8
  |------|-------------|
9
+ | `episodes_website.json` | Episode metadata (108 episodes): titles, guests, YouTube URLs |
10
+ | `episodes_embedding_chunks.csv` | 14,865 transcript chunks with timestamps |
11
 
12
  ## What Gets Embedded
13
 
 
41
 
42
  **Index:** FAISS flat index (`IndexFlatL2`)
43
  - Simple brute-force similarity search
44
+ - Works well for our scale (~15K vectors)
45
  - No approximation — returns exact nearest neighbors
46
  - Trade-off: Larger indices (100K+) would benefit from IVF or HNSW for speed
47
 
 
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:
60
+ ```bash
61
+ uv run python build_index.py
62
+ ```
63
+ 5. Check index status:
64
+ ```bash
65
+ uv run python build_index.py --check
66
+ ```
67
 
68
  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.
69
 
70
+ **Current data (as of Feb 2026):** 108 episodes (#276–#491), newest: Peter Steinberger (OpenClaw)
71
+
72
  ## Privacy
73
 
74
  No user data is stored here. All content is from publicly available podcast episodes.
data/episodes_embedding_chunks.csv CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:1165e6cdd3e3b1ec29519de6646148259930158e8e877d8f6465f7d826b16410
3
- size 27106060
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:64e69c03f843ce97d2d2383f13c0cf9ab36c544657a2ec95712cc4cc3450157b
3
+ size 27832156
data/episodes_website.json CHANGED
@@ -1,4 +1,26 @@
1
  [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  {
3
  "episode_number": 489,
4
  "guest_name": "Paul Rosolie",
 
1
  [
2
+ {
3
+ "episode_number": 491,
4
+ "guest_name": "Peter Steinberger",
5
+ "profile": "Creator of OpenClaw",
6
+ "title": "OpenClaw: The Viral AI Agent that Broke the Internet - Peter Steinberger",
7
+ "youtube_url": "https://www.youtube.com/watch?v=YFjfBk8HI5o",
8
+ "youtube_video_id": "YFjfBk8HI5o",
9
+ "episode_slug": "peter-steinberger",
10
+ "transcript_slug": "peter-steinberger-transcript",
11
+ "has_transcript": true
12
+ },
13
+ {
14
+ "episode_number": 490,
15
+ "guest_name": "Nathan Lambert & Sebastian Raschka",
16
+ "profile": "AI Researchers",
17
+ "title": "State of AI in 2026: LLMs, Coding, Scaling Laws, China, Agents, GPUs, AGI",
18
+ "youtube_url": "https://www.youtube.com/watch?v=EV7WhVT270Q",
19
+ "youtube_video_id": "EV7WhVT270Q",
20
+ "episode_slug": "ai-sota-2026",
21
+ "transcript_slug": "ai-sota-2026-transcript",
22
+ "has_transcript": true
23
+ },
24
  {
25
  "episode_number": 489,
26
  "guest_name": "Paul Rosolie",
faiss_index.db/index.faiss CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:c609fc546b70173c230a6355c8417cc8a599d2a9b1c75d611c50584efcd78861
3
- size 89174061
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:48131af84c43f26974390a6f078e89da9bceb4b2c2d36f5c7acbd8640f18e08d
3
+ size 91330605
faiss_index.db/index.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5c1cc082fbfe506298fb091029ec824c21c017b9fcbd591206080424ef7c3b1d
3
- size 27603569
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1816c664eac81f1cc44cb162e9055bb4e6a72c58dfd6fcdd5dde87c2f407c14c
3
+ size 28366238