docgate
Spend the minimum on document AI, and know exactly what you didn't check.
Documentation · PyPI · GitHub
Most document pipelines extract every page and return an empty string when a page could not be read. docgate is deliberately different: it extracts the free text layer first, bounds raster output before allocating memory, and always returns a receipt that distinguishes blank, skipped, and unknown pages.
Formats
| Format | Local extraction | Model call needed |
|---|---|---|
| Embedded text per page | Only for pages without usable text | |
| DOCX | Paragraphs and tables | No |
| XLSX | One logical page per worksheet | No |
| PPTX | One logical page per slide | No |
| TXT, Markdown, HTML | Native text | No |
Install Office readers when needed:
pip install "docgate[office]"
For a non-PDF byte stream, pass a file path today; stream adapters are an intentional extension point for future formats.
import docgate
result = docgate.read("bundle.pdf")
print(result.text)
print(result.receipt.complete)
print(result.receipt.unknown)
Install
pip install docgate
What it can do
- Extract usable embedded PDF text for free, before calling any model.
- Output-bounded page rasterisation. A pathological declared page size cannot allocate an unbounded image.
- A
Clampis reported whenever requested output is reduced. - A
Receiptaccompanies every result. Pages that were not read are explicitUnknownrecords, never silently omitted. - Send only pages without a usable text layer to your OCR/vision provider.
- Fall back across providers and record every failed attempt as a
Degradation.
Bring any OCR or vision model
docgate is provider-neutral. It ships lightweight adapters for Claude and any
OpenAI-compatible vision endpoint (including compatible GLM deployments). For
every other model — local Tesseract, a private endpoint, Azure, Gemini, or an
SDK you already use — pass a small CallableProvider or implement Provider.
from decimal import Decimal
import docgate
vision = docgate.OpenAICompatibleProvider(
model="your-vision-model",
api_key="...",
base_url="https://your-openai-compatible-host/v1",
cost_per_page=Decimal("0.004"),
)
result = docgate.read("bundle.pdf", providers=[vision])
Claude uses its native Messages API:
claude = docgate.AnthropicProvider(model="your-claude-vision-model", api_key="...")
result = docgate.read("bundle.pdf", providers=[claude])
For a local or custom engine:
local_ocr = docgate.CallableProvider("my-ocr", lambda png_pages: [run_ocr(p) for p in png_pages])
result = docgate.read("bundle.pdf", providers=[local_ocr])
Control spend and retain private cache hits
Pass a hard budget to stop before an extra paid page. The partial result stays
useful and every page not attempted is listed as BUDGET_EXHAUSTED.
result = docgate.read("bundle.pdf", providers=[vision], budget=Decimal("0.10"))
assert result.receipt.spent <= Decimal("0.10")
Use SQLiteCache for durable local cache hits across runs. Cache entries use a
SHA-256 digest of the rendered PNG bytes plus provider identity — never a path
or filename. Failed and empty responses are never stored.
cache = docgate.SQLiteCache(".docgate-cache.db")
result = docgate.read("bundle.pdf", providers=[vision], cache=cache)
Stay available when providers fail
Provider fallbacks are tried in order. Repeated failures open a circuit breaker,
so a dead key does not consume a full timeout for every page. After its cooldown,
docgate automatically allows one recovery probe; a successful call closes the
breaker. Every skipped or failed provider is recorded in receipt.degraded.
docgate.configure(
providers=[primary_vision, backup_vision],
breaker_failures=3,
breaker_cooldown_s=120,
)
Use ExecutionPolicy when provider calls need bounded waiting and retries:
policy = docgate.ExecutionPolicy(
timeout_s=30,
max_attempts=3,
initial_backoff_s=0.5,
max_backoff_s=4,
)
result = docgate.read("bundle.pdf", providers=[primary, fallback], execution_policy=policy)
Timeouts and failed attempts remain visible in receipt.degraded. For progress
reporting, pass on_progress; events include page/provider/attempt metadata but
never document text, images, filenames, or credentials. See the
reliability guide for the thread-timeout boundary and
fallback behavior.
Escalate OCR to vision only when needed
A provider returning weak text is not the same as a provider outage. Use a
TextQualityGate to reject visibly insufficient OCR output and try the next
provider, usually a vision model. The receipt records this as
quality_gate_rejected, while preserving the OCR provider's health.
ocr = docgate.MistralOcrProvider(api_key="...", cost_per_page=Decimal("0.004"))
vision = docgate.OpenAICompatibleProvider(model="your-vision-model", api_key="...")
result = docgate.read(
"employment-bundle.pdf",
need="CV and experience letter",
selection="top_k",
max_paid_pages=3,
providers=[ocr, vision],
quality_gate=docgate.TextQualityGate(min_characters=40, min_alphanumeric_characters=25),
)
See OCR-to-vision routing for the complete mixed-document workflow and its limits.
Enforce where documents may go
Companies can prevent sensitive pages from crossing a trust boundary while still using the same provider ladder. Policy runs before cache lookup or model invocation; a denial is visible on the receipt and never silently bypassed.
local = docgate.CallableProvider("local-ocr", run_ocr, trust_zone="local")
remote = docgate.OpenAICompatibleProvider(model="your-vision-model", api_key="...")
result = docgate.read(
"confidential.pdf",
providers=[local, remote],
provider_policy=docgate.TrustZonePolicy(("local",)),
)
If local OCR cannot read a page, it becomes Unknown; docgate will not send it
to the external fallback. Custom policies receive cost and provider metadata,
never document text, images, filenames, paths, or credentials. See
provider governance.
Keep estimates auditable
Pricing is supplied by your application because provider pricing changes. Attach
the date and source you verified; every provider call records its estimate in
receipt.quotes. Cache entries include the exact image bytes plus a non-secret
provider/model/prompt identity, so a model or prompt change cannot reuse old
output.
from datetime import date
from decimal import Decimal
pricing = docgate.PricingProfile(
per_page=Decimal("0.004"),
verified_on=date(2026, 8, 30),
source="https://provider.example/pricing",
)
ocr = docgate.MistralOcrProvider(api_key="...", pricing=pricing)
read() is a convenience wrapper around plan() then execute(). Planning is
local-only and does not invoke any provider.
plan = docgate.plan("bundle.pdf", need="degree certificate")
print(plan.estimated_cost) # Decimal('0') in v0.1
print(plan.candidate_pages) # ranked from local text-layer signals
result = docgate.execute(plan)
Plans and receipts also have versioned JSON forms for CI, job metadata, and audit logs. They exclude document text, source paths, images, and secrets by default.
docgate plan bundle.pdf --need "degree certificate" --selection top_k \
--max-paid-pages 1 --json
docgate read notes.docx --json
An incomplete docgate read exits with code 2. Use --include-text only when
the JSON consumer intentionally needs extracted document content.
Select before you spend
Pass selection="candidates" to read only pages with local keyword evidence,
or selection="top_k" with max_paid_pages to cap work at the highest-ranked
pages. Every excluded page is present in receipt.unknown as
SKIPPED_BY_PLAN; partial output is always visible.
result = docgate.read(
"bundle.pdf",
need="degree certificate",
selection="top_k",
max_paid_pages=1,
providers=[vision],
)
What docgate does not do
- It is not an OCR engine or hosted model service.
- It is not RAG, a vector store, or an agent framework.
- It does not interpret, score, verify, or make decisions about documents.
- It has no telemetry or phone-home behaviour.
Status
v0.2 is in development. The current main branch adds selective paid-page execution, OCR-to-vision quality routing, auditable pricing, model-safe cache namespaces, timeouts and retries, metadata-only progress events, stable JSON receipts, and a dependency-free CLI. Hybrid image-region OCR remains next.
License
Apache-2.0.
Releases
Releases are published from GitHub Actions using PyPI Trusted Publishing—no long-lived PyPI token is stored in this project. See the release guide.
Metadata
Release files for docgate 0.2.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 | |
|---|---|---|---|
| docgate-0.2.1.tar.gz | 772.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| docgate-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 797.6 kB
Release files / docgate-0.2.1.tar.gz
| Download URL | docgate-0.2.1.tar.gz |
|---|---|
| Size | 772.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d247524de38c3654ff19f1309cd3fe103a5dda7881f0938a77496f31454bccda
|
|
BLAKE2b-256 checksum How to use checksums |
a51c7337062755e294aa69cc4d8790f88b3411c5f50f549760edc3133a46bdb1
|
| 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 / docgate-0.2.1-py3-none-any.whl
| Download URL | docgate-0.2.1-py3-none-any.whl |
|---|---|
| Size | 25.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b42c83321fedffb6068fef59c81f781d7a9c4d92f3a2d117629f3002eeb273d8
|
|
BLAKE2b-256 checksum How to use checksums |
325b4f98e98e9808270ad068e51fbe918d02e212d8375a8096f5654554465734
|
| 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