Skip to main content

tabellio

PyPI Python CI License

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…

Brassens - INA archive

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 a confidence.
  • Verbatim transcription too. The full diplomatic text of the act, [?] for an illegible word — as act.transcription in full mode, or on its own with output_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 / role vocabulary is fixed English. The detected language is recorded on act.language.

Install

pip install "tabellio[gemini]"     # or [openai] / [nim] / [anthropic] / [ollama] / [all]

Requires Python 3.11+.

Example

A parish burial act from 1757, a public-domain register page, examples/sample_act.jpg:

1757 burial act

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

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) — rich Act: raw spelling, per-field confidence, date qualifier, transcription, validation warnings. act.date is the date of the act; the subject's real birth / death (often a different day — "né la veille", "décédé hier") lands on persons[i].birth_date / death_date with qualifier: "calculated" + a note when deduced. validate warns if a baptism or burial has no such date on its subject.
  • output_mode="simple" — a bare ActSummary: type, date (ISO string or null), location, persons (role / given / surname). Nothing else — no confidence, no raw, no qualifiers, no language, no life-event dates, no transcription. Shortest prompt, fewest tokens.
  • output_mode="transcription" — a Transcription: {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 Cost
gemini tabellio[gemini] gemini-3.6-flash free tier, no card — best on acts
mistral tabellio[mistral] mistral-small-latest paid (mistral-medium-latest for harder hands)
openai tabellio[openai] gpt-4o paid
anthropic tabellio[anthropic] claude-sonnet-4 paid
nim tabellio[nim] (pulls Pillow) meta/llama-3.2-11b-vision-instruct free, but slow / weak here
ollama tabellio[ollama] llama3.2-vision local, no key

For a no-cost run, gemini (its AI Studio free tier needs no credit card). openai / anthropic are OpenAI/Anthropic-compatible, so third-party gateways (Groq, OpenRouter, GitHub Models — some with free tiers) work via provider="openai", base_url=... with no extra code.

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 ollama provider 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

Apache-2.0. See LICENSE and NOTICE.

Metadata

Release files for tabellio 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tabellio 0.1.2
File Size Uploaded
tabellio-0.1.2.tar.gz 582.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tabellio 0.1.2
File Interpreter ABI Platform
tabellio-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 619.7 kB

Release files / tabellio-0.1.2.tar.gz

Download URL tabellio-0.1.2.tar.gz
Size 582.4 kB
Tags Source
SHA-256 checksum
How to use checksums
83315c2aaf1b6c091557578db7b9b9828a18d1a94c3baed6877318f3b978641f
BLAKE2b-256 checksum
How to use checksums
4204aaefbaef5f0969e35631afa9390552b3401539ff4fb6378e89608af226a8
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

Release files / tabellio-0.1.2-py3-none-any.whl

Download URL tabellio-0.1.2-py3-none-any.whl
Size 37.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
68153fb734e41ee2b36ed8f555886efd00ee4fff6123c1e15f8d39e20950d37b
BLAKE2b-256 checksum
How to use checksums
7e19ee4995ad6cc3a625e6c87ea1acd3f6e5f123c534fe1db854f6a64c2a50d9
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

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page