Russian KASKO insurance policy field extractor — pulls structured fields (policy terms + policyholder + contacts) from PDF policies via text-layer, tables, and OCR fallback.
Project description
polis-recognizer
A deterministic field extractor for Russian KASKO insurance policy PDFs.
Pulls 7 structured fields without LLMs — text-layer extraction
(pypdf) plus optional table-aware reading (pdfplumber), with
Tesseract OCR fallback for scanned policies.
Status: pre-stable (0.x). API may change before 1.0.
What it extracts
| Field | Type | Example |
|---|---|---|
policy_number |
str |
"AC524160804" |
policy_period |
{start, end} (date) |
{"start": date(2025, 2, 27), "end": date(2026, 2, 26)} |
franchise |
{value, currency, absent} |
{"value": 30000, "currency": "RUB", "absent": False} |
limit |
{value, currency} |
{"value": 5525000, "currency": "RUB"} |
premium |
{value, currency} |
{"value": 220000, "currency": "RUB"} |
sum_type |
"aggregate" / "non_aggregate" |
"non_aggregate" |
repair_mode |
"dealer" / "service" / "cash" |
"dealer" |
policyholder |
{type, name, inn, ogrn, kpp, passport, birth_date} |
{"type": "legal_entity", "name": "ООО \"Альфа\"", "inn": "7707083893", "ogrn": "1027700132195", "kpp": "770701001", "passport": None, "birth_date": None} |
policyholder_contacts |
{phones, emails, address, postal_code} |
{"phones": ["+74951234567"], "emails": ["contact@alpha.ru"], "address": "101000, г. Москва, ул. Ленина, д. 1", "postal_code": "101000"} |
Quick start
from polis_recognizer import PolicyExtractor
extractor = PolicyExtractor()
result = extractor.extract_from_pdf("/path/to/polis.pdf")
print(result.policy_number)
# → "AC524160804"
print(result.policy_period)
# → {"start": date(2025, 2, 27), "end": date(2026, 2, 26)}
print(result.franchise)
# → {"value": 30000.0, "currency": "RUB", "absent": False}
Input methods:
extractor.extract_from_pdf("polis.pdf")
extractor.extract_from_bytes(pdf_bytes, filename="polis.pdf")
extractor.extract_from_text("сырой текст полиса") # bypass PDF/OCR
Installation
pip install polis-recognizer
System dependencies
polis-recognizer shells out to Tesseract for OCR and to poppler
for PDF→image conversion. These are NOT pip-installable; install them
through your OS package manager.
Linux (Debian/Ubuntu):
sudo apt-get install -y tesseract-ocr tesseract-ocr-rus poppler-utils libgl1
macOS (Homebrew):
brew install tesseract tesseract-lang poppler
Windows: best-effort. Install Tesseract for Windows and add it to PATH; install Poppler binaries for PDF support. We don't test on Windows in CI.
The Russian language pack (tesseract-ocr-rus / tesseract-lang) is
required — without it, OCR silently falls back to English and Cyrillic
documents come back as garbage. The library logs a CRITICAL warning at
import time if the pack is missing.
How it works
The extractor runs three stages:
- PDF ingestion —
PdfExtractionRoutertries text-layer extraction first (pypdf for text, pdfplumber for tables on the same page). If the result is too short (fewer than 100 chars by default) or detected as glued/EDI-envelope text, it falls back to Tesseract OCR. - Text normalization — Unicode NFKC, NBSP stripping, hyphenated line-break healing, runs of multiple spaces collapsed.
- Field extraction — 7 deterministic parsers (one per field) run
regex + table-aware patterns and emit
Candidates with confidence scores. A ranker picks the winner per field.
There's no LLM and no cloud dependency. Everything runs locally.
PDF extractor choice
The default is "hybrid" — pypdf text plus pdfplumber tables in one
pass. Two alternatives:
| Option | When to use |
|---|---|
"hybrid" (default) |
Best for KASKO. pypdf preserves date/period text quality, pdfplumber's tables fix the column layout for limit/franchise/premium. |
"pypdf" |
Faster, no tables. Use when document quality is uniform and tables aren't needed. |
"pdfplumber" |
Fully layout-aware. Slower; on KASKO it slightly regresses date parsing. Use for table-heavy non-KASKO formats. |
extractor = PolicyExtractor(pdf_extractor="pypdf")
Supported insurer formats
The parser ships with patterns for these Russian insurers' KASKO templates: АльфаСтрахование (XLS form-mask), СОГАЗ-АВТО, Чулпан, Ингосстрах, ВСК, АбсолютСтрахование, Росгосстрах, СОГАЗ Diadoc-wrapped PDFs. Recall on real-world KASKO corpora is ~50-65% per field; pulling above that requires per-format parser additions.
If you have a policy from an insurer not on this list — see CONTRIBUTING.md for how to add a parser pattern.
Configuration
Constructor arguments:
extractor = PolicyExtractor(
ocr_language="rus+eng", # Tesseract language string
ocr_timeout_seconds=300,
ocr_page_limit=50,
ocr_max_text_size=500_000,
pdf_extractor="hybrid", # "pypdf" | "pdfplumber" | "hybrid"
image_preprocessing="fallback", # "never" | "fallback" | "always"
psm=None, # Tesseract --psm (None = auto)
oem=None, # Tesseract --oem (None = auto)
max_image_size_bytes=None, # reject images larger than this
extract_pii=False, # opt-in for passport + birth date
)
extract_pii
PolicyExtractor does not extract passport or birth date by default
— with extract_pii=False, policyholder.passport and
policyholder.birth_date are always None even when the source
text contains them. This keeps the default output safe to log /
cache / persist without extra redaction work. Set extract_pii=True
to opt in. Operational contact data (phone, email, address) is not
gated — those handle data the caller has a legal basis to process
under 152-ФЗ ст. 6 ч. 1 п. 5 (исполнение договора).
License
MIT © Grigorii Grachev. Free for any use, including commercial.
Roadmap
Next up: ОСАГО support — the underlying field model already accommodates it; only parser patterns need adding.
Past releases — see CHANGELOG.md. The design and implementation plan for the policyholder + contacts work shipped in 0.3.0 is preserved in docs/roadmap-policyholder.md for reference.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file polis_recognizer-0.3.0.tar.gz.
File metadata
- Download URL: polis_recognizer-0.3.0.tar.gz
- Upload date:
- Size: 95.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11a252ad7a17219e4b977c850204be215036843b0c2119321a1343c58c5b2761
|
|
| MD5 |
9f39e9b39f05836c364876242c48edc2
|
|
| BLAKE2b-256 |
b3fe92d4aa3cf2b95ee107f3a93ea3293c8217db26178c12882fc42decba3ec8
|
Provenance
The following attestation bundles were made for polis_recognizer-0.3.0.tar.gz:
Publisher:
publish.yml on grigra27/polis-recognizer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polis_recognizer-0.3.0.tar.gz -
Subject digest:
11a252ad7a17219e4b977c850204be215036843b0c2119321a1343c58c5b2761 - Sigstore transparency entry: 1607428081
- Sigstore integration time:
-
Permalink:
grigra27/polis-recognizer@e0d64155a61320a9bfa0d09cad376c6cfcc0d24f -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/grigra27
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e0d64155a61320a9bfa0d09cad376c6cfcc0d24f -
Trigger Event:
push
-
Statement type:
File details
Details for the file polis_recognizer-0.3.0-py3-none-any.whl.
File metadata
- Download URL: polis_recognizer-0.3.0-py3-none-any.whl
- Upload date:
- Size: 105.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4e0a297a027f887a4e12d0028265801ee09d94c0b935fcd8c1efc61af354599
|
|
| MD5 |
702381622ab97dfea1bbeb175a4b7937
|
|
| BLAKE2b-256 |
9ab89fc3baae77c633dd5b79fabdd9bf1bf4e44bd7f44bede91fa0a643204135
|
Provenance
The following attestation bundles were made for polis_recognizer-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on grigra27/polis-recognizer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polis_recognizer-0.3.0-py3-none-any.whl -
Subject digest:
b4e0a297a027f887a4e12d0028265801ee09d94c0b935fcd8c1efc61af354599 - Sigstore transparency entry: 1607428133
- Sigstore integration time:
-
Permalink:
grigra27/polis-recognizer@e0d64155a61320a9bfa0d09cad376c6cfcc0d24f -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/grigra27
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e0d64155a61320a9bfa0d09cad376c6cfcc0d24f -
Trigger Event:
push
-
Statement type: