feat(podcasts): constrain monologue briefs to a single speaker

This commit is contained in:
CREDO23 2026-06-11 11:56:57 +02:00
parent eb56acc407
commit f0fc660d70
4 changed files with 55 additions and 14 deletions

View file

@ -148,6 +148,14 @@ class PodcastSpec(BaseModel):
raise ValueError("speaker slots must be unique")
return self
@model_validator(mode="after")
def _check_style_speakers(self) -> PodcastSpec:
# One voice is what "monologue" means; letting extra speakers through
# would force drafting to silently pick a winner.
if self.style is PodcastStyle.MONOLOGUE and len(self.speakers) != 1:
raise ValueError("a monologue has exactly one speaker")
return self
def speaker_for(self, slot: int) -> SpeakerSpec:
"""Return the speaker bound to ``slot`` or raise if none matches."""
for speaker in self.speakers:

View file

@ -14,6 +14,7 @@ from pydantic import ValidationError
from app.podcasts.schemas import (
DurationTarget,
PodcastSpec,
PodcastStyle,
SpeakerRole,
SpeakerSpec,
Transcript,
@ -80,6 +81,27 @@ def test_a_brief_needs_at_least_one_speaker():
)
def test_a_monologue_brief_carries_exactly_one_speaker():
spec = PodcastSpec(
language="en",
style=PodcastStyle.MONOLOGUE,
speakers=[_speaker(0)],
duration=DurationTarget(min_minutes=5, max_minutes=10),
)
assert spec.style is PodcastStyle.MONOLOGUE
def test_a_monologue_brief_rejects_multiple_speakers():
"""One voice is what 'monologue' means; a second speaker is a user error."""
with pytest.raises(ValidationError):
PodcastSpec(
language="en",
style=PodcastStyle.MONOLOGUE,
speakers=[_speaker(0), _speaker(1, voice_id="kokoro:af_bella")],
duration=DurationTarget(min_minutes=5, max_minutes=10),
)
def test_duration_rejects_an_inverted_range():
"""A max below the min is a user error caught at the brief gate."""
with pytest.raises(ValidationError):