* docs: update CLAUDE.md and user docs for error handling and podcast retry Add missing documentation for features introduced in v1.7.2 (#590) and v1.7.3 (#595): error classification system, global exception handlers, ConfigurationError, podcast failure recovery, and retry endpoint. * chore: update uv.lock
3.8 KiB
3.8 KiB
Podcasts Module
Domain models for podcast generation featuring speaker and episode profile management with job tracking.
Purpose
Encapsulates podcast metadata and configuration: speaker profiles (voice/personality config), episode profiles (generation settings), and podcast episodes (with job status tracking via surreal-commands).
Architecture Overview
Two-tier profile system:
- SpeakerProfile: TTS provider/model + 1-4 speaker configurations (name, voice_id, backstory, personality)
- EpisodeProfile: Generation settings (outline/transcript models, segment count, briefing template)
- PodcastEpisode: Generated episode record linking profiles, content, and async job
All inherit from ObjectModel (SurrealDB base class with table_name and save/load).
Component Catalog
SpeakerProfile
- Validates 1-4 speakers with required fields: name, voice_id, backstory, personality
- Stores TTS provider/model (e.g., "elevenlabs", "openai")
get_by_name()async query by profile name- Raises ValueError on invalid speaker counts or missing fields
EpisodeProfile
- Configures outline/transcript generation: provider, model, num_segments (3-20 validated)
- References speaker_config by name
- Stores default_briefing template for episode generation
get_by_name()async query
PodcastEpisode
- Stores episode_profile and speaker_profile as dicts (snapshots of config at generation time)
- Optional audio_file path, transcript/outline dicts
- Job tracking: command field links to surreal-commands RecordID
get_job_status()fetches async job status via surreal-commands libraryget_job_detail()returns both status and error_message from the job (used for retry validation and UI error display)_prepare_save_data()ensures command field is always RecordID format for database
Common Patterns
- Profile snapshots: episode_profile and speaker_profile stored as dicts to freeze config at generation time
- Field validation: Pydantic validators enforce constraints (segment count, speaker count, required fields)
- Async database access:
get_by_name()queries via repo_query - Job tracking: command field delegates to surreal-commands; get_job_status() returns "unknown" on failure
- Record ID handling: ensure_record_id() converts string to RecordID before save
Key Dependencies
pydantic: Field validators, ObjectModel inheritancesurrealdb: RecordID type for job referencesopen_notebook.database.repository: repo_query, ensure_record_idopen_notebook.domain.base: ObjectModel base classsurreal_commands(optional): get_command_status() for job status
Important Quirks & Gotchas
- Snapshot approach: Episode/speaker profiles stored as dicts (not references), so profile updates don't retroactively affect past episodes
- Job status resilience: get_job_status() catches all exceptions and returns "unknown" (no error propagation)
- No automatic retries: Podcast generation commands use
retry={"max_attempts": 1}to prevent duplicate episode records on failure; retry is user-initiated viaPOST /podcasts/episodes/{id}/retry - validate_speakers executes late: Validators run at instantiation; bulk inserts may not trigger full validation
- RecordID coercion: ensure_record_id() handles both string and RecordID inputs; command field parsed during deserialization
- No cascade delete: Removing a profile doesn't cascade to episodes using it
How to Extend
- Add new speaker field: Add to required_fields list in validate_speakers()
- Add episode config field: Validate in EpisodeProfile, update briefing generation code
- Add job metadata: Extend PodcastEpisode with new fields (e.g., progress tracking)
- Change job provider: Replace surreal-commands with alternative job queue library; update get_job_status()