Skip to content

Reliability

docgate makes provider failure visible while allowing an ordered fallback ladder. The receipt records each degradation, so a successful fallback does not hide a failed primary.

Circuit breakers

CircuitBreakers protects a provider from repeated calls after it fails enough times. Configure the process-wide defaults with configure():

docgate.configure(
    providers=[primary_ocr, backup_vision],
    breaker_failures=3,
    breaker_cooldown_s=120,
)

After the configured number of failures, new calls to that provider are skipped until its cooldown passes. Then exactly one recovery probe is allowed. A successful probe closes the breaker; a failed probe starts a fresh cooldown. When a breaker prevents a call, the receipt has a Degradation with reason="breaker_open", and docgate moves to the next provider if one exists.

For call-specific control, construct CircuitBreakers and pass it as breakers to execute().

Timeouts, retries, and backoff

ExecutionPolicy applies to one provider/page call. It sets a logical timeout, the maximum number of attempts, and bounded exponential backoff.

import docgate

policy = docgate.ExecutionPolicy(
    timeout_s=30,
    max_attempts=3,
    initial_backoff_s=0.5,
    backoff_multiplier=2,
    max_backoff_s=4,
)
result = docgate.read(
    "scan.pdf",
    providers=[primary_ocr, backup_vision],
    execution_policy=policy,
)

The delay after failed attempt n is min(initial_backoff_s * backoff_multiplier ** (n - 1), max_backoff_s). Failures and timeouts create Degradation entries with an attempt number. Retries point fell_back_to at the same provider; after the final failed attempt, the field identifies the next provider when there is one.

Important: a timeout ends docgate's wait for the callback, but Python cannot safely force-kill a running native or local thread. The underlying task may continue in the background. Built-in remote adapters also have transport-level timeouts. Use provider/client controls appropriate to your environment; hard termination through subprocess isolation is not currently provided by docgate.

Progress events

Pass on_progress to read() or execute() for operational metadata. A ProgressEvent includes a page number, event name, optional provider, optional attempt, and optional detail—never extracted text, images, filenames, or keys.

def log_progress(event: docgate.ProgressEvent) -> None:
    print(event.page, event.event, event.provider, event.attempt, event.detail)

result = docgate.read("scan.pdf", providers=[primary_ocr], on_progress=log_progress)

Current event names are page_skipped, page_unknown, page_cached, provider_skipped, provider_started, provider_failed, provider_rejected, and page_completed. Exceptions raised by the callback are ignored so observability cannot change extraction correctness.