Spaces:
Runtime error
Runtime error
Commit History
-
feat(dictation): word-level dictation scoring with tolerant normalization (#8)
Arthur_DiazUnverified signature## What & why The ML core of M2 dictation: compares the learner's typed answer to the reference and produces a word-level diagnosis. Pure and network-free, reused by M5 (pronunciation). ## Design - Tolerance calibrated to what dictation tests — hearing/spelling the right word. Case, punctuation, spacing ignored; contractions ("don't" = "do not") and US/UK spelling ("colour" = "color") accepted as transcription variants; the word itself counts ("sheep" vs "ship") — the signal dictation must catch. - Word-aligned feedback via Levenshtein backtrace (jiwer): reference shown with substitutions/omissions/insertions marked, plus WER and an error breakdown. ## Changes - services/dictation.py: normalize_words, score_dictation → DictationResult (WER, S/D/I/H counts, per-word ops) - main.py: render_dictation_feedback (marked reference + breakdown), pure+tested - Tests: +8 (82 total) — normalization, perfect-despite-variants, phonetic confusion caught, deletion/insertion alignment, empty hypot…f6bff07 -
feat(asr): faster-whisper ASR client behind the ASRClient Protocol (#7)
Arthur_DiazUnverified signature## What & why Implements the M2 ASR service: faster-whisper (CTranslate2, int8, no torch) behind the existing ASRClient Protocol, with a config-driven factory mirroring create_llm_client. Model = small, per the PR-1 latency benchmark (ADR 0004). ## Design - Lazy model load (construction is free; the ~480 MB model loads on first use) - Synchronous transcription runs in a worker thread (asyncio.to_thread) so it never blocks the event loop - All failures (model load, decode, backend) are wrapped in ASRError — the app surfaces a clean message instead of crashing - Decoding via faster-whisper's bundled ffmpeg (file path in, no soundfile in the request path) ## Changes - services/asr/faster_whisper_client.py: FasterWhisperASRClient - services/asr/factory.py: create_asr_client (fake | faster_whisper) - services/asr/base.py: +ASRError - config/.env: ASR_PROVIDER, ASR_MODEL, ASR_COMPUTE_TYPE, ASR_CPU_THREADS - Diagnostics: "ASR quick check" (upload/mic → transcription; lazy, graceful) -…e667bd0 -
feat(reading): add cloze (fill-in-the-blank) as a second exercise type (#5)
Arthur_DiazUnverified signature## What & why Adds fill-in-the-blank (cloze) exercises alongside comprehension questions, selectable per generation. The verified text is shared between activity types (generated once, cached), so a cloze on an already-seen text costs only the cloze call. Blanks are chosen pedagogically by the LLM — verbs, prepositions, connectors, collocations, the levers of real mastery — with plausible distractors of the same kind. ## Robustness The LLM is not trusted for indexing. `locate_blanks` validates every proposed answer as a verbatim whole-word match against the text, computes the character offset Python-side, consumes repeated words at distinct positions, and drops anything not found — the cloze analogue of the ONNX fp32 faithfulness gate. One malformed-JSON retry, then a clear error. Free-text scoring is case/accent tolerant ("Café " matches "cafe"). ## UX - Cloze hides the intact text (it would reveal every answer) and shows only the gapped version; comprehension-questions mode still s…8a8cb44
-
feat: M0 walking skeleton (app, service protocols, CI/CD, Space deploy)
- Gradio app: one tab per skill + diagnostics tab with LLM ping - Provider-agnostic services via typing.Protocol + tested fakes (single OpenAI-compatible LLM client: Gemini/Mistral/Ollama/OpenAI) - Env-driven config (pydantic-settings), offline-safe defaults - CI: ruff + pytest, Docker image pushed to GHCR on main - Repo sync workflow to HF Space (Docker SDK, cpu-basic) - ADRs: stack & architecture, LLM provider strategy, datasets & licensing00d5ae5