Files
UmbraAudio/tests/test_llm_attributor.py
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

80 lines
3.1 KiB
Python

from __future__ import annotations
from umbraaudio.analysis.llm_attributor import LLMAttributor
def _attributor_with_speakers(speakers: list[dict]) -> LLMAttributor:
attr = LLMAttributor.__new__(LLMAttributor)
attr._load_speakers = lambda: speakers # type: ignore[method-assign]
return attr
_SPEAKERS = [
{
"primary_name": "Kainda Walters Foster Murphy",
"aliases": ["Princess Kainda", "Princess", "Captain Capek"],
},
{
"primary_name": "Narrator",
"aliases": ["narrator", "the narrator"],
},
{
"primary_name": "Nanny",
"aliases": [],
},
]
class TestAliasMap:
def test_first_word_of_primary_maps_to_full_name(self) -> None:
attr = _attributor_with_speakers(_SPEAKERS)
alias_map = attr._build_alias_map()
assert alias_map["kainda"] == "Kainda Walters Foster Murphy"
assert alias_map["princess kainda"] == "Kainda Walters Foster Murphy"
assert alias_map["nanny"] == "Nanny"
def test_existing_short_alias_not_overridden(self) -> None:
speakers = [
{"primary_name": "Captain Weston", "aliases": ["Weston"]},
{"primary_name": "Weston", "aliases": []},
]
attr = _attributor_with_speakers(speakers)
alias_map = attr._build_alias_map()
assert alias_map["weston"] == "Captain Weston"
def test_primary_name_always_maps_to_itself(self) -> None:
attr = _attributor_with_speakers(_SPEAKERS)
alias_map = attr._build_alias_map()
assert alias_map["kainda walters foster murphy"] == "Kainda Walters Foster Murphy"
class TestNormalizeSpeaker:
def test_narrator_stays_lowercase(self) -> None:
attr = _attributor_with_speakers(_SPEAKERS)
alias_map = attr._build_alias_map()
assert attr._normalize_speaker("narrator", alias_map) == "narrator"
assert attr._normalize_speaker("Narrator", alias_map) == "narrator"
assert attr._normalize_speaker(" The Narrator ", alias_map) == "narrator"
def test_short_name_resolves_to_full_primary(self) -> None:
attr = _attributor_with_speakers(_SPEAKERS)
alias_map = attr._build_alias_map()
assert attr._normalize_speaker("Kainda", alias_map) == "Kainda Walters Foster Murphy"
assert attr._normalize_speaker("kainda", alias_map) == "Kainda Walters Foster Murphy"
def test_unknown_name_unchanged(self) -> None:
attr = _attributor_with_speakers(_SPEAKERS)
alias_map = attr._build_alias_map()
assert attr._normalize_speaker("Unknown Person", alias_map) == "Unknown Person"
def test_empty_name_unchanged(self) -> None:
attr = _attributor_with_speakers(_SPEAKERS)
alias_map = attr._build_alias_map()
assert attr._normalize_speaker("", alias_map) == ""
def test_pronoun_resolves_to_narrator(self) -> None:
attr = _attributor_with_speakers(_SPEAKERS)
alias_map = attr._build_alias_map()
for pronoun in ("She", "she", "He", "THEY", "her"):
assert attr._normalize_speaker(pronoun, alias_map) == "narrator"