Skip to main content
Glama
NightPrinceY

Quran Recitation Validator

by NightPrinceY
README.md
# مُدَقِّق التِّلاوة القُرآنية — Quran Recitation Validator v2.2

> **Validates Arabic Quran recitations** — single verse, full surah, juz, page, or any consecutive verse range.
> Supports standard Arabic, Uthmani script, and full tashkeel (harakat) validation.

![Architecture](architecture.svg)

---

## Features

| Feature | Detail |
|---|---|
| **Single-verse** | Finds + validates any of the 6,236 Quran verses |
| **Multi-verse** | Full surah, juz, page, or arbitrary consecutive range |
| **Tashkeel** | Per-word harakat comparison (فتحة، ضمة، كسرة، مدة، شدة، سكون) |
| **Uthmani input** | Paste directly from Mushaf — normalizes `ٱلۡكِتَٰبَ` → كتاب, `الرحمٰن` → الرحمن |
| **4-layer search** | Exact → Linguistic (roots/morphology) → Relaxed → Fuzzy |
| **WER scoring** | Word Error Rate = (subs + dels + ins) / reference words |
| **Arabic feedback** | Human-readable result in Arabic |

---

## Folder Structure

```
validator/
│
├── 📄 server.py                  FastMCP 2.0 server (port 3001)
├── 📄 validator_mcp.py           Main routing: auto single ↔ multi-verse
├── 📄 normalizer.py              Arabic normalizer pipeline (7 steps)
├── 📄 quran_db.py                O(1) indexed DB (gid / sura / juz / page)
├── 📄 quran_search.py            4-layer verse search engine
├── 📄 multi_verse.py             Forward alignment for multi-verse recitation
├── 📄 tashkeel.py                Per-word harakat validation
│
├── 📂 data/
│   ├── quran.json                6,236 verses — gid, uthmani, standard, standard_full, ...  (5.1 MB)
│   ├── uthmani_standard_map.json 2,017 Uthmani→standard word pairs, corpus-derived  (70 KB) ★
│   ├── word-map.json             Arabic word → root + morphological forms  (877 KB)
│   └── morphology.json           Root index + verb/noun patterns  (2.5 MB)
│
├── 📂 tests/
│   ├── test_all.py               124 tests across 12 categories — 123/124 pass (99.2%)
│   ├── dataset_gen.py            Auto-generates 63 test cases from quran.json
│   └── dataset.json              Generated test cases (gitignored)
│
├── 🖼️  architecture.svg           System architecture diagram (this file)
├── 📄  README.md                  This file
├── 📄  Dockerfile
└── 📄  .env.example
```

---

## Architecture

The system has 6 pipeline stages (see `architecture.svg`):

```
Input Text
    ↓
[Mode Detection] → single (≤8 words) or multi (>8 words)
    ↓
[Normalizer] — 7 steps:
    ① NFC unicode
    ② Word-level map (2017 Uthmani→standard pairs) ← NEW v2.2
    ③ Remove tashkeel / Quranic marks
    ④ U+0670 contextual fallback (ٰ → ا unless ى/ذ/ه/ل)
    ⑤ Alef variants → ا   Hamza variants → ء
    ⑥ word-initial ءا → ا   ى → ي
    ⑦ Remove non-Arabic, collapse whitespace
    ↓
[Search / Alignment]
    Single: 4-layer search (exact AND → linguistic → relaxed → fuzzy)
    Multi:  detect start verse → word-by-word boundary scan → forward align
    ↓
[Word Diff] — SequenceMatcher opcodes → substitutions / deletions / insertions → WER
    ↓
[Tashkeel Check] — if user provided harakat: per-word harakat comparison
    ↓
JSON Result: {is_correct, verse_key, wer, corrections, tashkeel_errors, feedback, ...}
```

---

## Normalizer — Uthmani Script Handling

The key innovation of v2.2 is the **word-level corpus map**:

```python
# uthmani_standard_map.json — built by aligning all 6,236 verses
{
  "الرحمٰن":  "الرحمن",    # ← Bismillah fix (was "الرحمان" in v2.1)
  "الكتٰب":   "الكتاب",
  "الخٰسرون": "الخاسرون",
  "أولٰئك":   "أولئك",
  "ذٰلك":     "ذلك",
  "هٰذا":     "هذا",
  "علىٰ":     "على",
  ...  # 2,017 total entries
}
```

**Result:** 100% accuracy on all 8,107 ٰ-containing words in the Quran corpus.

---

## Run

### MCP Server (production)
```bash
uv run python server.py
# Port 3001 / SSE endpoint at /sse
```

### Tests
```bash
cd servers/validator
python3 tests/dataset_gen.py   # regenerate 63 test cases
python3 tests/test_all.py      # run all 124 tests
```

---

## API

Exposed as the MCP tool **`validate_recitation(text)`** (SSE at `:3001/sse`).
The tool returns the Arabic `feedback` string; the internal
`validate_recitation()` in `validator_mcp.py` produces the full result dict
below (single- and multi-verse shapes):

Input:
```json
{ "text": "بسم الله الرحمن الرحيم" }
```

Single-verse result:
```json
{
  "mode": "single",
  "is_correct": true,
  "verse_key": "1:1",
  "surah_name": "الفاتحة",
  "wer": 0.0,
  "corrections": [],
  "matched_verse": "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِیمِ",
  "feedback": "ممتاز! تلاوتك صحيحة تماماً.",
  "has_tashkeel": false
}
```

**Multi-verse result** (7-verse Fatiha):
```json
{
  "mode": "multi",
  "is_correct": true,
  "total_verses": 7,
  "correct_verses": 7,
  "total_wer": 0.0,
  "verses": [ {"verse_key":"1:1","is_correct":true,"wer":0.0}, ... ],
  "range": "من الفاتحة (1:1) إلى (1:7)"
}
```

---

## Test Results — v2.2

| Category | Tests | Pass |
|---|---|---|
| Normalizer unit tests | 11 | 11 ✅ |
| QuranDB unit tests | 7 | 7 ✅ |
| Single-verse perfect | 10 | 10 ✅ |
| Single-verse substitution | 4 | 3 ✅ 1 ❌¹ |
| Single-verse deletion | 3 | 3 ✅ |
| Single-verse tashkeel | 6 | 6 ✅ |
| Multi-verse full surahs | 7 | 7 ✅ |
| Multi-verse with errors | 3 | 3 ✅ |
| Multi-verse consecutive | 6 | 6 ✅ |
| Multi-verse full pages | 5 | 5 ✅ |
| Edge cases | 5 | 5 ✅ |
| Dataset-driven | 62 | 62 ✅ |
| **Total** | **124** | **123 (99.2%)** |

¹ SS03: `واحد` → finds 6:19 instead of 112:1 — wrong root in `word-map.json` source data.

---

## Known Limitations

| # | Issue | Cause | Affects |
|---|---|---|---|
| 1 | `واحد` finds 6:19 not 112:1 | Wrong root in `word-map.json` | Ikhlas v1 detection |
| 2 | Huruf muqatta'at (الم، الر) | Not searchable | Start-verse detection |
| 3 | Identical verse openings | Lower GID always wins | 2:63 vs 2:93 |
| 4 | `يَٰۤأَيُّهَا` structural split | 1 Uthmani word = 2 standard words | 338 verses w/ يا أيها |

---

## Changelog

### v2.2 (2026-03-09)
- **NEW** `data/uthmani_standard_map.json` — 2,017 corpus-derived Uthmani→standard word pairs
- **FIX** `الرحمٰن` → `الرحمن` (was `الرحمان` in v2.1)
- **FIX** All 8,107 ٰ-containing Quranic words now normalize with 100% accuracy
- Architecture SVG diagram added

### v2.1 (2026-03-09)
- **FIX** U+0670 contextual rule: `ٰ→ا` except after ى/ذ/ه/ل
- **FIX** `ءَاتَ` (Uthmani initial ءا) → standard `اتَ`
- Verse 2:121 Uthmani input now validates correctly (0 errors, was 3 errors)

### v2.0 (2026-03-09)
- Multi-verse alignment engine (`multi_verse.py`)
- Tashkeel validation (`tashkeel.py`)
- Complete Arabic normalizer (`normalizer.py`)
- O(1) QuranDB (`quran_db.py`)
- 4-layer search (`quran_search.py`)
- Test suite: 123/124 (99.2%)