tabellio
Turn an image of a civil-registry or parish record into validated structured JSON.
tabellio: the Roman / medieval scribe who officially drafted legal acts.
The 'vieux tabellion' Georges Brassens addresses in Supplique pour être enterré sur la plage de Sète (1966):
Trempe dans l'encre bleue du Golfe du Lion,
Trempe, trempe ta plume, ô mon vieux tabellion,
Et de ta plus belle écriture,
Note ce qu'il faudrait qu'il advînt de mon corps…
This library reads back what that pen wrote.
- BYOK — you pass your own VLM API key; it is never stored, never logged.
- Provider-agnostic — Gemini, OpenAI, NVIDIA NIM, Anthropic, or a local Ollama model. None required at install.
- No embedded model, no training, no storage. Reliability comes from a strict Pydantic schema and post-extraction consistency rules, not from trusting the model.
- No silent resolution. Original spelling is kept, every date carries a
qualifier(exact / about / calculated / …), every field aconfidence. - Verbatim transcription too. The full diplomatic text of the act,
[?]for an illegible word — asact.transcriptionin full mode, or on its own withoutput_mode="transcription". - GEDCOM 7 out of the box.
tabellio.to_gedcom(act)— schema built to map cleanly to GEDCOM. - Any language or script. Text is transcribed in the language of the act
(Latin, French, German, …) and never translated; only the schema's
type/rolevocabulary is fixed English. The detected language is recorded onact.language.
Example
A parish burial act from 1757, a public-domain register page,
examples/sample_act.jpg:
export TABELLIO_PROVIDER=...
export TABELLIO_KEY=...
for mode in full simple transcription; do
python -m tabellio examples/sample_act.jpg --output "$mode"
done
| Mode | Output | What you get |
|---|---|---|
full |
sample_act.full.json |
every field, raw spelling, confidence, date qualifier, transcription, warnings |
simple |
sample_act.simple.json |
type / date / location / persons only |
transcription |
sample_act.transcription.json |
the verbatim text + detected language |
(The .json files land once the reference run against gemini is done.)
Install
pip install "tabellio[gemini]" # or [openai] / [nim] / [anthropic] / [ollama] / [all]
Requires Python 3.11+.
Use
import tabellio
act = tabellio.parse(
"register-page.jpg",
provider="gemini",
api_key="...", # your key, BYOK
act_type_hint="baptism", # optional
)
print(act.type) # ActType.BAPTISM
print(act.date.raw) # "le douziesme jour de may mil sept cens trois"
print(act.date.iso) # "1703-05-12"
for p in act.persons:
print(p.role, p.given.value if p.given else None, p.surname.raw if p.surname else None)
print(act.warnings) # consistency + low-confidence flags
parse() returns an Act Pydantic model. Unread
fields are null with a note — never guessed.
Output modes
for mode in ("full", "simple", "transcription"):
print(tabellio.parse("register-page.jpg", output_mode=mode))
output_mode="full"(default) — richAct: raw spelling, per-fieldconfidence, datequalifier,transcription, validationwarnings.act.dateis the date of the act; the subject's real birth / death (often a different day — "né la veille", "décédé hier") lands onpersons[i].birth_date/death_datewithqualifier: "calculated"+ a note when deduced.validatewarns if a baptism or burial has no such date on its subject.output_mode="simple"— a bareActSummary:type,date(ISO string ornull),location,persons(role / given / surname). Nothing else — no confidence, no raw, no qualifiers, nolanguage, no life-event dates, no transcription. Shortest prompt, fewest tokens.output_mode="transcription"— aTranscription:{text, language}— the verbatim text of the act ([?]/[illegible]for gaps) and its detected language, nothing else. Still JSON, just two fields.
Hints
tabellio.parse(
img,
act_type_hint="burial", # --hint
act_language_hint="fr", # --lang
context="The deceased is Joseph BOMMAL, buried 21 December 1757.", # --context
)
All three are optional. act_type_hint / act_language_hint are guesses the
model verifies against the image. context is free text of whatever you already
know — names, a date, a place. It is used only to disambiguate hard-to-read
passages: it never overrides a clear reading, raw stays faithful to the page,
and in full mode the model adds a note wherever it followed the image
against your context.
GEDCOM
act = tabellio.parse("register-page.jpg") # the one network call
print(act.model_dump_json(indent=2)) # JSON
print(tabellio.to_gedcom(act)) # a complete GEDCOM 7.0 document
to_gedcom(act) writes one SOUR record for the act, one INDI per person it
names, and a FAM for the couple or parent-child link the act states —
BIRT / BAPM / MARR / DEAT / BURI events with DATE / PLAC / AGE,
ASSO + ROLE for witnesses and godparents, QUAY from confidence. It
transcribes one act; it does not merge a tree — your genealogy software does
that on import. Output is conformance-checked against the gedcom7 library in
the test suite.
CLI: python -m tabellio examples/sample_act.jpg --format gedcom.
The schema is GEDCOM-mappable by design: GenDate.qualifier
(about/before/after/calculated → ABT/BEF/AFT/CAL),
GenDate.calendar (julian, french_republican), Person.name_particle
(SPFX), Person.name_suffix (NSFX).
Configuration
Three optional environment variables, each a fallback for the matching
parse() argument. An explicit argument always wins. tabellio reads them from
os.environ — it does not load a .env file; your application manages its own
environment.
| Variable | Fills | Default |
|---|---|---|
TABELLIO_PROVIDER |
provider= |
gemini |
TABELLIO_KEY |
api_key= |
— (required except ollama) |
TABELLIO_MODEL |
model= |
the provider's default |
The key is never logged and never stored.
Command line
export TABELLIO_PROVIDER=gemini
export TABELLIO_KEY=... # in your shell
python -m tabellio examples/sample_act.jpg [--hint baptism] [--lang fr] [--context "..."] \
[--output simple|transcription] [--format gedcom] [-v]
Prints the result as JSON — Act, ActSummary or Transcription per
--output — on stdout, warnings on stderr; --format gedcom prints a GEDCOM 7
document instead. The key is only read from the environment, never a CLI
argument.
Providers
provider= |
Extra | Default model |
|---|---|---|
gemini |
tabellio[gemini] |
gemini-3.6-flash |
openai |
tabellio[openai] |
gpt-4o |
nim |
tabellio[nim] (pulls Pillow) |
meta/llama-3.2-11b-vision-instruct |
anthropic |
tabellio[anthropic] |
claude-sonnet-4 |
ollama |
tabellio[ollama] |
llama3.2-vision (local, no key) |
Set TABELLIO_MODEL (or pass model=) to switch model — quality vs speed is
your call. Keyword options pass straight through: timeout= (seconds, default
120), base_url=, host=, max_tokens=.
NIM caveat. On the hosted free endpoint (integrate.api.nvidia.com) the 90B
vision model routinely times out; the default is therefore
meta/llama-3.2-11b-vision-instruct, which answers in seconds but is a small
model — it hallucinates names and dates on old cursive and returns poor results
here. Use --output simple with it, and treat gemini as the real path.
The endpoint also rejects inline images larger than ~180 KB: tabellio re-encodes
an oversized image in memory (lower JPEG quality, then downscale, long edge
kept ≥ 1200 px) before sending — the source file is never touched — and logs a
warning; pass shrink=False to get an error instead. NVCF asset upload is not
implemented.
Local / general-purpose VLMs hallucinate plausible names and dates on old cursive. The
ollamaprovider exists for convenience and testing, not for production genealogy.
Scope
In: extraction of a single record to schema, ambiguity flags, confidence surfacing, provider-agnostic adapter, JSON and GEDCOM 7 output.
Out: embedded/trained model, home-grown HTR, storage, accounts, billing, GEDCOM import, building or merging a family tree, image segmentation / deskew, translation of any transcribed text.
Development
make install # uv sync --all-extras
make lint # ruff check + format --check
make test # pytest
To run python -m tabellio against a real provider while developing, export
TABELLIO_KEY (and optionally TABELLIO_PROVIDER / TABELLIO_MODEL) in your
shell, or use your own direnv / dotenv — the repo ships none.
The examples/ directory holds a sample act and its extracted output —
fictional or public-domain only, see examples/README.md.
License
Metadata
Release files for tabellio 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tabellio-0.1.1.tar.gz | 579.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tabellio-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 615.2 kB
Release files / tabellio-0.1.1.tar.gz
| Download URL | tabellio-0.1.1.tar.gz |
|---|---|
| Size | 579.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f815d1e81ef577ff79d6d5f59c783548c80b19642fb020e5613e4862bbf98346
|
|
BLAKE2b-256 checksum How to use checksums |
e17c98b6c1cb8dadf104ec4a45e7cf7c820bea71e670ebb4f5fcd1856b96fd86
|
| 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 30, 2026.
Transparency logRelease files / tabellio-0.1.1-py3-none-any.whl
| Download URL | tabellio-0.1.1-py3-none-any.whl |
|---|---|
| Size | 35.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8e52cda0128ad1c0139756d00de3226b70f6b5f43a6b9a3285bf9582bb1fc9f5
|
|
BLAKE2b-256 checksum How to use checksums |
b0c6d7b2471f4502670b06b40574ee37043d7ddc0c73523dc5aacebcc701880a
|
| 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 30, 2026.
Transparency log