Spaces:
Runtime error
title: Polyglot Tutor
emoji: π
colorFrom: indigo
colorTo: green
sdk: docker
app_port: 7860
pinned: false
π Polyglot Tutor
Adaptive language tutor covering the four skills (reading, listening, writing, speaking), driven by a CEFR classifier and a predictive spaced-repetition learner model. Runs for free on a Hugging Face Space (CPU) with an optional local-GPU "premium" mode.
Status: M0 β walking skeleton. CI, Docker β GHCR, HF Space deploy and all service contracts (LLM / ASR / TTS / storage) are live; features land milestone by milestone.
TL;DR (for recruiters)
- ML core: fine-tuned multilingual CEFR (A1βC2) text classifier benchmarked against a published baseline; FSRS-style spaced repetition evaluated offline on public review logs; LLM-generated exercises gated by an LLM-as-judge.
- Engineering: provider-agnostic services (
typing.Protocol+ env-driven factories), pinned toolchain (uv, ruff, pre-commit), tested fakes, CI β GHCR, free-tier deployment with documented constraints (ephemeral disk, cold starts, rate limits). - Honest evals: every milestone ships metrics with caveats, tracked in MLflow
(see
docs/evals/).
Architecture
flowchart LR
UI[Gradio UI<br/>4 skill tabs] --> EX[Exercise generators<br/>+ cache]
EX --> LLM[LLMClient<br/>Gemini / Mistral / Ollama / fake]
EX --> ASR[ASRClient<br/>faster-whisper / remote / fake]
EX --> TTS[TTSClient<br/>edge-tts / local / fake]
EX --> CEFR[CEFR classifier<br/>ONNX int8, CPU]
UI --> SRS[Learner model<br/>FSRS + ability estimate]
SRS --> DB[(Repository<br/>memory / SQLite / Supabase)]
EX --> DB
Each box on the right is a Protocol with swappable implementations selected by environment
variables β the same image runs as the free Space ("light") or against a local GPU box over
Tailscale ("premium"). See docs/adr/ for the reasoning.
Quickstart (dev)
uv sync # runtime + dev deps (pinned via uv.lock)
uv run pre-commit install
cp .env.example .env # defaults to the offline fake provider
uv run python -m tutor.app.main # http://localhost:7860
uv run pytest
To talk to a real LLM, set in .env: LLM_PROVIDER=gemini, LLM_API_KEY=...
(free key at https://aistudio.google.com/apikey), then check the Diagnostics tab β Ping LLM.
Dataset download / EDA (kept out of the runtime image):
uv run --group data python scripts/download_data.py
Deployment
- CI (GitHub Actions) β lint + tests on every push/PR; on
main, the Docker image is pushed toghcr.io/<owner>/polyglot-tutor. - HF Space (Docker, cpu-basic) β create the Space once, then set:
- GitHub β Secrets:
HF_TOKEN(write) Β· Variables:HF_SPACE=<user>/<space> - Space β Settings:
LLM_PROVIDER,LLM_MODEL,LLM_API_KEY,GRADIO_AUTH_USERNAME,GRADIO_AUTH_PASSWORDEvery push tomainsyncs the repo to the Space, which rebuilds from the same Dockerfile (a Docker Space cannot pull the GHCR image β GHCR is the CI artifact, see ADR 0001).
- GitHub β Secrets:
Roadmap
| Milestone | Scope | Headline eval |
|---|---|---|
| M0 β | Walking skeleton: CI, GHCR, Space, Protocols + fakes | CI green, live Space |
| M1 | CEFR classifier + reading comprehension (LLM questions, cached & judge-gated) | macro-F1 / adjacent acc. / QWK vs published UniversalCEFR baseline |
| M2 | TTS + dictation with light ASR | WER (incl. non-native audio) justifying tiny vs small |
| M3 | Supabase persistence + writing correction (typed errors via ERRANT, per-learner profile) | error-type P/R on W&I+LOCNESS sample |
| M4 | Learner model: FSRS scheduling + ability estimate, next-exercise policy | AUC / log-loss / calibration on public Anki review logs |
| M5 | Pronunciation scoring + premium local-GPU mode (Tailscale) | correlation with expert scores on speechocean762 |
| M6 (stretch) | Real-time voice conversation tab (Gemini Live API behind a ConversationClient Protocol) | qualitative demo; latency budget documented |
Data & licensing
This is a non-commercial portfolio/demo project. CEFR-labeled corpora come from
UniversalCEFR (per-subset, mostly research-only
licenses β original papers cited); SRS benchmarks use the open Anki review-log datasets;
pronunciation evaluation uses speechocean762 (free for any use). No licensed text is
committed to this repo; data/ is gitignored and rebuilt by scripts/download_data.py.
Details and caveats: docs/adr/0003-datasets-and-licensing.md.