Spaces:
Build error
Build error
feat: wire TTS into Phase 2 pipeline with voice design mode
Browse files- core/pipeline.py +33 -10
core/pipeline.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
| 1 |
"""EuropaLex Pipeline β Phase 2 orchestration.
|
| 2 |
|
| 3 |
Receives English texts generated in Phase 1 and produces translated
|
| 4 |
-
CardData objects via tiny-aya-water translation engine
|
| 5 |
-
|
| 6 |
-
Images and audio are not yet wired β those fields remain empty.
|
| 7 |
"""
|
| 8 |
|
| 9 |
from __future__ import annotations
|
| 10 |
|
| 11 |
import logging
|
|
|
|
| 12 |
from typing import Iterator
|
| 13 |
|
| 14 |
from core.engine import EnginePool
|
|
@@ -22,11 +22,14 @@ def generate_phase2(
|
|
| 22 |
scenario: str,
|
| 23 |
cefr_level: CEFRLevel,
|
| 24 |
batch_size: int,
|
|
|
|
|
|
|
| 25 |
) -> Iterator[tuple[int, str, list[CardData]]]:
|
| 26 |
-
"""Generate
|
| 27 |
|
| 28 |
Orchestrates the translation pipeline: gets the tiny-aya engine,
|
| 29 |
-
calls generate with retry validation,
|
|
|
|
| 30 |
|
| 31 |
Yields (progress_percent, phase_label, cards) at each step.
|
| 32 |
|
|
@@ -35,11 +38,15 @@ def generate_phase2(
|
|
| 35 |
scenario: Original scenario/topic description.
|
| 36 |
cefr_level: CEFR proficiency level.
|
| 37 |
batch_size: Number of translations expected.
|
|
|
|
|
|
|
| 38 |
|
| 39 |
Yields:
|
| 40 |
(20, "Preparing translation...", []) β before engine call
|
| 41 |
-
(
|
| 42 |
-
(
|
|
|
|
|
|
|
| 43 |
|
| 44 |
Raises:
|
| 45 |
ValidationError: If translation fails after max retries.
|
|
@@ -68,15 +75,31 @@ def generate_phase2(
|
|
| 68 |
except ValidationError:
|
| 69 |
raise
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
cards = [
|
| 72 |
CardData(
|
| 73 |
text=text,
|
| 74 |
translation=translation,
|
| 75 |
-
audio_path=None,
|
| 76 |
image_path=None,
|
| 77 |
cefr_level=cefr_level,
|
| 78 |
)
|
| 79 |
-
for text, translation in zip(texts, translations)
|
| 80 |
]
|
| 81 |
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""EuropaLex Pipeline β Phase 2 orchestration.
|
| 2 |
|
| 3 |
Receives English texts generated in Phase 1 and produces translated
|
| 4 |
+
CardData objects via tiny-aya-water translation engine, with optional
|
| 5 |
+
TTS audio generation using OmniVoice voice design mode.
|
|
|
|
| 6 |
"""
|
| 7 |
|
| 8 |
from __future__ import annotations
|
| 9 |
|
| 10 |
import logging
|
| 11 |
+
from pathlib import Path
|
| 12 |
from typing import Iterator
|
| 13 |
|
| 14 |
from core.engine import EnginePool
|
|
|
|
| 22 |
scenario: str,
|
| 23 |
cefr_level: CEFRLevel,
|
| 24 |
batch_size: int,
|
| 25 |
+
target_language: str = "Latvian",
|
| 26 |
+
include_audio: bool = False,
|
| 27 |
) -> Iterator[tuple[int, str, list[CardData]]]:
|
| 28 |
+
"""Generate translations and optional TTS audio for Phase 1 English texts.
|
| 29 |
|
| 30 |
Orchestrates the translation pipeline: gets the tiny-aya engine,
|
| 31 |
+
calls generate with retry validation, optionally generates TTS audio
|
| 32 |
+
for all translations via OmniVoice (voice design mode), and yields CardData objects.
|
| 33 |
|
| 34 |
Yields (progress_percent, phase_label, cards) at each step.
|
| 35 |
|
|
|
|
| 38 |
scenario: Original scenario/topic description.
|
| 39 |
cefr_level: CEFR proficiency level.
|
| 40 |
batch_size: Number of translations expected.
|
| 41 |
+
target_language: Target language name for TTS (e.g., "Latvian"). Used to improve synthesis quality.
|
| 42 |
+
include_audio: If True, generate TTS audio for all translations after translation completes.
|
| 43 |
|
| 44 |
Yields:
|
| 45 |
(20, "Preparing translation...", []) β before engine call
|
| 46 |
+
(15-70, "Translating... (N/total)", []) β during per-sentence translation
|
| 47 |
+
(70, "Generating audio...", []) β before TTS starts (if include_audio=True)
|
| 48 |
+
(95, "Audio complete!", cards) β after TTS batch (if include_audio=True)
|
| 49 |
+
(100, "Translation and audio complete!", cards) β with final CardData list
|
| 50 |
|
| 51 |
Raises:
|
| 52 |
ValidationError: If translation fails after max retries.
|
|
|
|
| 75 |
except ValidationError:
|
| 76 |
raise
|
| 77 |
|
| 78 |
+
audio_paths: list[str | None] = [None] * len(translations)
|
| 79 |
+
|
| 80 |
+
if include_audio:
|
| 81 |
+
yield 70, "Generating audio...", []
|
| 82 |
+
try:
|
| 83 |
+
tts_engine = pool.get_tts_engine()
|
| 84 |
+
output_dir = Path(config.models_dir) / "output" / "audio"
|
| 85 |
+
audio_result = tts_engine.synthesize(translations, output_dir, language=target_language)
|
| 86 |
+
audio_paths = audio_result.audio_paths
|
| 87 |
+
except Exception as e:
|
| 88 |
+
logger.error("TTS generation failed: %s", e, exc_info=True)
|
| 89 |
+
# Continue with None audio paths β cards still render with translations
|
| 90 |
+
|
| 91 |
cards = [
|
| 92 |
CardData(
|
| 93 |
text=text,
|
| 94 |
translation=translation,
|
| 95 |
+
audio_path=audio_paths[i] if include_audio else None,
|
| 96 |
image_path=None,
|
| 97 |
cefr_level=cefr_level,
|
| 98 |
)
|
| 99 |
+
for i, (text, translation) in enumerate(zip(texts, translations))
|
| 100 |
]
|
| 101 |
|
| 102 |
+
if include_audio:
|
| 103 |
+
yield 100, "Translation and audio complete!", cards
|
| 104 |
+
else:
|
| 105 |
+
yield 100, "Translation complete!", cards
|