bif26/Local_DE_Coach

A local, offline German pronunciation coach

0

stars

18

commits

Python

primary language

Sep 13, 2026

updated

bif26.github.io/Local_DE_Coach/

README

Local DE Coach Logo

Local DE Coach

A RAM-budgeted German pronunciation coach for Goethe & ÖSD exam preparation

Architecture Docs Latest Release License: MIT Arch Linux Python 3.14 SvelteKit

Runs offline on an Intel i5-1135G7 + 8 GB RAM laptop with a hard 2 GB RAM ceiling. No GPU required. Combines Wav2Vec2 + Whisper dual-model ASR, a 5-layer adaptive scorer (A1–C1), SQLite persistence, and SM-2 spaced repetition — all on localhost.


📊 Project Fingerprint

MetricValue
RAM ceiling< 2 GB (peak ~1.5 GB with Wav2Vec2, ~420 MB with Whisper)
Target levelsA1–C1 (Goethe & ÖSD aligned)
Scoring latency≤ 2× audio duration (5s clip → ~2s scoring)
Scoring layers5 (Levenshtein → word diff → suffix → umlaut → phonetic)
ModelsWav2Vec2 large xlsr-53-german + Whisper tiny int8
PersistenceSQLite (WAL mode) + SM-2 spaced repetition
BackendPython 3.14 + FastAPI + torch CPU-only
FrontendSvelteKit 4 + Tailwind CSS
ControllerAlways-on orchestrator (port 8766, ~80 MB)
Backend port8765 (on demand, started/stopped by controller)
ArchitectureController + Backend two-tier (start/stop from webUI)
Freeze detectionCPU >95% for 30s OR RAM >2.5 GB → kill backend
Idle auto-stop15 min configurable

✨ Features

🎙️ Dual-Model ASR with Dynamic Swap

  • /api/scoreWav2Vec2 (character-level phonetic accuracy, ~1.2 GB RAM)
  • /api/liveWhisper tiny int8 (streaming, ~150 MB RAM)
  • Engine auto-swaps between them — never both in RAM simultaneously → stays under 2 GB ceiling

📊 5-Layer Adaptive Scorer (A1–C1)

  • Layer 1: Global Levenshtein ratio (A1+)
  • Layer 2: Word tracking — missing/extra/substituted words (A1+)
  • Layer 3: German suffix quality — dropped endings (-en, -er, -e) (A2+)
  • Layer 4: Umlaut accuracy — ä/ö/ü vs a/o/u confusion (B1+)
  • Layer 5: Phonetic features — final devoicing, vowel length (C1 only)

🧠 Spaced Repetition (SM-2)

  • Same algorithm as Anki
  • Words you keep mispronouncing are surfaced for targeted re-practice
  • Ease factor + interval + reps tracking per word

💾 SQLite Persistence

  • Every session logged (target text, transcript, score, layers, feedback)
  • WAL mode for concurrent read/write
  • Weekly trend, top 10 error words, level-up readiness indicator

🎛️ Controller Architecture

  • Always-on controller (port 8766, ~80 MB) serves the frontend + orchestrates the backend
  • Backend on demand (port 8765, ~1.5 GB) — only runs when you're practicing
  • Start/Stop from the webUI System page
  • Live backend logs streamed via SSE
  • Analytics page with CPU/RAM charts

🛡️ Auto-Recovery & Freeze Detection

  • Idle auto-stop: backend stops after 15 min of no API activity → frees ~1.5 GB RAM
  • CPU freeze: kills backend if CPU >95% for 30 consecutive seconds
  • RAM circuit breaker: kills backend if RAM >2.5 GB
  • Health endpoint unresponsive: kills backend if /api/status doesn't respond for 30s

🌐 Web UI (SvelteKit + Tailwind)

  • 9 routes: Dashboard, Practice, Shadowing, Live, Progress, SRS Trainer, Analytics, Settings, System
  • Live RAM/health indicator (green/yellow/red) in nav bar
  • Color-coded feedback (green = perfect, yellow = suffix issues, red = missing words)
  • Dark mode + mobile responsive

📦 Smart Setup

  • Idempotent: re-running never re-downloads what's already there
  • 7 modes: full / --quick / --offline / --models-only / --dev-deps / --check / --force
  • aria2c for parallel downloads on slow connections
  • HuggingFace models cached in ~/.cache/huggingface (survives venv rebuild)

🏗️ Architecture

┌─────────────────────────────────────────────────────────────────────┐
│ Browser → http://127.0.0.1:8766                                    │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │ Controller (port 8766, ALWAYS ON, ~80 MB)                │     │
│  │  - Serves SvelteKit static frontend                     │     │
│  │  - /api/controller/status, start, stop, logs, analytics  │     │
│  │  - Proxies /api/* → backend (port 8765)                  │     │
│  │  - Monitors backend CPU/RAM, kills on freeze            │     │
│  │  - Auto-stops backend after 15 min idle                  │     │
│  └──────────────────────────────────────────────────────────┘     │
│                          │ spawns/kills subprocess                 │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │ Backend (port 8765, ON DEMAND)                           │     │
│  │  - /api/score → Wav2Vec2 (~1.2 GB, character-level)      │     │
│  │  - /api/live  → Whisper tiny int8 (~150 MB, streaming)  │     │
│  │  - 5-layer adaptive scorer (A1-C1)                       │     │
│  │  - SQLite + SM-2 SRS                                     │     │
│  └──────────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────────────┘

🚀 Quick Start (Arch Linux)

Prerequisites

  • Arch Linux (kernel 6.x)
  • Python 3.12, 3.13, or 3.14
  • Node.js 20+
  • ~2 GB free disk for model weights

Setup + Run

git clone https://github.com/bif26/Local_DE_Coach.git
cd Local_DE_Coach
./setup.sh          # one-time setup (idempotent)
./start.sh          # starts controller on http://127.0.0.1:8766

Then open http://127.0.0.1:8766 in your browser, go to the System page, click Start backend.

Update from upstream

git pull origin main
./setup.sh          # only downloads what changed
./start.sh

Stop

./stop.sh           # stops controller + backend

📂 Project Structure

Local_DE_Coach/
├── backend/
│   ├── server.py              # FastAPI backend (port 8765)
│   ├── controller.py          # Always-on orchestrator (port 8766)
│   ├── config.py              # Settings (ports, RAM, model IDs)
│   ├── db.py                  # SQLite access layer
│   ├── launcher.py            # Optional pystray system-tray
│   ├── api/
│   │   ├── routes_score.py    # POST /api/score
│   │   ├── routes_live.py     # POST /api/live (SSE)
│   │   ├── routes_progress.py # GET /api/progress, /api/srs/*
│   │   └── routes_system.py   # GET /api/status, POST /api/warmup
│   ├── core/
│   │   ├── engine.py          # Wav2Vec2 + Whisper dynamic swap
│   │   ├── preprocessor.py    # 16kHz + VAD + normalize
│   │   ├── scorer.py          # 5-layer scorer, takes level param
│   │   ├── phonetics.py       # German suffix/umlaut rules
│   │   └── srs.py             # SM-2 scheduler
│   ├── data/
│   │   ├── schema.sql         # users, sessions, word_errors, srs_queue
│   │   └── exam_corpus/       # Goethe + ÖSD reference texts
│   └── tests/
│       └── test_scorer.py
├── frontend/                  # SvelteKit + Tailwind
│   └── src/
│       ├── lib/               # api.ts, controller-api.ts, components/
│       └── routes/            # 9 routes
├── docs/                      # Documentation (Markdown + PDF + Web UI)
│   ├── markdown/              # ← Source of truth (19 chapters)
│   ├── index.html             # Web UI (renders Markdown at runtime)
│   ├── architecture.pdf       # 33-page PDF spec
│   └── assets/                # CSS, JS, images, logo
├── systemd/                   # Controller + backend services
├── setup.sh                   # Idempotent setup (7 modes)
├── start.sh                   # Start controller (+ optional --dev)
├── stop.sh                    # Stop everything
└── README.md

📊 RAM Budget

ComponentRAMNotes
Controller (always on)~80 MBServes frontend + orchestrates
Backend (on demand) — cold~190 MBuvicorn baseline, no model
Backend — scoring (Wav2Vec2)~1,470 MBWav2Vec2 large loaded
Backend — live (Whisper)~420 MBWhisper tiny int8 loaded
Idle auto-stopDefault 15 min, configurable
Hard ceiling2,000 MBController kills at 2,500 MB

🎯 Scoring Layers (CEFR-adaptive)

LayerFocusActive fromPass threshold
1. GlobalLevenshtein ratio (0–100%)A160%
2. Word trackingMissing / extra / substituted wordsA165%
3. Suffix qualityDropped endings (-en, -er, -e, -n, -s)A275%
4. Umlaut accuracyä/ö/ü vs a/o/u confusionB180%
5. Phonetic featuresFinal devoicing, vowel lengthC185%

📖 Documentation

FormatLocationBest for
Markdown (source of truth)docs/markdown/Reading, editing, searching
Web UIbif26.github.io/Local_DE_CoachInteractive browsing with charts, search, dark mode
PDFdocs/architecture.pdfPrinting, offline reading

🔧 Setup Modes

./setup.sh                # full (default) — installs only what's missing
./setup.sh --quick        # skip pacman + skip model pre-download
./setup.sh --offline      # skip ALL downloads; use only cached deps
./setup.sh --models-only  # only download ASR models
./setup.sh --dev-deps     # only install pytest + httpx
./setup.sh --check        # check what's missing; don't install anything
./setup.sh --force        # re-download and rebuild everything

📋 API Reference

Controller endpoints (port 8766, always on)

EndpointMethodPurpose
/api/controller/statusGETBackend running? RAM, CPU, idle
/api/controller/startPOSTStart backend
/api/controller/stopPOSTStop backend
/api/controller/logsGET (SSE)Live log stream
/api/controller/analyticsGETCPU/RAM charts

Backend endpoints (port 8765, proxied through 8766)

EndpointMethodPurpose
/api/scorePOSTScore audio (uses Wav2Vec2)
/api/livePOST (SSE)Live transcription (uses Whisper)
/api/progressGETWeekly trend, top errors
/api/srs/queueGETWords due for review
/api/statusGETRAM, model, health

Interactive docs at http://127.0.0.1:8766/docs (Swagger UI).


📦 Releases

VersionHighlights
v0.6.3Rebuilt docs UI: no sidebar on cover, new indigo theme, AI logo
v0.6.0PDF spec implemented: dual-model auto-swap engine
v0.5.0Whisper-only architecture
v0.4.0Controller architecture, live logs, analytics
v0.1.0Initial release

📄 License

MIT — see LICENSE.

🙏 Acknowledgements

Contributors

bif26

18 commits

bif26/Local_DE_Coach

A local, offline German pronunciation coach

0

stars

18

commits

Python

primary language

Sep 13, 2026

updated

bif26.github.io/Local_DE_Coach/

README

Local DE Coach Logo

Local DE Coach

A RAM-budgeted German pronunciation coach for Goethe & ÖSD exam preparation

Architecture Docs Latest Release License: MIT Arch Linux Python 3.14 SvelteKit

Runs offline on an Intel i5-1135G7 + 8 GB RAM laptop with a hard 2 GB RAM ceiling. No GPU required. Combines Wav2Vec2 + Whisper dual-model ASR, a 5-layer adaptive scorer (A1–C1), SQLite persistence, and SM-2 spaced repetition — all on localhost.


📊 Project Fingerprint

MetricValue
RAM ceiling< 2 GB (peak ~1.5 GB with Wav2Vec2, ~420 MB with Whisper)
Target levelsA1–C1 (Goethe & ÖSD aligned)
Scoring latency≤ 2× audio duration (5s clip → ~2s scoring)
Scoring layers5 (Levenshtein → word diff → suffix → umlaut → phonetic)
ModelsWav2Vec2 large xlsr-53-german + Whisper tiny int8
PersistenceSQLite (WAL mode) + SM-2 spaced repetition
BackendPython 3.14 + FastAPI + torch CPU-only
FrontendSvelteKit 4 + Tailwind CSS
ControllerAlways-on orchestrator (port 8766, ~80 MB)
Backend port8765 (on demand, started/stopped by controller)
ArchitectureController + Backend two-tier (start/stop from webUI)
Freeze detectionCPU >95% for 30s OR RAM >2.5 GB → kill backend
Idle auto-stop15 min configurable

✨ Features

🎙️ Dual-Model ASR with Dynamic Swap

  • /api/scoreWav2Vec2 (character-level phonetic accuracy, ~1.2 GB RAM)
  • /api/liveWhisper tiny int8 (streaming, ~150 MB RAM)
  • Engine auto-swaps between them — never both in RAM simultaneously → stays under 2 GB ceiling

📊 5-Layer Adaptive Scorer (A1–C1)

  • Layer 1: Global Levenshtein ratio (A1+)
  • Layer 2: Word tracking — missing/extra/substituted words (A1+)
  • Layer 3: German suffix quality — dropped endings (-en, -er, -e) (A2+)
  • Layer 4: Umlaut accuracy — ä/ö/ü vs a/o/u confusion (B1+)
  • Layer 5: Phonetic features — final devoicing, vowel length (C1 only)

🧠 Spaced Repetition (SM-2)

  • Same algorithm as Anki
  • Words you keep mispronouncing are surfaced for targeted re-practice
  • Ease factor + interval + reps tracking per word

💾 SQLite Persistence

  • Every session logged (target text, transcript, score, layers, feedback)
  • WAL mode for concurrent read/write
  • Weekly trend, top 10 error words, level-up readiness indicator

🎛️ Controller Architecture

  • Always-on controller (port 8766, ~80 MB) serves the frontend + orchestrates the backend
  • Backend on demand (port 8765, ~1.5 GB) — only runs when you're practicing
  • Start/Stop from the webUI System page
  • Live backend logs streamed via SSE
  • Analytics page with CPU/RAM charts

🛡️ Auto-Recovery & Freeze Detection

  • Idle auto-stop: backend stops after 15 min of no API activity → frees ~1.5 GB RAM
  • CPU freeze: kills backend if CPU >95% for 30 consecutive seconds
  • RAM circuit breaker: kills backend if RAM >2.5 GB
  • Health endpoint unresponsive: kills backend if /api/status doesn't respond for 30s

🌐 Web UI (SvelteKit + Tailwind)

  • 9 routes: Dashboard, Practice, Shadowing, Live, Progress, SRS Trainer, Analytics, Settings, System
  • Live RAM/health indicator (green/yellow/red) in nav bar
  • Color-coded feedback (green = perfect, yellow = suffix issues, red = missing words)
  • Dark mode + mobile responsive

📦 Smart Setup

  • Idempotent: re-running never re-downloads what's already there
  • 7 modes: full / --quick / --offline / --models-only / --dev-deps / --check / --force
  • aria2c for parallel downloads on slow connections
  • HuggingFace models cached in ~/.cache/huggingface (survives venv rebuild)

🏗️ Architecture

┌─────────────────────────────────────────────────────────────────────┐
│ Browser → http://127.0.0.1:8766                                    │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │ Controller (port 8766, ALWAYS ON, ~80 MB)                │     │
│  │  - Serves SvelteKit static frontend                     │     │
│  │  - /api/controller/status, start, stop, logs, analytics  │     │
│  │  - Proxies /api/* → backend (port 8765)                  │     │
│  │  - Monitors backend CPU/RAM, kills on freeze            │     │
│  │  - Auto-stops backend after 15 min idle                  │     │
│  └──────────────────────────────────────────────────────────┘     │
│                          │ spawns/kills subprocess                 │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │ Backend (port 8765, ON DEMAND)                           │     │
│  │  - /api/score → Wav2Vec2 (~1.2 GB, character-level)      │     │
│  │  - /api/live  → Whisper tiny int8 (~150 MB, streaming)  │     │
│  │  - 5-layer adaptive scorer (A1-C1)                       │     │
│  │  - SQLite + SM-2 SRS                                     │     │
│  └──────────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────────────┘

🚀 Quick Start (Arch Linux)

Prerequisites

  • Arch Linux (kernel 6.x)
  • Python 3.12, 3.13, or 3.14
  • Node.js 20+
  • ~2 GB free disk for model weights

Setup + Run

git clone https://github.com/bif26/Local_DE_Coach.git
cd Local_DE_Coach
./setup.sh          # one-time setup (idempotent)
./start.sh          # starts controller on http://127.0.0.1:8766

Then open http://127.0.0.1:8766 in your browser, go to the System page, click Start backend.

Update from upstream

git pull origin main
./setup.sh          # only downloads what changed
./start.sh

Stop

./stop.sh           # stops controller + backend

📂 Project Structure

Local_DE_Coach/
├── backend/
│   ├── server.py              # FastAPI backend (port 8765)
│   ├── controller.py          # Always-on orchestrator (port 8766)
│   ├── config.py              # Settings (ports, RAM, model IDs)
│   ├── db.py                  # SQLite access layer
│   ├── launcher.py            # Optional pystray system-tray
│   ├── api/
│   │   ├── routes_score.py    # POST /api/score
│   │   ├── routes_live.py     # POST /api/live (SSE)
│   │   ├── routes_progress.py # GET /api/progress, /api/srs/*
│   │   └── routes_system.py   # GET /api/status, POST /api/warmup
│   ├── core/
│   │   ├── engine.py          # Wav2Vec2 + Whisper dynamic swap
│   │   ├── preprocessor.py    # 16kHz + VAD + normalize
│   │   ├── scorer.py          # 5-layer scorer, takes level param
│   │   ├── phonetics.py       # German suffix/umlaut rules
│   │   └── srs.py             # SM-2 scheduler
│   ├── data/
│   │   ├── schema.sql         # users, sessions, word_errors, srs_queue
│   │   └── exam_corpus/       # Goethe + ÖSD reference texts
│   └── tests/
│       └── test_scorer.py
├── frontend/                  # SvelteKit + Tailwind
│   └── src/
│       ├── lib/               # api.ts, controller-api.ts, components/
│       └── routes/            # 9 routes
├── docs/                      # Documentation (Markdown + PDF + Web UI)
│   ├── markdown/              # ← Source of truth (19 chapters)
│   ├── index.html             # Web UI (renders Markdown at runtime)
│   ├── architecture.pdf       # 33-page PDF spec
│   └── assets/                # CSS, JS, images, logo
├── systemd/                   # Controller + backend services
├── setup.sh                   # Idempotent setup (7 modes)
├── start.sh                   # Start controller (+ optional --dev)
├── stop.sh                    # Stop everything
└── README.md

📊 RAM Budget

ComponentRAMNotes
Controller (always on)~80 MBServes frontend + orchestrates
Backend (on demand) — cold~190 MBuvicorn baseline, no model
Backend — scoring (Wav2Vec2)~1,470 MBWav2Vec2 large loaded
Backend — live (Whisper)~420 MBWhisper tiny int8 loaded
Idle auto-stopDefault 15 min, configurable
Hard ceiling2,000 MBController kills at 2,500 MB

🎯 Scoring Layers (CEFR-adaptive)

LayerFocusActive fromPass threshold
1. GlobalLevenshtein ratio (0–100%)A160%
2. Word trackingMissing / extra / substituted wordsA165%
3. Suffix qualityDropped endings (-en, -er, -e, -n, -s)A275%
4. Umlaut accuracyä/ö/ü vs a/o/u confusionB180%
5. Phonetic featuresFinal devoicing, vowel lengthC185%

📖 Documentation

FormatLocationBest for
Markdown (source of truth)docs/markdown/Reading, editing, searching
Web UIbif26.github.io/Local_DE_CoachInteractive browsing with charts, search, dark mode
PDFdocs/architecture.pdfPrinting, offline reading

🔧 Setup Modes

./setup.sh                # full (default) — installs only what's missing
./setup.sh --quick        # skip pacman + skip model pre-download
./setup.sh --offline      # skip ALL downloads; use only cached deps
./setup.sh --models-only  # only download ASR models
./setup.sh --dev-deps     # only install pytest + httpx
./setup.sh --check        # check what's missing; don't install anything
./setup.sh --force        # re-download and rebuild everything

📋 API Reference

Controller endpoints (port 8766, always on)

EndpointMethodPurpose
/api/controller/statusGETBackend running? RAM, CPU, idle
/api/controller/startPOSTStart backend
/api/controller/stopPOSTStop backend
/api/controller/logsGET (SSE)Live log stream
/api/controller/analyticsGETCPU/RAM charts

Backend endpoints (port 8765, proxied through 8766)

EndpointMethodPurpose
/api/scorePOSTScore audio (uses Wav2Vec2)
/api/livePOST (SSE)Live transcription (uses Whisper)
/api/progressGETWeekly trend, top errors
/api/srs/queueGETWords due for review
/api/statusGETRAM, model, health

Interactive docs at http://127.0.0.1:8766/docs (Swagger UI).


📦 Releases

VersionHighlights
v0.6.3Rebuilt docs UI: no sidebar on cover, new indigo theme, AI logo
v0.6.0PDF spec implemented: dual-model auto-swap engine
v0.5.0Whisper-only architecture
v0.4.0Controller architecture, live logs, analytics
v0.1.0Initial release

📄 License

MIT — see LICENSE.

🙏 Acknowledgements

Contributors

bif26

18 commits

Languages

Python

43.0%

Svelte

33.3%

Shell

15.7%

TypeScript

6.0%