savitr — fast Surya OCR on Apple Silicon, for Indian electoral rolls
savitr runs Surya OCR
(datalab-to/surya-ocr-2) on Apple Silicon via MLX. It also ships an
electoral-roll-specific model that emits one compact line per voter, plus a
pipeline that turns scanned roll PDFs into the canonical voter CSV. It runs
locally without a cloud GPU.
Install
Requirements: an Apple-Silicon Mac (M-series — the OCR runs on MLX) and poppler (used to read PDFs):
brew install poppler # macOS (Debian: sudo apt-get install poppler-utils)
Then:
pip install savitr # MLX runtime + terse roll model (auto-downloaded from HF)
pip install "savitr[train]" # + the distillation toolchain (transformers/peft/torch)
Latest from git: pip install "git+https://github.com/in-rolls/savitr"
The terse model is fetched from gojiberries/savitr on
first use (~800 MB, one time). The pure-Python parsing API (parse_terse) imports on any platform;
only the OCR itself needs Apple Silicon.
Quickstart
The distilled terse model is the default and auto-downloads on first run — a bundled sample roll lets you try it immediately:
# OCR the bundled sample roll -> per-page voter summary (works from any install)
savitr ocr "$(savitr sample)"
# ... or write the voter records straight to CSV
savitr ocr "$(savitr sample)" -o voters.csv
# whole rolls -> canonical voter CSV (a directory of *_ENG.pdf, or a single -f file)
savitr parse-rolls -d english/ -o voters.csv
from savitr import MLXSuryaOCR, parse_terse, resolve_terse_model
from savitr.rolls.parse import TERSE_PROMPT
eng = MLXSuryaOCR(
resolve_terse_model(), prompt=TERSE_PROMPT
) # downloads the model if not local
text, _ = eng.ocr_image("page.png")
voters = parse_terse(text) # [{'id': 'KMY...', 'elector_name': ..., 'age': ..., ...}]
Two models, and which one you want
Electoral rolls → the terse model. Published at
gojiberries/savitr and downloaded on first use, so
everything above works with no setup. It was distilled to emit voter rows and will emit them
whatever the page holds — it is not a general OCR.
Anything else → base Surya. Upstream publishes datalab-to/surya-ocr-2 but not an MLX build of
it, so convert it once (~1.3 GB fetched, ~500 MB written):
python -m mlx_vlm convert --hf-path datalab-to/surya-ocr-2 \
--mlx-path models/surya-mlx-4bit -q --q-bits 4
from savitr import MLXSuryaOCR
eng = MLXSuryaOCR() # finds models/surya-mlx-4bit, or $SAVITR_BASE_PATH
eng = MLXSuryaOCR("/some/other/model") # or say where
text, _ = eng.ocr_image("page.png") # HTML: <table><tr><td>…
With no converted model, every entry point — the constructor, savitr ocr --html, and
--cover-model — says the same thing and repeats the command above. savitr ocr --html needs it;
--cover-model is optional metadata and carries on without it.
terse-Surya (gojiberries/savitr)
Surya self-distilled to emit pipe-delimited voter rows. The model card is the single source for checkpoint provenance, evaluation definitions, results, and limitations.
What's in the box
Three layers — two you install and use, one for reproducing the model:
src/savitr/ # the pip package (use it)
mlx_ocr.py # GENERIC MLX Surya engine (MLXSuryaOCR) — run any Surya OCR fast
rolls/ # ELECTORAL-ROLL app: parse · fields · schema · pipeline · ocr
cli.py, __init__.py
training/ # repo-only: build_corpus · train · eval · merge · kaggle_* (reproduce the model)
- Use it — electoral rolls (the product):
savitr parse-rolls/savitr ocrrun the distilled terse model (the default) on roll PDFs → voter records / canonical CSV (savitr.rolls). - Use it — generic fast Surya:
savitr.MLXSuryaOCRruns any compatible Surya model on Apple Silicon. - Reproduce it — training/distillation: lives in top-level
training/, not shipped in the wheel (install the[train]extra to run it). We ship code to use the model, not to train it.
How it was built / what was tried
See the findings for the measured baseline (decode, not cold-start, dominates; ~110 s/page on llama.cpp), the MLX win, the tested negatives (compact prompt, guided JSON, parallelism), and the distillation method and numbers.
Develop
uv sync --all-groups --all-extras
uv run pytest
uv run ruff check .
uv run ruff format --check .
Release files for savitr 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| savitr-0.3.0.tar.gz | 748.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| savitr-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.5 MB
Release files / savitr-0.3.0.tar.gz
| Download URL | savitr-0.3.0.tar.gz |
|---|---|
| Size | 748.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dcd386167a7a5ade03b4058bd6c689233552296800675a7a4bea553e76dd8d8b
|
|
BLAKE2b-256 checksum How to use checksums |
af09a2e26d35d4539fc83ce2cec7daf33a4a4cdaac03ae47a3cbf44547fdf1f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 17, 2026.
Transparency logRelease files / savitr-0.3.0-py3-none-any.whl
| Download URL | savitr-0.3.0-py3-none-any.whl |
|---|---|
| Size | 751.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5f55afee9615d65e5ea1331cd7faf487dfb4bf2665ace69e9c5cb192ad65817f
|
|
BLAKE2b-256 checksum How to use checksums |
f29401d795b08077d3d9cc018fab7e073dc2275cec0b03afb5b1a4ea21044100
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 17, 2026.
Transparency log