Configuration reference¶
Every knob the cascade has, with the measurement that fixed its default.
This page is generated from the pydantic model during the site build,
by docs/hooks/config_reference.py. Each description below is the field's
own description= string, copied verbatim — there is no second place where
a threshold is explained, so there is no second place for one to go stale.
Config is frozen and rejects unknown keys (extra='forbid'): a typo
in a field name is a ValidationError at construction rather than a setting
that silently does nothing. To vary one field, use
config.model_copy(update={...}).
No field points at a network
There is no host, port, URL or credential on this model, and
tests/unit/test_config.py::test_no_field_points_at_a_network is what keeps it
that way. All networking lives in the constructor of a step somebody wrote
by hand — see ADR 0001.
from autosxtract import Cascade, Config
cascade = Cascade(Config(dpi=150, min_agreement=0.60, engines=["paddle"]))
Step order¶
Which steps the cascade assembles, and in what order.
engines¶
list[str] | None · default None
Explicit order of OCR engines. None lets the platform decide: Vision first on Apple hardware, PP-OCRv6 elsewhere.
use_native¶
bool · default True
Read the PDF's text layer before any OCR. Measured: 31% of a real archive's documents are resolved here, at 13.4 ms — free against the ~400 ms of any OCR.
Rasterising¶
How a page becomes pixels. Every OCR step downstream is handed the result of these three, and is handed the same result — two engines compared on one document must see identical images, or the difference between them is preprocessing noise.
dpi¶
int · default 150 · ≥ 36, ≤ 600
Render resolution for OCR. 150 is the minimum that preserves numeric anchors: at 100 DPI preservation falls to 85.5%, losing dates and tax numbers in 35 of 60 documents.
grayscale¶
bool · default True
Rasterise in a single channel. The resolution does not change — only chrominance is discarded, and a scanned document carries almost none. It saves ~84 ms per document in the encoder.
max_pages¶
int · default 64 · ≥ 1
Ceiling on rasterised pages per document.
The acceptance gate¶
The single criterion, applied by quality/gate.py. The step that thinks it solved the document and the cascade that decides whether to pay for the next one read these same numbers.
min_useful_words¶
int · default 12 · ≥ 0
Floor of alphabetic words outside the stamp. Below this, what is left is not content. Derived from an audit of 1,339 documents: 403 had text that looked fine and was only the digital signature stamp.
min_chars_per_page¶
int · default 200 · ≥ 0
Minimum density. A page of text yields hundreds of characters.
min_confidence¶
float · default 70.0 · ≥ 0.0, ≤ 100.0
Engine confidence floor. It does NOT arbitrate quality — measured on 60 audited documents, OCR confidence does not separate a good reading from an unsafe one. It only guards against degenerate output.
min_score¶
float · default 0.35 · ≥ 0.0, ≤ 1.0
Below this text-quality score the step is refused.
native_accept_score¶
float · default 0.75 · ≥ 0.0, ≤ 1.0
Score from which the native text ends the cascade without paying for OCR. It comes with the coverage caveat: a high score describes the text that came out, not the fraction of the page left behind.
Gates between steps¶
The four that stop the cascade from paying for a step it does not need.
coverage_gate¶
bool · default True
Refuse the native text when there is a large image in a region with no text. In a filing that embeds an official letter as an image, the native text is flawless and the attachment — the actual content — is never read.
consensus_gate¶
bool · default True
Declare the page empty when independent engines agree there is no content. One dissenter is enough to escalate.
agreement_gate¶
bool · default True
Stop when two engines read THE SAME THING — proof that the reading is complete, not that the page is short.
min_agreement¶
float · default 0.6 · ≥ 0.0, ≤ 1.0
Vocabulary Jaccard from which two readings agree. Calibrated on 24 real escalations: 13 cases landed between 0.00 and 0.48 and 11 between 0.65 and 0.80 — the threshold sits in the gap.
per_page_routing¶
bool · default True
In a mixed PDF, OCR only the pages without native text. Measured: of the 419 pages that went through OCR on a real case file, 54 (12.9%) already had text.
fix_orientation¶
bool · default True
Detect and correct a sideways page BEFORE OCR, with Tesseract's OSD (needs the [veto] extra; without it the correction is skipped and the reason goes to the provenance and to diagnose). On by default because a rotated page is read badly by every engine, and everything downstream then judges a bad reading caused by an input defect it cannot see. UNMEASURED on this project's archive: the cost is one OSD pass per rasterised page, so a document resolved by the native text layer pays nothing, and no number here yet says what it buys. Measure with scripts/compare_engines.py before relying on it, and turn it off if your scans are known upright.
Around an expensive step¶
The five vetoes that run before a step marked expensive, and the replacement gate that judges what it produced.
expensive_step_vetoes¶
bool · default True
Run the five vetoes before a step marked expensive. Measured on 19 escalated documents: they save 27.2 minutes of the expensive step and the largest content lost is a 124-character stamp.
replacement_gate¶
bool · default True
Submit an expensive step's text to the anchor and coverage gate before letting it replace what already exists. Length alone gets both of the costliest cases wrong.
veto_engine¶
str | None · default 'tesseract'
The local engine that acts as WITNESS for vetoes 3 to 5. It must be of a different architecture from those already run — a second engine of the same family is not independent evidence. None turns off the three vetoes that depend on it.
veto_max_pages¶
int · default 3 · ≥ 1
Pages the witness reads. Measuring 3 already separates the archive.
min_reliable_words¶
int · default 3 · ≥ 0
Floor of legible words for the witness to declare there IS text. Measured on 489 documents: below 3 the expensive step yielded at most 124 characters; from 7 upwards, at least 257. The band is empty.
rebuild_prose¶
bool · default True
Join the lines the OCR broke at each visual line of the page. Measured: 85% of the lines do not end in punctuation, and without this the text is a list of fragments, not prose.
Line containment layers¶
They need an engine that exposes line geometry (read_page). With any other engine the cascade records engine without geometry and moves on — nothing breaks, there is simply no layer.
layers¶
bool · default True
Contain stamps, signatures and junk line by line. Measured on 895 pages: entity recall 0.902 -> 0.921, median CER 0.132 -> 0.129, and p50 latency FALLS from 298 to 236 ms. 79 pages improve, 4 get worse (all already bad), clean pages are untouched.
layer2¶
bool · default True
Re-read the targets: rotate the vertical line 90 degrees, crop the dirty line tighter. It costs ~30 ms at p50 and is what recovers the protocol stamp instead of merely marking it.
max_layer2_targets¶
int · default 10 · ≥ 0
Ceiling on re-reads per page — it bounds the latency tail.
min_layer2_gain¶
float · default 0.08 · ≥ 0.0
How far the re-reading's score must beat the original's to replace the line. Swapping on a tie is noise.
lexicon¶
Any | None · default None
The Lexicon legibility is judged against. None uses the built-in one — a floor of legal Portuguese. Building your own from validated texts is measurably better: with a small lexicon, correct text falls into suspect more often (the safe side of the error, but it escalates pages for nothing).
signature_detector¶
str | None · default None
Path to a YOLO in ONNX for signature detection (Layer 1.5). None leaves only the structural rule, which always runs. Off by default because a model trained on a public signature dataset did NOT transfer: 19% of pages with a detection, most of them false positives on seals, stamps, logos and QR codes.
page_routing¶
bool · default False
Classify the page type (table / stamped_digital / degraded / normal) and record it in the provenance. It does not change the extraction — it is a signal for whoever consumes it.
Engines and parallelism¶
The three parallelism fields accept None, meaning decide from this machine, and that is the default: the same library runs on a two-core laptop and a seventy-two-core server, and one fixed number serves both badly. An explicit number is obeyed — what it is not, is a promise.
engine_options¶
dict[str, dict[str, Any]] | None · default None
Constructor arguments for a SPECIFIC engine, by name. The registry builds engines with no arguments, so everything their constructors accept — PP-OCR's tier, the INT8 flag, the thread count, the preprocessing, the execution providers — was unreachable from an assembled cascade. A knob that cannot be turned from outside is not a knob. {'paddle': {'quantized': True}} asks for INT8 weights; {'paddle': {'det': 'small', 'rec': 'medium'}} picks a tier. Different options are a different engine and load their own model.
engine_parallelism¶
dict[str, int] | None · default None
Pages in flight for a SPECIFIC engine, by name — the caller's last word over the engine's own statement about itself. An engine declares scales_with_threads = False when it knows a single hardware queue sits behind it (Apple's Neural Engine serves one request at a time), and that declaration is a good DEFAULT because whoever configures the cascade cannot see behind the engine. It is a bad law: it was measured on one machine, and registered engines are built with no arguments, so without this there is no way at all to tune them. {'vision': 4} overrides one; the others keep deciding for themselves.
page_parallelism¶
int | None · default None · ≥ 1
Threads per document in OCR. None resolves to min(4, cores). PyMuPDF is OUTSIDE this count: it is serialised by a process lock, because it crashes the process under concurrency (segfault in page_get_textpage). An engine with a single hardware queue — Apple's — ignores this number and uses 1: measured from 1 to 12 threads, constant throughput and latency from 430 ms to 3,492 ms.
document_parallelism¶
int | None · default None · ≥ 1
Documents in flight during batch processing. None resolves to min(4, cores).
concurrency_cap¶
int | None · default None · ≥ 0
Maximum pages in flight across ALL documents of a batch. None resolves to cores x 2; 0 turns the cap off. It exists because the product documents x pages grows without anyone noticing — 4 x 8 is 32 simultaneous pages, each holding a rendered image and the model's activations. On a small machine the memory limit arrives before the CPU limit.
Domain¶
The two fields that point at data rather than at a number. This is the adaptation seam: see a new pattern pack.
language¶
str · default 'pt-BR'
Preferred OCR engine language, and the pack of the pattern catalogue that goes with it. A tag with no bundled pack keeps the default one — which is what the library did before the catalogue existed, when the patterns were Portuguese whatever this said.
patterns¶
Any | None · default None
The pattern catalogue: a PatternSet, or a path to a TOML file or a directory of them. It overrides the bundled packs ENTRY BY ENTRY, so a pack that redefines one stamp inherits the other sixty patterns and keeps receiving their fixes. None resolves AUTOSXTRACT_PATTERNS, then the pack for language, then the neutral base.
stamps¶
tuple[str, ...] | None · default None
Stamp patterns (regexes) to strip before measuring the extraction. None takes stamp.conformity from the catalogue — the Brazilian court ones, unless a pack replaced them. It stays a field of its own because it is the override reached for most often: the stamp survives when the body of the page produces nothing, so it has to go before the measurement, and saying so should not require writing a pack.
Methods, not fields¶
Five things on Config resolve when they are called rather than when the
object is built, and the reason is the same for all of them: the machine
that resolves may not be the one that serialised the configuration. A
preset stored in YAML and used in two environments has to answer differently
in each, and a path resolved in the constructor points at a file that is not
there.
Config.pattern_set()¶
The resolved catalogue for this configuration.
Config.stamp_patterns()¶
The stamp list in force — stamps if given, the catalogue's if not.
Config.pages_in_flight()¶
Threads per document, already resolved for this machine.
Config.documents_in_flight()¶
Simultaneous documents in a batch, already resolved for this machine.
Config.batch_concurrency()¶
(documents, pages) already limited by the aggregate cap.