Episode Profile System in Open Notebook: A Complete Guide to Podcast Customization
The episode profile system in Open Notebook encapsulates podcast configuration into reusable EpisodeProfile objects that define LLM models, language settings, briefing templates, and speaker configurations, enabling users to customize podcast generation without managing low-level model IDs.
Open Notebook treats podcast episodes as compositions of configuration rather than monolithic jobs. The system replaces a legacy 15-field schema with concise, user-friendly profile objects that separate content strategy from technical implementation. By centralizing these settings in SurrealDB-backed Pydantic models, the repository allows users to create, version, and reuse podcast configurations across multiple generation runs.
Core Components of the Episode Profile System
The architecture revolves around two primary models defined in open_notebook/podcasts/models.py: EpisodeProfile for generation parameters and SpeakerProfile for voice configuration.
EpisodeProfile Data Model
The EpisodeProfile class inherits from ObjectModel and persists to the episode_profile table. It serves as the single source of truth for episode generation parameters:
class EpisodeProfile(ObjectModel):
table_name = "episode_profile"
name: str
description: Optional[str]
speaker_config: str
outline_llm: Optional[str]
transcript_llm: Optional[str]
language: Optional[str]
default_briefing: str
num_segments: int = 5
Key fields include:
speaker_config– References aSpeakerProfileby name, linking voice and personality data.outline_llmandtranscript_llm– SurrealDB record IDs (e.g.,"model:uuid-1234") pointing to Model objects for outline generation and transcript writing.default_briefing– The system prompt template that primes the LLM for content generation.num_segments– Controls podcast structure, defaulting to 5 sections (validated between 3-20 intests/test_domain.py).
Legacy fields like outline_provider and outline_model exist only for database migration and are ignored by current application logic.
SpeakerProfile Integration
Each EpisodeProfile references a SpeakerProfile via the speaker_config field. The SpeakerProfile class (defined later in the same models file) describes one to four speakers, including their voice-model IDs and personality parameters. When a podcast job executes, the system resolves both profiles to obtain complete provider-model-configuration triples for the audio synthesis pipeline.
API Endpoints for Managing Episode Profiles
All profile management operations expose through FastAPI routes in api/routers/episode_profiles.py, registered under the "episode-profiles" tag in api/main.py:
GET /api/episode-profiles– Returns a list ofEpisodeProfileResponseobjects.GET /api/episode-profiles/{profile_name}– Retrieves a specific profile by its unique name.POST /api/episode-profiles– Creates a profile usingEpisodeProfileCreateschema.PUT /api/episode-profiles/{profile_id}– Performs partial updates usingmodel_dump(exclude_unset=True).DELETE /api/episode-profiles/{profile_id}– Removes the profile from SurrealDB.POST /api/episode-profiles/{profile_id}/duplicate– Creates a copy with " - Copy" appended to the name.
The router leverages the generic CRUD helpers from ObjectModel, including _prepare_save_data, which automatically normalizes model IDs to SurrealDB RecordID objects before persistence.
How Episode Profiles Drive Podcast Generation
When run_podcast() executes in commands/podcast_commands.py, the episode profile system resolves abstract configuration into concrete model instances:
- Profile Loading – The system loads the selected profile via
await EpisodeProfile.get_by_name(input_data.episode_profile). - LLM Resolution – It resolves outline and transcript configurations using
await profile.resolve_outline_config()andawait profile.resolve_transcript_config(). - Speaker Resolution – It fetches the referenced
SpeakerProfileusingawait SpeakerProfile.get_by_name(profile.speaker_config)and resolves TTS model settings. - Pipeline Execution – The resolved triples pass to the LangGraph workflow that builds outlines, generates transcripts, and synthesizes audio.
This resolution pattern isolates users from provider-specific model IDs while allowing runtime swapping of LLM backends.
Creating and Using Episode Profiles
Creating a Profile via Python Client
Use the APIClient from open_notebook.api.client to programmatically create profiles:
from open_notebook.api.client import APIClient
client = APIClient(base_url="http://localhost:5055")
profile_data = {
"name": "Tech Talk – AI",
"description": "Standard AI-focused tech podcast",
"speaker_config": "default_speakers",
"outline_llm": "model:uuid-1234",
"transcript_llm": "model:uuid-5678",
"language": "en-US",
"default_briefing": "Create a 10-minute episode about recent AI news.",
"num_segments": 6,
}
new_profile = client.create_episode_profile(**profile_data)
print(new_profile)
Creating a Profile via cURL
For direct API access:
curl -X POST http://localhost:5055/api/episode-profiles \
-H "Content-Type: application/json" \
-d '{
"name": "Science Daily",
"description": "Daily science digest",
"speaker_config": "science_speakers",
"outline_llm": "model:uuid-1111",
"transcript_llm": "model:uuid-2222",
"language": "en-US",
"default_briefing": "Summarise the top three science headlines.",
"num_segments": 4
}'
Using Profiles in Generation Commands
Reference profiles by name when triggering podcast generation:
from commands.podcast_commands import run_podcast
await run_podcast(
podcast_name="my_first_episode",
episode_profile="Science Daily",
source_url="https://example.com/news",
)
Duplicating Existing Profiles
Clone configurations for rapid iteration:
curl -X POST http://localhost:5055/api/episode-profiles/12345-abcde/duplicate
Summary
- The episode profile system centralizes podcast configuration in
EpisodeProfileobjects withinopen_notebook/podcasts/models.py. - Profiles encapsulate LLM references (
outline_llm,transcript_llm), language settings, briefing templates, and segment counts. - SpeakerProfile integration via
speaker_configenables voice and personality customization separate from episode structure. - Full CRUD operations expose through
/api/episode-profilesendpoints, including a duplicate function for rapid templating. - Runtime resolution in
commands/podcast_commands.pytranslates profile names into concrete model configurations for the LangGraph pipeline.
Frequently Asked Questions
What fields are required when creating an EpisodeProfile?
The name, speaker_config, and default_briefing fields are required, while num_segments defaults to 5. The outline_llm and transcript_llm fields are optional but necessary for generation; if omitted, the system cannot resolve model configurations during the podcast pipeline execution.
How does the episode profile system handle speaker configuration?
The speaker_config field stores the name of a SpeakerProfile record. When run_podcast() executes, it calls SpeakerProfile.get_by_name(profile.speaker_config) to load voice model IDs and personality data for one to four speakers, then passes these to the TTS synthesis stage of the pipeline.
Can I update an episode profile after creating it?
Yes. The PUT /api/episode-profiles/{profile_id} endpoint supports partial updates using Pydantic's model_dump(exclude_unset=True), allowing you to modify specific fields like default_briefing or num_segments without resubmitting the entire configuration object.
What is the maximum number of segments allowed in an episode profile?
The num_segments field accepts integers between 3 and 20, as validated by unit tests in tests/test_domain.py. Attempting to set values outside this range will fail validation before persistence to SurrealDB.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →