A RAG-powered app where podcast guests answer your questions based on what they actually said on Lenny's Podcast, then discuss with each other.
No installation required. The live demo uses a pre-built vector database hosted on HuggingFace — users skip the 50-minute local ingest step entirely.
- 300+ transcripts from Lenny's Podcast included (Thanks to https://www.lennysnewsletter.com/ - go subscribe, now)
- 292 unique speakers including product leaders from Stripe, Netflix, Airbnb, and more
- Multi-round discussions where experts build on each other's perspectives
git clone https://github.com/manimohans/lennysroundtable.git
cd lennysroundtable
uv syncCopy the example config:
cp .env.example .envEdit .env with your provider:
Option A: Ollama (Local, Free) - Tested ✓
- Install Ollama
- Pull models:
ollama pull embeddinggemma # For embeddings ollama pull gemma3:4b # For generation (or any model you prefer)
- Set in
.env:LLM_BASE_URL=http://localhost:11434/v1 LLM_MODEL=gemma3:4b LLM_API_KEY=not-needed
Option B: LM Studio (Local, Free) - Tested ✓
- Install LM Studio
- Download a model and start the local server
- You still need Ollama for embeddings:
ollama pull embeddinggemma
- Set in
.env:LLM_BASE_URL=http://localhost:1234/v1 LLM_MODEL=your-loaded-model-name LLM_API_KEY=not-needed
Option C: OpenAI (Cloud, No Ollama Required) - Tested ✓
- Get an API key from OpenAI
- Set in
.env:WithLLM_BASE_URL=https://api.openai.com/v1 LLM_MODEL=gpt-4o-mini LLM_API_KEY=sk-your-api-key-here EMBEDDING_PROVIDER=openai OPENAI_API_KEY=sk-your-api-key-hereEMBEDDING_PROVIDER=openai, Ollama is not required — embeddings usetext-embedding-3-smallinstead.
Why isn't this included? The vector database (
chroma_db/) is ~600MB, which exceeds GitHub's file size limits. You need to generate it locally once. This takes about 50 minutes on CPU.
If using Ollama embeddings (default):
ollama pull embeddinggemma # If you haven't already
uv run python -m roundtable.ingestIf using OpenAI embeddings (EMBEDDING_PROVIDER=openai in .env):
uv run python -m roundtable.ingestNo Ollama needed — embeddings are generated via the OpenAI API (text-embedding-3-small).
You'll see progress like:
Found 301 transcript files
[301/301] Parsing: Zoelle Egner.txt
Created 17482 parent documents
Unique speakers: 292
Storing parent documents...
Creating child chunks for vector indexing...
Embedding and indexing child chunks (this may take a while)...
Generating embeddings: 100%|████████| 2048/2048 [04:08<00:00]
...
Ingestion complete!
Parent chunks: 17482
Child chunks: 22435
uv run streamlit run roundtable/app.py- Your Name: How speakers will address you (default: "PM")
- Your Question: Ask anything about product, startups, leadership, etc.
| Setting | Description |
|---|---|
| Discussion Rounds | How many rounds of back-and-forth (1-5) |
| Number of Experts | How many speakers to include (3-7) |
| Response Length | 1=brief, 5=detailed |
Click Generate Discussion and watch:
- Initial Thoughts - Each expert shares their perspective
- Round 2+ - Experts respond to each other, agree/disagree, add nuance
Click Save as Markdown to download the full conversation.
Want to update with the newest podcast episodes?
- Go to https://www.dropbox.com/developers/apps
- Create an app with "scoped access" and "full Dropbox"
- In Permissions tab, enable:
sharing.read,files.content.read - Generate an access token
- Add to
.env:DROPBOX_ACCESS_TOKEN=your_token_here
uv run python sync_transcripts.pyuv run python -m roundtable.ingest --resetHost your own live demo in three steps.
Run the ingest pipeline locally first (see Quick Start above),
then upload the resulting chroma_db/ to HuggingFace:
pip install huggingface_hub
huggingface-cli login # once — creates ~/.cache/huggingface/token
python scripts/upload_chroma.py --repo-id YOUR_HF_USERNAME/lennysroundtable-chromaThis creates a public HuggingFace dataset containing chroma_db.tar.gz (~600 MB).
- Fork this repo on GitHub.
- Go to share.streamlit.io → New app.
- Point it to
roundtable/app.pyin your fork.
In Settings → Secrets on Streamlit Cloud, paste:
LLM_BASE_URL = "https://api.openai.com/v1"
LLM_MODEL = "gpt-4o-mini"
LLM_API_KEY = "sk-..."
EMBEDDING_PROVIDER = "openai"
OPENAI_API_KEY = "sk-..."
HF_REPO_ID = "YOUR_HF_USERNAME/lennysroundtable-chroma"On first boot the app downloads the pre-built ChromaDB (~1–2 min) and caches it for the lifetime of the server instance. Subsequent users see no delay.
See .streamlit/secrets.toml.example for the full template.
User Question
│
▼
┌─────────────────────────────────────┐
│ Embed query (Ollama or OpenAI) │
│ Search child chunks (512 chars) │
│ Return parent chunks (2048 chars) │
│ Rank speakers by relevance │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Generate responses (your LLM) │
│ Stream to UI │
│ Build on previous responses │
└─────────────────────────────────────┘
We use a two-tier chunking strategy:
- Child chunks (512 chars): Small pieces for precise semantic matching
- Parent chunks (2048 chars): Larger coherent chunks returned to the LLM
This gives you accurate retrieval AND rich context.
Speakers are ranked by relevance to your question:
- Cosine similarity: Each child chunk gets a similarity score (0-1, higher = more relevant)
- Aggregation: Sum all similarity scores for a speaker's matching chunks
- Normalization: Divide by √(num_chunks) to balance speakers with many vs. few appearances
score = sum(similarities) / sqrt(num_chunks)
Typical scores range from ~0.5 (weakly relevant) to ~2.0 (highly relevant). The top 5 speakers are selected for the discussion.
Two providers are supported, configured via EMBEDDING_PROVIDER in .env:
| Provider | Default model | Requires |
|---|---|---|
ollama (default) |
embeddinggemma |
Ollama running locally |
openai |
text-embedding-3-small |
OPENAI_API_KEY |
Override the model with EMBEDDING_MODEL. Important: the embedding model must stay consistent between ingestion and queries — if you re-ingest with a different model, delete chroma_db/ first.
lennysroundtable/
├── roundtable/
│ ├── app.py # Streamlit UI
│ ├── generator.py # LLM response generation
│ ├── retriever.py # Semantic search & ranking
│ ├── ingest.py # Embedding pipeline
│ └── parser.py # Transcript parsing
├── transcripts/ # 300+ podcast transcripts
├── chroma_db/ # Generated locally (not in repo)
├── .env.example # Configuration template
└── sync_transcripts.py # Fetch new episodes
| Variable | Description | Example |
|---|---|---|
LLM_BASE_URL |
API endpoint | http://localhost:11434/v1 |
LLM_MODEL |
Model name | gemma3:4b |
LLM_API_KEY |
API key (if needed) | not-needed or sk-... |
| Variable | Default | Description |
|---|---|---|
EMBEDDING_PROVIDER |
ollama |
Embedding backend: ollama or openai |
EMBEDDING_MODEL |
embeddinggemma (ollama) / text-embedding-3-small (openai) |
Embedding model name |
OPENAI_API_KEY |
— | Required when EMBEDDING_PROVIDER=openai |
OLLAMA_BASE_URL |
http://localhost:11434 |
Ollama host (ollama provider only) |
| Variable | Default | Description |
|---|---|---|
CHILD_CHUNK_SIZE |
512 | Smaller = more precise matching |
PARENT_CHUNK_SIZE |
2048 | Larger = more context for LLM |
"No relevant speakers found"
- You need to run the embedding step first:
uv run python -m roundtable.ingest - If using Ollama: make sure it's running with
embeddinggemmapulled
"Connection refused" errors
- Make sure your LLM provider is running (Ollama/LM Studio)
- Check the
LLM_BASE_URLin.env
Embedding step fails (Ollama)
- Ensure Ollama is running:
ollama serve - Pull the embedding model:
ollama pull embeddinggemma - Or switch to OpenAI embeddings: set
EMBEDDING_PROVIDER=openaiandOPENAI_API_KEYin.env
Embedding step fails (OpenAI)
- Check that
OPENAI_API_KEYis set in.env - Verify the key is valid at https://platform.openai.com/api-keys
Slow generation
- Local models depend on your hardware
- Try a smaller model like
gemma3:1bor use OpenAI
- RAG Framework: LlamaIndex
- Vector Store: ChromaDB
- Embeddings:
embeddinggemmavia Ollama (default) ortext-embedding-3-smallvia OpenAI - LLM: Any OpenAI-compatible API
- UI: Streamlit
- Package Manager: uv
MIT