Files
UmbraAudio/specs/10-cli-orchestrator.md
B.D. Murphy c86e323d7a Initial commit: UmbraAudio audiobook generation program
Quality-first multi-pass audiobook generation from English manuscripts.

Includes:
- umbraaudio/ core package (pipeline, config, voices, web review UI)
- scripts/ CLI utilities + model download script
- tts_server/ remote TTS server
- specs/ design docs, tests/, pyproject.toml + uv.lock
- Qwen3-TTS git submodule (pinned 022e286) with local demo.py UI patch
  saved at scripts/qwen3_tts_demo_umbra_patch.patch
- .env with runtime config/secrets

Excluded (see .gitignore): models/ (downloaded via scripts/download_models.sh),
output/, logs/, .venv, .umbra runtime data, and book projects
(DealHunterShip_*), which are committed separately.
2026-08-30 07:58:24 -07:00

6.3 KiB
Raw Permalink Blame History

Spec: CLI & Orchestrator

Overview

The CLI provides Click-based commands for each pipeline pass. The orchestrator coordinates the full pipeline with LM Studio lifecycle management.

CLI Commands

Global Options

umbraaudio [--project DIR] COMMAND
Option Description
--project, -p Project directory (default: .)

Command: init

umbraaudio init MANUSCRIPT [--name NAME]
Argument/Option Type Description
MANUSCRIPT Path (required) Source manuscript file
--name, -n str (optional) Project name

Flow:

  1. Detect format via detect_format()
  2. Parse manuscript via parse_manuscript()
  3. Create project in DB
  4. Insert chapters into DB
  5. Update status to "parsed"

Command: analyze

umbraaudio analyze

Flow:

  1. BookNLP analysis per chapter
  2. Character merging and deduplication
  3. LLM profiling for each character (up to 30)
  4. Pronunciation guide for unusual terms
  5. Update status to "analyzed"

Command: voices

umbraaudio voices

Flow:

  1. Load non-narrator characters with voice_description
  2. Load Chatterbox TTS
  3. Generate voice reference WAV per character
  4. Extract speaker embedding
  5. Update DB with paths and embeddings

Command: map-audio

umbraaudio map-audio

Flow:

  1. Segment each chapter via Segmenter.segment()
  2. Build generation manifests
  3. Save to .umbra/manifests/ch{id}.json

Command: test

umbraaudio test [--chapter N]
Option Type Description
--chapter, -c int (optional) Chapter to test (default: 1)

Flow:

  1. Load manifest for specified chapter
  2. Load Chatterbox TTS
  3. Generate audio for the chapter
  4. Report results

Command: generate

umbraaudio generate [--chapters RANGE]
Option Type Description
--chapters, -c str Chapter range: "all", "3", "1-5", "3,7"

Flow:

  1. Load Chatterbox TTS
  2. Load manifest per chapter
  3. Generate audio with caching, retry, normalization, watermark
  4. Report progress per chapter

Command: export

umbraaudio export [--format FORMAT]
Option Type Description
--format, -f str (multiple) Export formats: "m4b", "mp3", "wav"

Flow:

  1. Load normalized audio (fallback to raw)
  2. Stitch chapter audio
  3. Export in requested formats

Command: pipeline

umbraaudio pipeline [--chapters RANGE] [--author NAME]
Option Type Default Description
--chapters, -c str "all" Chapter range
--author, -a str "B.D. Murphy" Author name for watermark

Flow: Calls orchestrator.run_pipeline(). See Orchestrator section below.

Command: status

umbraaudio status

Displays project status, chapter count, character count, and progress in a Rich table.

Command: review

umbraaudio review [--host HOST] [--port PORT]
Option Type Default Description
--host str "127.0.0.1" Server bind address
--port int 8420 Server port

Launches the web review UI.

Chapter Range Parsing

def _parse_chapter_range(chapters_str: str, total: int) -> range
Input Result
"all" range(0, total)
"3" range(2, 3)
"1-5" range(0, 5)
"3,7" range(2, 3) ∪ range(6, 7) (union)

Orchestrator (orchestrator.py)

Function: run_pipeline()

async def run_pipeline(
    project_dir: Path,
    chapters: str | None,
    author_name: str,
) -> dict[str, Any]

Returns:

{
    "stages": list[dict],   # Per-stage results
    "defects": list[dict],  # Quality defects
    "summary": dict,        # Final statistics
}

Full Pipeline Stages

1. init
   └── Load project and chapters from DB

2. lms_unload
   └── Run: lms unload --all (free VRAM)

3. chatterbox_load
   └── Load ChatterboxTTS (~3.2 GB VRAM)

4. Pass 1: Analysis (conditional: status == "initialized" or "parsed")
   ├── BookNLP analysis per chapter
   ├── _merge_characters() (deduplicate, filter noise)
   ├── CharacterProfiler.profile_character() per character
   ├── PronunciationGenerator.find_unusual_terms()
   └── Update DB + status → "analyzed"

5. Pass 2: Voice references
   ├── VoiceDesigner.generate_voice_reference() per character
   ├── Skip if voice_ref_path exists on disk
   ├── ChatterboxEngine.extract_embedding()
   └── Update DB

6. Pass 3: Segment & manifest
   ├── Segmenter.segment() per chapter
   ├── build_generation_manifest()
   └── save_manifest() to .umbra/manifests/

7. Pass 5: Generate audio
   ├── ChapterGenerator per chapter
   ├── Per-chapter: gc.collect() + torch.cuda.empty_cache()
   └── AudioNormalizer per file

8. Quality check
   └── check_acx_compliance() per generated file

9. Export
   ├── export_mp3()
   └── export_wav()

10. chatterbox_unload
    └── Unload Chatterbox from VRAM

11. lms_restart
    └── Run: lms server stop && lms server start

LM Studio Lifecycle

The orchestrator manages LM Studio to prevent VRAM conflicts:

def unload_lm_studio() -> bool:
    result = subprocess.run(["lms", "unload", "--all"], ...)
    return result.returncode == 0

def stop_lm_studio() -> bool:
    result = subprocess.run(["lms", "server", "stop"], ...)
    return result.returncode == 0

def start_lm_studio() -> bool:
    result = subprocess.run(["lms", "server", "start"], ...)
    return result.returncode == 0

Order: Unload → Stop → (generate) → Unload Chatterbox → Start

Error Handling

  • Entire pipeline wrapped in try/except
  • On failure: unload Chatterbox, restart LM Studio, log CRITICAL defect
  • Individual utterance failures don't abort the pipeline
  • Defects collected and written to .umbra/defects.json

Progress Reporting

Rich progress bars per stage:

  • Pass 1: Per-chapter analysis
  • Pass 2: Per-character voice design
  • Pass 3: Per-chapter segmentation
  • Pass 5: Per-utterance generation with ETA