Source code for podcast_llm.generate

"""
Podcast generation module.

This module provides functionality to generate complete podcast episodes from a given topic.
It can operate in two modes:
1. Research mode: Automatically researches the topic online
2. Existing context mode: Uses provided URLs and files as source material

Example:
    Research mode:
    >>> generate(topic='Artificial Intelligence', mode='research')

    Existing context mode:
    >>> generate(
    ...     topic='Artificial Intelligence',
    ...     mode='context',
    ...     sources=['article.pdf', 'https://example.com/ai-article']
    ... )
"""

import os
import argparse
from pathlib import Path
from typing import Optional, List, Literal
from podcast_llm.research import (
    research_background_info,
    research_discussion_topics
)
from podcast_llm.writer import (
    write_draft_script,
    write_final_script
)
from podcast_llm.outline import outline_episode
from podcast_llm.utils.checkpointer import (
    Checkpointer,
    to_snake_case
)
from podcast_llm.text_to_speech import generate_audio
from podcast_llm.config import PodcastConfig, setup_logging
from podcast_llm.utils.text import generate_markdown_script
from podcast_llm.extractors import extract_content_from_sources
import logging


PACKAGE_ROOT = Path(__file__).parent
DEFAULT_CONFIG_PATH = os.path.join(PACKAGE_ROOT, 'config', 'config.yaml')


[docs] def generate( topic: str, mode: Literal['research', 'context'], sources: Optional[List[str]] = None, qa_rounds: int = 2, use_checkpoints: bool = True, audio_output: Optional[str] = None, text_output: Optional[str] = None, config: str = DEFAULT_CONFIG_PATH, debug: bool = False, log_file: Optional[str] = None ) -> None: """ Generate a podcast episode. Args: topic: Topic of the podcast mode: Generation mode - either 'research' or 'context' sources: List of URLs and file paths to use as source material (for context mode) qa_rounds: Number of Q&A rounds use_checkpoints: Whether to use checkpointing audio_output: Path to save audio output text_output: Path to save text output config: Path to config file debug: Whether to enable debug logging log_file: Log output file """ log_level = logging.DEBUG if debug else logging.INFO setup_logging(log_level, output_file=log_file) config = PodcastConfig.load(yaml_path=config) checkpointer = Checkpointer( checkpoint_key=f"{to_snake_case(topic)}_qa_{qa_rounds}_", enabled=use_checkpoints ) # Get background info based on mode if mode == 'research': background_info = checkpointer.checkpoint( research_background_info, [config, topic], stage_name='background_info' ) else: # context mode if not sources: raise ValueError("Sources must be provided when using context mode") background_info = checkpointer.checkpoint( extract_content_from_sources, [sources], stage_name='background_info' ) outline = checkpointer.checkpoint( outline_episode, [config, topic, background_info], stage_name='outline' ) # Get detailed info based on mode if mode == 'research': deep_info = checkpointer.checkpoint( research_discussion_topics, [config, topic, outline], stage_name='deep_info' ) else: deep_info = background_info # Use the same extracted content draft_script = checkpointer.checkpoint( write_draft_script, [config, topic, outline, background_info, deep_info, qa_rounds], stage_name='draft_script' ) final_script = checkpointer.checkpoint( write_final_script, [config, topic, draft_script], stage_name='final_script' ) if text_output: with open(text_output, 'w+') as f: f.write(generate_markdown_script(topic, outline, final_script)) if audio_output: generate_audio(config, final_script, audio_output)
[docs] def parse_arguments() -> argparse.Namespace: """Parse command line arguments.""" parser = argparse.ArgumentParser( description='Generate podcasts using LLMs and TTS' ) parser.add_argument( 'topic', help='Topic of the podcast' ) parser.add_argument( '--mode', choices=['research', 'context'], default='research', help='Generation mode: research (automatic research) or context (use provided sources)' ) parser.add_argument( '--sources', nargs='+', help='List of URLs and file paths to use as source material (required for context mode)' ) parser.add_argument( '--qa-rounds', type=int, default=2, help='Number of question-answer rounds per section' ) parser.add_argument( '--checkpoint', action='store_true', dest='checkpoint', default=True, help='Enable checkpointing (default: True)' ) parser.add_argument( '--no-checkpoint', action='store_false', dest='checkpoint', help='Disable checkpointing' ) parser.add_argument( '--audio-output', type=str, default=None, help='Output filename for the generated audio' ) parser.add_argument( '--text-output', type=str, default=None, help='Output filename for the generated text script' ) parser.add_argument( '--config', type=str, default=DEFAULT_CONFIG_PATH, help='Path to YAML config file' ) parser.add_argument( '--debug', action='store_true', help='Enable debug logging' ) return parser.parse_args()
[docs] def main() -> None: """Main entry point for the CLI.""" args = parse_arguments() if args.mode == 'context' and not args.sources: raise ValueError("--sources must be provided when using context mode") generate( topic=args.topic, mode=args.mode, sources=args.sources, qa_rounds=args.qa_rounds, use_checkpoints=args.checkpoint, audio_output=args.audio_output, text_output=args.text_output, config=args.config, debug=args.debug )
if __name__ == '__main__': main()