Takosaga commited on
Commit
6ab1467
Β·
1 Parent(s): c8b7bf2

docs: Phase 2 translation design spec (tiny-aya)

Browse files
docs/superpowers/specs/2026-06-11-phase-2-translation-design.md ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 2 Translation β€” Design Spec
2
+
3
+ **Date:** 2026-06-11
4
+ **Status:** Approved
5
+ **Topic:** Wire tiny-aya translation into EuropaLex two-phase workflow
6
+
7
+ ## Context
8
+
9
+ EuropaLex is in Phase 1: English text generation via MiniCPM5-1B. Cards render with English on the front and a dashed placeholder on the back. Phase 2 must translate those English sentences into Latvian using the existing `LlamaCppTextEngine` (tiny-aya-water Q4_K_M), while images and audio toggles remain unchecked by default.
10
+
11
+ ## Architecture Overview
12
+
13
+ The two-phase workflow stays intact:
14
+
15
+ 1. **Phase 1** β€” User enters scenario β†’ MiniCPM5-1B generates English sentences β†’ cards render with placeholder back
16
+ 2. **Phase 2** β€” User clicks "Generate Cards" β†’ tiny-aya translates the English text β†’ cards re-render with Latvian on front, English on back
17
+
18
+ Images and audio toggles default to unchecked (`value=False`). Media parameters are absent from the pipeline API for now but will be added later when TTS and image engines are wired.
19
+
20
+ ## Components to Modify
21
+
22
+ ### `core/pipeline.py` β€” Phase 2 orchestration (new)
23
+
24
+ A single function that receives Phase 1 English texts and produces translated `CardData` objects:
25
+
26
+ ```python
27
+ def generate_phase2(
28
+ texts: list[str],
29
+ scenario: str,
30
+ cefr_level: CEFRLevel,
31
+ batch_size: int,
32
+ ) -> Iterator[tuple[int, str, list[CardData]]]:
33
+ """Yields (progress_percent, phase_label, cards) at each step."""
34
+ ```
35
+
36
+ - Calls `EnginePool.get_translation_engine().generate()` for translation
37
+ - Yields progress updates: 20% (preparing), 60% (translating), 100% (complete)
38
+ - Returns `CardData` objects with `translation` populated, `audio_path`/`image_path` empty
39
+
40
+ ### `core/engine.py` β€” Extend `LlamaCppTextEngine`
41
+
42
+ Add sentence-count validation and retry loop mirroring `MiniCPMTextEngine`:
43
+
44
+ - Wrap existing `generate()` body in a loop (max 3 attempts)
45
+ - If output line count == batch_size β†’ return `TextResult(generated_texts=lines)`
46
+ - If mismatch β†’ `_build_retry_prompt(actual_count, expected_count)` appended to conversation context, increment counter, retry
47
+ - After 3 failures β†’ raise `ValidationError(raw_output=text)`
48
+ - `_build_retry_prompt` constructs a stricter prompt referencing the actual vs expected count
49
+
50
+ ### `app.py` β€” Wire Phase 2 via pipeline
51
+
52
+ - `generate_media_async()` calls `pipeline.generate_phase2(...)` instead of using mock data
53
+ - Cards built from returned `CardData` objects with `placeholder_back=False`
54
+ - Images/audio toggles default to `value=False`
55
+ - `_enable_phase2()` and `_reset_to_idle()` updated for unchecked defaults
56
+
57
+ ## Data Flow
58
+
59
+ ```
60
+ User enters scenario β†’ Phase 1 generates English (MiniCPM5-1B)
61
+ ↓
62
+ Cards render: English front, placeholder back
63
+ ↓
64
+ User clicks "Generate Cards" β†’ generate_media_async()
65
+ ↓
66
+ pipeline.generate_phase2(texts, scenario, cefr_level, batch_size)
67
+ ↓
68
+ EnginePool.get_translation_engine().generate() with retry loop
69
+ ↓
70
+ CardData objects: {text: <English>, translation: <Latvian>, audio_path: None, image_path: None}
71
+ ↓
72
+ Cards re-render: Latvian front, English back (placeholder_back=False)
73
+ ```
74
+
75
+ ## Error Handling
76
+
77
+ - `ValidationError` from `LlamaCppTextEngine` β†’ caught in `app.py` β†’ rendered as error message (same pattern as Phase 1)
78
+ - Model not found β†’ same fallback as Phase 1
79
+ - EnginePool mutual exclusion still enforced β€” translation engine gets exclusive VRAM access
80
+
81
+ ## Constraints
82
+
83
+ - No unit test framework change β€” inline tests or smoke test only
84
+ - Follow existing import conventions: absolute imports from project root
85
+ - Max line length: 100 characters
86
+ - Type hints on all public functions
87
+ - Docstrings: one-line summary + args/returns for multi-arg functions