API reference¶
Engine — savitr.mlx_ocr¶
Generic MLX runtime for Surya OCR — load the model once, OCR page images to text.
This roll-agnostic engine turns a page image into the model’s raw text. The
electoral-roll parsing of that text lives in savitr.rolls.
It is reusable for any Surya OCR task, not just rolls.
- savitr.mlx_ocr.BASE_REPO = 'datalab-to/surya-ocr-2'¶
Upstream Surya weights. Not published in MLX form — converted locally, once.
- savitr.mlx_ocr.BASE_PATHS = ('models/surya-mlx-4bit', '~/.cache/savitr/surya-mlx-4bit')¶
Locations searched for a converted base model, in order.
- savitr.mlx_ocr.convert_hint(looked_in)[source]¶
Build the one message every entry point gives when there is no base model.
- savitr.mlx_ocr.base_model_path(path=None)[source]¶
Return a local MLX Surya directory, or say exactly how to make one.
Savitr publishes the terse roll model, while base Surya must be converted locally. This function prevents a missing local path from being mistaken for a Hub repository ID.
The library raises
FileNotFoundError; CLI entry points turn it into aSystemExitwith the same guidance.
- class savitr.mlx_ocr.MLXSuryaOCR(mlx_path=None, max_tokens=8192, prompt='OCR this image to HTML.')[source]¶
Load an MLX-converted Surya model once; OCR page images to text.
With no
mlx_path, this looks for a converted base Surya model. Passsavitr.rolls.resolve_terse_model()for the electoral-roll model.
Electoral-roll parsing — savitr.rolls.parse¶
Convert Surya HTML or terse output to voter records.
The generic MLX engine turns page images into text. These functions parse that text into the canonical electoral-roll fields.
parse_voters()/dedupe_voters()parse the base model’s verbose HTML.parse_terse()parses the distilled terse model’s one-line-per-voter output.to_terse()renders voter dicts back to the terse training target.resolve_terse_model()locates (or downloads) the terse model weights.
- savitr.rolls.parse.TERSE_REPO = 'gojiberries/savitr'¶
Hugging Face repository for the distilled roll model.
- savitr.rolls.parse.resolve_terse_model(local='models/surya-terse-8bit')[source]¶
Resolve a local model directory or download the pinned Hub snapshot.
- savitr.rolls.parse.parse_voters(html)[source]¶
Split page HTML into voter records using the
Name :anchor.
- savitr.rolls.parse.dedupe_voters(voters)[source]¶
Collapse duplicated rows, keeping the fullest record per voter.
Keys by EPIC id when present, then serial number, then identity (name + relation + age). A serial prevents two legitimate voters with the same name and age from being collapsed. The fullest repeated record wins.
- savitr.rolls.parse.TERSE_COLS = ['number', 'id', 'elector_name', 'relationship', 'father_or_husband_name', 'house_no', 'age', 'sex']¶
Canonical column order for one terse voter line.
- savitr.rolls.parse.TERSE_PROMPT = 'Extract every voter from this electoral-roll page as pipe-delimited rows, one per line, columns: serial|epic|name|relation(F/H/M)|relation_name|house|age|sex'¶
Instruction given to the distilled model.
- savitr.rolls.parse.to_terse(voters)[source]¶
Render voter dicts to terse pipe-delimited text (the training target).
Pipeline — savitr.rolls.pipeline¶
Parse Manipur 2025 rolls into the canonical voter CSV.
The pipeline OCRs each page with MLX Surya, parses voter records, and applies the in-rolls column schema.
Use savitr parse-rolls --help for command-line options.
- savitr.rolls.pipeline.html_to_text(html)[source]¶
Convert HTML to newline-separated text for cover-field parsing.
- savitr.rolls.pipeline.parse_pdf_mlx(eng, pdf_path, dpi, terse=True, cover_eng=None)[source]¶
OCR + parse one PDF with MLX; return (rows, recon) in the canonical schema.
In terse mode,
engreads voters from every page. Ifcover_engis provided, it reads the first two pages for metadata.In HTML mode,
engreads both voters and metadata.- Parameters:
eng (MLXSuryaOCR)
pdf_path (str)
dpi (int)
terse (bool)
cover_eng (MLXSuryaOCR | None)
- Return type:
Schema — savitr.rolls.schema¶
Canonical in-rolls voter CSV schema (vendored from parse_unsearchable_rolls).
The output stays compatible with the parse_unsearchable_rolls pipeline.