# How EpisodeProfile and SpeakerProfile Enable Multi-Speaker Podcast Generation in Open Notebook

> Discover how EpisodeProfile and SpeakerProfile models in Open Notebook streamline multi-speaker podcast generation by separating LLM and TTS configurations, supporting up to four speakers.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: deep-dive
- Published: 2026-07-04

---

**The EpisodeProfile and SpeakerProfile Pydantic models provide the configuration foundation for multi-speaker podcast generation by separating episode-level LLM settings from voice-specific TTS configurations, supporting up to four distinct speakers per episode.**

The open-notebook repository delivers a sophisticated backend for automated podcast creation, with multi-speaker support built on two central data models. These models abstract the complexity of coordinating multiple AI providers—handling everything from outline generation to voice synthesis—into simple, validated configuration objects. By isolating speaker personas from episode generation parameters, the system enables dynamic, personality-driven conversations between multiple AI voices.

## EpisodeProfile: The Episode Configuration Container

The `EpisodeProfile` model in [`open_notebook/podcasts/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/podcasts/models.py) serves as the central configuration hub for an individual podcast episode. It stores references to the models used for content generation and links to the speaker configuration that defines who speaks during the episode.

Key capabilities include:

- **Model selection for content generation**: Stores `outline_llm` and `transcript_llm` references that point to specific model records in the registry
- **Speaker configuration linkage**: The `speaker_config` field references a `SpeakerProfile` by name, establishing which voices will participate in the episode
- **Token budget control**: An optional `max_tokens` field overrides default limits for both outline and transcript generation stages
- **Configuration resolution**: The `resolve_outline_config()` and `resolve_transcript_config()` methods (lines 36-44 and 86-89) translate stored model IDs into concrete provider/model/config tuples via `_resolve_model_config()`

## SpeakerProfile: Defining Voices and Personalities

The `SpeakerProfile` model handles the multi-speaker aspect of podcast generation by defining up to four distinct voices, each with unique characteristics and TTS settings. Located in the same [`open_notebook/podcasts/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/podcasts/models.py) file, this model ensures that every speaker has a defined persona and voice configuration.

The model enforces strict validation (lines 71-74) to ensure the `speakers` list contains between one and four entries. Each speaker entry must include:

- `name`: The display name used in the transcript
- `voice_id`: The specific voice identifier for the TTS provider
- `backstory`: Contextual background information to inform the LLM's generation style
- `personality`: Behavioral traits that shape how the speaker interacts with others

The model supports both legacy TTS configuration via `tts_provider` and `tts_model` fields, and modern registry-based references through the `voice_model` field (lines 60-64). The `resolve_tts_config()` method (lines 93-100) loads the appropriate provider, model name, and credentials at runtime.

## Creating a Multi-Speaker Configuration

To define the voices for a podcast, you instantiate a `SpeakerProfile` with detailed speaker definitions:

```python
from open_notebook.podcasts.models import SpeakerProfile

profile = SpeakerProfile(
    name="tri-voice-news",
    description="Three distinct news anchors",
    voice_model="model:tts/openai/tts-1",
    speakers=[
        {
            "name": "Anchor 1",
            "voice_id": "en-US-Andy",
            "backstory": "Seasoned journalist",
            "personality": "Calm and authoritative",
        },
        {
            "name": "Anchor 2",
            "voice_id": "en-US-Jenny",
            "backstory": "Tech-savvy reporter",
            "personality": "Energetic and witty",
        },
        {
            "name": "Anchor 3",
            "voice_id": "en-US-Mike",
            "backstory": "Finance analyst",
            "personality": "Precise and analytical",
        },
    ],
)
await profile.save()

```

This configuration creates three distinct personas that the LLM will use to generate dialogue, while the TTS system maps each to a specific voice. The validation logic ensures that exactly three speakers—within the 1-4 supported range—are provided.

## Configuring Episode Generation Parameters

Once speaker profiles exist, an `EpisodeProfile` links them to specific generation models and episode settings:

```python
from open_notebook.podcasts.models import EpisodeProfile

episode = EpisodeProfile(
    name="weekly-tech-summary",
    description="A tech roundup with three hosts",
    speaker_config="tri-voice-news",
    outline_llm="model:openai/gpt-4o-mini",
    transcript_llm="model:openai/gpt-4o",
    default_briefing="Summarize the top tech news of the week.",
    num_segments=5,
    max_tokens=2000,
)
await episode.save()

```

The `speaker_config` field references the previously created `SpeakerProfile` by name, while `outline_llm` and `transcript_llm` point to model records that control the AI providers for text generation. The `max_tokens` parameter applies to both generation stages, providing a unified budget control mechanism.

## Runtime Resolution and Service Integration

During podcast generation, the [`api/podcast_service.py`](https://github.com/lfnovo/open-notebook/blob/main/api/podcast_service.py) (lines 9-12) orchestrates the workflow by resolving these abstract configurations into concrete provider settings:

```python

# Inside the podcast creation service

episode = await EpisodeProfile.get_by_name("weekly-tech-summary")
speaker = await SpeakerProfile.get_by_name(episode.speaker_config)

provider, model_name, config = await episode.resolve_outline_config()

# → ("openai", "gpt-4o-mini", {"max_tokens": 2000, ...})

tts_provider, tts_model, tts_cfg = await speaker.resolve_tts_config()

# → ("openai", "tts-1", {...})

```

The `resolve_outline_config()` and `resolve_transcript_config()` methods translate the episode's model references into executable tuples containing provider names, model identifiers, and configuration dictionaries. Similarly, `speaker.resolve_tts_config()` prepares the voice synthesis parameters. These resolved configurations are then passed to the respective LLM and TTS backends to generate the multi-speaker audio content.

## Summary

- **EpisodeProfile** manages episode-level settings including LLM selection for outlines and transcripts, token limits, and references to speaker configurations.
- **SpeakerProfile** defines 1-4 distinct voices with unique personalities, backstories, and TTS settings, validated to ensure proper multi-speaker constraints.
- **Configuration resolution** methods translate registry model IDs into provider-specific tuples at runtime, abstracting implementation details from the configuration layer.
- **Service integration** in [`api/podcast_service.py`](https://github.com/lfnovo/open-notebook/blob/main/api/podcast_service.py) binds these models together to execute the full podcast generation workflow.
- Both models support modern registry-based model references while maintaining backward compatibility with legacy TTS provider configurations.

## Frequently Asked Questions

### How many speakers can a single podcast episode support?

The `SpeakerProfile` model enforces a validation constraint (lines 71-74 in [`open_notebook/podcasts/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/podcasts/models.py)) that limits the `speakers` list to between one and four entries. This design choice balances multi-speaker dialogue complexity with TTS processing feasibility, ensuring the generation pipeline can handle the audio synthesis workload efficiently.

### What is the difference between voice_model and the individual speaker voice_id?

The `voice_model` field in `SpeakerProfile` (lines 60-64) specifies a default TTS model registry reference for the entire profile, while individual `voice_id` values within each speaker entry identify specific voices within that provider (such as "en-US-Andy" or "en-US-Jenny"). This architecture allows you to set a global TTS provider while varying specific voice characteristics per speaker, or override the global model with per-speaker configurations if needed.

### How does EpisodeProfile connect to SpeakerProfile?

The `EpisodeProfile` model contains a `speaker_config` string field that stores the name of a `SpeakerProfile` record. When the podcast service initiates generation, it loads the episode configuration, extracts the `speaker_config` value, and retrieves the corresponding `SpeakerProfile` to access the speaker definitions and TTS settings. This reference-based design enables multiple episodes to reuse the same speaker configurations without duplicating voice definitions.

### Can I use different LLMs for outline generation and transcript generation?

Yes. The `EpisodeProfile` model provides separate `outline_llm` and `transcript_llm` fields, each referencing distinct model records in the registry. This allows you to use a smaller, faster model (such as GPT-4o-mini) for generating the structural outline, while employing a more capable model (such as GPT-4o) for the detailed transcript generation, optimizing both cost and quality for each stage of multi-speaker podcast generation.