Why not just use a VLM
A vision-language model will happily read a scanned page, and it will occasionally invent a line that was never there. For a contract, a judgment or an invoice that is not an acceptable failure mode. Purpose-built OCR gives you a bounding box and a confidence value for every line, which means a human can check the machine's work.
So CoderAI separates the two jobs. An OCR engine produces text with geometry. Only then, optionally, does a text model turn that text into structured fields — and it is working from a transcript, not from pixels.
Three engines
| Engine | Licence | Runs | Strengths |
|---|---|---|---|
doctr | Apache-2.0 | in-process, GPU | The straightforward default. No extra environment. |
paddle | Apache-2.0 | isolated venv subprocess | PaddleOCR plus PP-Structure — layout regions and table recovery. |
surya | GPL, opt-in | isolated venv | Strong multilingual and layout quality. Three serving modes: local, Surya-2 through CoderAI's vLLM backend, or an external llama-server. |
A missing dependency returns 503 with an actionable message rather than crashing the server, and the isolated environments can be built on demand from the admin UI.
Calling it
curl -H "Authorization: Bearer $TOKEN" \
-F file=@sentenza.pdf \
-F engine=paddle \
-F dpi=300 \
-F detect=both \
-F structured=true \
-F schema=italian_sentenza \
https://your-host/v1/ocr
| Field | Meaning |
|---|---|
file | Image or PDF, up to 100 MB. /v1/ocr/batch takes repeated files and reports per-file errors inline. |
engine | paddle, doctr or surya. Defaults to the configured engine. |
dpi | PDF rasterisation resolution. |
structured | Run schema-driven extraction with a text model after OCR. |
schema | Inline JSON, a named schema, or empty for automatic selection. |
detect | off, layout, detector or both — stamp and signature detection. |
The response carries text for the whole document plus per-page lines[] (text, bounding box, confidence), regions[] for layout, tables[], and — when asked — stamps[], signatures[] and structured.
Schemas are data, not code
A schema is a JSON document describing what to pull out of a class of document. Adding support for a new document type is a file, not a deployment.
{
"name": "italian_sentenza",
"description": "Italian civil judgment",
"language": "it",
"type": "template",
"prompt_hint": "Extract the parties, the court and the outcome.",
"schema": { "tribunale": "", "rg": "", "giudice": "", "parti": [], "dispositivo": "" }
}
Two kinds: a template (a shape to fill in) or a full jsonschema, which is validated when validation is enabled — a validation failure is surfaced alongside the result rather than discarding it.
Resolution order is inline JSON, then a named lookup in <config>/ocr_schemas/, then the built-ins. User files shadow built-ins, so you can override a shipped schema without editing the package. Built-ins cover italian_sentenza, generic_document and an invoice JSON Schema. The whole store is editable from the admin UI.
Stamps and signatures
Legal and administrative documents are often only meaningful if they are stamped and signed, and that fact is invisible in plain text. CoderAI can flag both:
layout— matches the OCR engine's own layout regions against English and Italian markers (timbro, sigillo, firma). Cheap, no extra model.detector— a small YOLO detector, bucketed into stamps and signatures.both— run both and merge.
What this is not
It locates stamps and signatures. It does not verify them, match them to a signatory, or make any statement about authenticity. Treat a hit as "a human should look here".
Configuration and pipelines
OCR is configured in config.json under "ocr" — not in models.json: the default engine, DPI, concurrency, per-engine enablement, instance counts and GPU use, detection mode and thresholds, the extraction model and schema, and the isolated venv paths.
There is also an ocr pipeline step type, so an OCR-then-LLM chain is a single custom-pipeline call rather than two round trips. The repository's docs/ocr.md carries the full reference.
AISBF