Spaces:
Paused
Paused
File size: 10,877 Bytes
f495ad5 c054869 f495ad5 e308c53 f495ad5 e308c53 f495ad5 c5e89ef f495ad5 53ace40 e308c53 53ace40 e308c53 53ace40 c054869 f495ad5 c054869 f495ad5 c054869 c5e89ef c054869 c5e89ef c054869 c5e89ef f495ad5 e308c53 f495ad5 c054869 e308c53 f495ad5 c054869 f495ad5 c054869 c5e89ef c054869 c5e89ef f495ad5 c054869 f495ad5 c054869 c5e89ef e308c53 c5e89ef f495ad5 c054869 f495ad5 34e6427 f495ad5 c054869 f495ad5 c054869 c5e89ef c054869 c5e89ef c054869 f495ad5 c5e89ef 53ace40 c5e89ef f495ad5 e308c53 f495ad5 c054869 f495ad5 e308c53 f495ad5 c054869 f495ad5 c5e89ef f495ad5 c054869 c5e89ef c054869 e308c53 c054869 f495ad5 c5e89ef f495ad5 c054869 f495ad5 c054869 f495ad5 c054869 c5e89ef e308c53 c5e89ef | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | # Tech Stack
## Architecture (Hackathon-Simple)
Multi-module Gradio app. Everything runs in one process on a GPU-enabled HF Space.
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Hugging Face Space (GPU: T4 or A10G) β
β β
β app.py (Gradio UI + wiring) β
β βββ voice_clone.py β
β β βββ Qwen3-TTS-1.7B (voice clone + TTS) β
β βββ tts.py (unified TTS: Qwen3 or Supertonic) β
β β βββ Supertonic TTS (stock voice fallback) β
β βββ inference.py β
β β βββ Whisper-small (ASR for child questions) β
β β βββ Qwen2.5-3B-Instruct (story Q&A) β
β βββ stories/ (10 .txt files, public domain) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
No database. No external storage. No external LLM API. Stories are flat files. Audio is generated as interruptible chunks, then cached in the session for replay and resume.
**VRAM Budget (T4 β 16 GB):**
| Component | Estimated VRAM | Notes |
|---|---|---|
| Qwen3-TTS-1.7B (fp16) | ~3.5 GB | Loaded on demand when cloning starts |
| Qwen2.5-3B-Instruct (4-bit) | ~2 GB | Loaded on demand for Q&A |
| Whisper-small | ~1 GB | Loaded on demand for voice questions |
| Supertonic (ONNX) | ~0.3 GB | Loaded on demand as fallback |
| Gradio + PyTorch overhead | ~1β2 GB | Runtime |
| **Total** | **~8β9 GB** | ~7 GB headroom for KV cache and activations |
**Concurrency:** Single-process Gradio serializes concurrent users. The hackathon demo is single-user. For multi-user, consider Gradio queue or separate worker processes.
---
## 1. Front-End
| Choice | Why |
|---|---|
| Gradio 5.x | Zero frontend code, instant HF Space deploy |
| `gr.Server` | Custom CSS/JS for Stitch-style polish (animations, palette, layout) |
| `gr.Audio` | Record/upload parent voice sample |
| `gr.Dropdown` / `gr.Gallery` | Story selection with cover art |
| `gr.Audio` (output) | Playback of streamed story chunks / Q&A answer |
| `gr.Textbox` + `gr.Audio` (input) | Child question via text or voice |
| Play/Pause/Ask buttons | Manual interruption and resume without open-mic barge-in |
**UI Tabs:**
1. **π€ Clone Voice** β record/upload 15β30s, preview clone
2. **π Listen** β pick story, hear streamed chunks in cloned voice
3. **β Ask** β pause narration, ask about the story, hear answer, resume
---
## 2. Voice Model (Qwen3-TTS-1.7B)
| Aspect | Detail |
|---|---|
| Model | `Qwen/Qwen3-TTS-12Hz-1.7B-Base` from Hugging Face Hub |
| Size | 1.7B params β fits on T4 in fp16 (~3.5 GB VRAM) |
| Capability | Zero-shot voice cloning via speaker embedding extraction + TTS synthesis |
| Input | Reference audio (β₯5s) for cloning; text + cached voice profile for synthesis |
| Output | WAV audio in cloned voice (24 kHz) |
| Latency | ~30s for speaker embedding extraction; ~25β50s per sentence synthesis on T4 |
| Optimization | Cache voice profile after recording (UUID-keyed server-side dict); generate story audio in sentence-level chunks streamed via background thread |
| Fallback | Supertonic TTS with stock voices (F1βF5, M1βM5) when no voice profile exists |
| Module | `voice_clone.py` (model + profile cache), `tts.py` (unified streaming API) |
---
## 3. ASR (Child Voice Input)
| Choice | Detail |
|---|---|
| Model | Whisper-small checkpoint via Transformers (local, 244M params) |
| Why | Fast, accurate for short child utterances; fits in GPU alongside TTS |
| Load strategy | Load only when the Ask tab receives audio; text questions bypass ASR |
| Fallback | Whisper-tiny/base or browser transcription if GPU memory or latency is tight |
---
## 4. Q&A (Story Comprehension)
| Choice | Detail |
|---|---|
| Model | `Qwen/Qwen2.5-3B-Instruct` |
| Why | Strong small-model instruction following with lower latency and VRAM pressure than an 8B-class model |
| Method | Current story position + relevant story passages + strict answer-from-story instruction + child question β short answer |
| Retrieval | Keyword overlap + proximity bonus: score each paragraph by shared words with the question plus a bonus for paragraphs near the current playback position; return top-3 as context. Full-story fallback when no context found. |
| Output cap | 1β2 child-friendly sentences, typically 40β80 new tokens |
| Runtime note | Use 4-bit/8-bit loading on T4; use bf16 or 8-bit on A10G for more headroom |
---
## 5. Stories (Content)
10 public domain children's stories stored as `.txt` in `stories/`, sourced from [Project Gutenberg](https://www.gutenberg.org/) via the `story_downloader/` pipeline:
| Story | Words | Author/Tradition |
|---|---|---|
| The Tale of Peter Rabbit | 948 | Beatrix Potter |
| The Tale of Benjamin Bunny | 1,118 | Beatrix Potter |
| The Tale of Jemima Puddle-Duck | 1,245 | Beatrix Potter |
| The Tale of Tom Kitten | 691 | Beatrix Potter |
| The History of Tom Thumb | 2,912 | Traditional |
| The Story of the Three Little Pigs | 956 | Traditional |
| The Little Red Hen | 1,295 | Traditional |
| The Little Gingerbread Man | 1,823 | Traditional |
| The Sleeping Beauty | 1,783 | Traditional |
| The Adventures of Puss in Boots | 503 | Traditional (verse) |
Each file: title on line 1, blank line, then story prose β ready for direct TTS chunking. No metadata DB needed.
**Story Pipeline (`story_downloader/`):**
- `gutenberg_downloader.py` β reusable downloader/parser for Project Gutenberg texts
- `download_stories.py` β fetches 10 specific children's stories by Gutenberg ID
- `clean_stories.py` β strips Gutenberg headers/footers, illustration tags, and metadata for TTS-clean output
---
## 6. Deployment
| What | How |
|---|---|
| Platform | Hugging Face Spaces |
| SDK | Gradio |
| Hardware | T4 with quantized Qwen for the budget path; A10G for lower risk live demos |
| Deploy | `git push` to HF Space repo |
| Secrets | None for LLM inference; `HF_TOKEN` only if any selected model requires gated access |
| Domain | `huggingface.co/spaces/{user}/readbookmom` |
---
## 7. Latency Plan
| Flow | Target | Implementation |
|---|---|---|
| Voice setup | One-time after recording | Compute and cache the voice representation before story generation. |
| Story narration start | First streamed chunk in β€ 5s | Split the story into paragraph chunks; synthesize and play the first chunk first. |
| Narration interruption | Pause in β€ 500ms after Ask tap | Stop playback, preserve current chunk index, and cancel or deprioritize queued narration jobs. |
| Q&A interruption loop | Spoken answer starts in β€ 8s | Use current story position, retrieve relevant passages, cap answer length, then synthesize the final answer. |
| Story resume | β€ 1s when next chunk is cached | Resume from the paused chunk or the next queued chunk after the answer finishes. |
| Story replay | Immediate after first generation | Cache generated audio by voice session and story ID. |
| Child audio transcription | 1β2s target | Load ASR only for audio questions; prefer lighter ASR fallback for demo mode. |
| Q&A text answer | 1β3s target | Send only relevant story passages to Qwen and cap output tokens. |
| Spoken Q&A answer | β€ 8s total target | Synthesize only the final short answer, not intermediate reasoning or context. |
## 8. Interaction State
| State | Meaning | Key Data |
|---|---|---|
| `playing` | Story chunk is currently playing. | `story_id`, `voice_session_id`, `current_chunk_index` |
| `paused` | Playback is paused by user action. | Current chunk, elapsed position if available |
| `asking` | Narration is interrupted while the child asks a question. | Current chunk, relevant passages, pending ASR input |
| `answering` | Qwen answer or answer TTS is being generated. | Question text, short answer, answer audio path |
| `resuming` | Answer finished and story playback is restarting. | Resume chunk index, cached next chunk |
| `finished` | Story narration completed. | Cached full-story audio |
**Legal transitions:**
```
playing β paused β playing
playing β asking β answering β resuming β playing
playing β finished
paused β asking β answering β resuming β playing
asking β asking (child asks a follow-up before answer starts)
```
All other transitions are illegal. The UI should disable buttons that would trigger an illegal transition.
---
## 9. Local Dev
```bash
pip install -r requirements.txt
python app.py
# β http://localhost:7860
```
No Docker, no DB, no infra setup. Requires GPU for voice cloning and Q&A inference.
---
## 10. Dependencies
```
gradio>=5.0
transformers
torch
accelerate
bitsandbytes
soundfile
numpy
supertonic>=1.3.1
onnxruntime>=1.18.0
huggingface-hub>=0.23.0
qwen-tts>=0.1.1
```
---
## 11. Google Stitch UI Customization (via gr.Server)
`gr.Server` injects custom HTML/CSS/JS to achieve Stitch-quality polish:
- **Custom CSS**: Rounded cards, warm color palette (#FFB347 accent, #FFF8E7 background), playful fonts (Nunito/Fredoka)
- **Micro-animations**: Fade-in on story cards, pulse on recording button, waveform visualization
- **Layout overrides**: Full-bleed hero on clone tab, grid gallery for stories
- **Custom favicon + title**: Branded for demo presentation
All in a `static/` folder loaded by gr.Server mount.
---
## 12. Review Notes
| Area | Critique | Upgrade |
|---|---|---|
| Privacy | Using an external Q&A API would undermine the local-inference claim. | Qwen2.5-3B-Instruct keeps questions, story text, and generated answers inside the Space runtime. |
| GPU fit | Qwen3-TTS-1.7B, Whisper-small, and Qwen2.5-3B-Instruct are a realistic fit for T4 with lazy loading and quantization, but concurrent use can pressure VRAM. | All models lazy-loaded on demand. Quantize Qwen Q&A on T4. Use A10G for demo headroom. |
| Latency | Qwen3-TTS synthesis takes ~25β50s per sentence, making full live narration impractical. | Cache voice profile after cloning. Stream sentence chunks. Pre-generate next chunk while current plays. Keep Q&A answers to 1β2 sentences. |
| Interaction | Streaming without cancellation can still feel rigid if the child must wait for a chunk to finish. | Add explicit playback state, Ask interruption, queued job cancellation/deprioritization, and resume from the saved chunk. |
| Dependencies | External LLM SDKs and API secrets are no longer aligned with the model choice. | Use local inference dependencies and optional HF authentication only. |
|