Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/api-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ Returns basic service metadata.

### `POST /audio/generate`

Generates text-to-speech audio using Gemini or Monlam TTS (depending on language and configuration), uploads the result to S3, and returns a presigned URL.
Generates text-to-speech audio using Monlam TTS for Tibetan (`bo`) or Gemini TTS for all other language codes, uploads the result to S3, and returns a presigned URL.

**Request body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `language` | string | Yes | Language code for TTS (e.g. `bo`, `en`) |
| `language` | string | Yes | Language code for TTS (e.g. `bo`, `en`, `fr`). Tibetan (`bo`) uses Monlam; all other codes use Gemini. |
| `text` | string | No* | Raw text to synthesize |
| `day_id` | UUID | No* | Plan day ID — generates audio for all text subtasks in that day |
| `sub_task_id` | UUID | No* | Single subtask ID — generates audio for that subtask only |
Expand Down
23 changes: 15 additions & 8 deletions tests/audio/test_tts_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
_generate_gemini_tts_audio,
_convert_to_wav,
_parse_audio_mime_type,
SUPPORTED_TTS_LANGUAGES,
MONLAM_TTS_LANGUAGE,
)
from worker_api.audio.enums import PlanAudioType

Expand Down Expand Up @@ -38,10 +38,18 @@ def test_whitespace_content_raises_error(self):
with pytest.raises(ValueError, match="Content cannot be empty"):
generate_tts_audio(" ", PlanAudioType.RECITATION)

def test_unsupported_language_raises_error(self):
with pytest.raises(ValueError, match="Unsupported language for TTS"):
generate_tts_audio("Hello", PlanAudioType.RECITATION, language="fr")

@patch("worker_api.audio.services.tts_service._generate_gemini_tts_audio")
def test_other_languages_use_gemini(self, mock_gemini):
mock_gemini.return_value = b"fake_wav_data"

result = generate_tts_audio("Bonjour le monde", PlanAudioType.RECITATION, language="fr")

mock_gemini.assert_called_once_with(
content="Bonjour le monde",
audio_type=PlanAudioType.RECITATION,
)
assert result == b"fake_wav_data"

@patch("worker_api.audio.services.tts_service.generate_monlam_tts_audio")
def test_tibetan_language_uses_monlam(self, mock_monlam):
mock_monlam.return_value = b"fake_audio_data"
Expand Down Expand Up @@ -77,9 +85,8 @@ def test_english_language_uses_gemini(self, mock_gemini):
)
assert result == b"fake_wav_data"

def test_supported_languages(self):
assert "en" in SUPPORTED_TTS_LANGUAGES
assert "bo" in SUPPORTED_TTS_LANGUAGES
def test_monlam_language_constant(self):
assert MONLAM_TTS_LANGUAGE == "bo"


class TestGenerateGeminiTtsAudio:
Expand Down
10 changes: 2 additions & 8 deletions worker_api/audio/services/tts_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from worker_api.audio.services.monlam_tts_service import generate_monlam_tts_audio
from worker_api.audio.enums import PlanAudioType

SUPPORTED_TTS_LANGUAGES = {"en", "bo"}
MONLAM_TTS_LANGUAGE = "bo"


def _normalize_language(language: str) -> str:
Expand All @@ -21,13 +21,7 @@ def generate_tts_audio(
if not content.strip():
raise ValueError("Content cannot be empty")

normalized_language = _normalize_language(language)
if normalized_language not in SUPPORTED_TTS_LANGUAGES:
raise ValueError(
f"Unsupported language for TTS: {language}. Supported: {', '.join(sorted(SUPPORTED_TTS_LANGUAGES))}"
)

if normalized_language == "bo":
if _normalize_language(language) == MONLAM_TTS_LANGUAGE:
return generate_monlam_tts_audio(content, voice_name=voice_name)

return _generate_gemini_tts_audio(content=content, audio_type=audio_type)
Expand Down
Loading