Skip to content

Backends

All model execution — the CRAFT detector and every gen2 recognizer — goes through one trait: inference::ModelBackend. The detection, recognition, and engine code call this trait only; they never call ort, tract, or candle APIs directly. config, types, and the geometry code stay entirely backend-agnostic, so a config or result type is never conditionally compiled per backend.

This keeps the backend choice non-load-bearing: a new backend implements the trait and slots in without touching the pipeline. See ADR 0004.

BackendFeatureTargetNotes
ortort, ort-bundled, ort-dynamicDesktop / serverNative ONNX Runtime. ort-bundled fetches a prebuilt runtime at build time (zero-config); ort-dynamic dlopens libonnxruntime at runtime via ORT_DYLIB_PATH.
tracttractWASM / AndroidPure-Rust ONNX interpreter — no native library to ship or load.
candlecandleReservedA pure-Rust native-tensor backend, deferred as a fallback if tract’s recurrent-layer (BiLSTM) support proves insufficient for the recognizer. Not yet implemented.

Pick ort (native) for CPU/RSS-optimized execution on desktop or server; pick tract when you need a pure-Rust build with no native dependency, notably WASM and Android. See ADR 0012 for the ort-bundled/ort-dynamic provisioning tradeoff, and ADR 0009 for why candle is off the critical path.

The active backend is selected through ModelConfig::backend (Backend::Ort / Backend::Tract / Backend::Candle), or --backend on the CLI.

Parallelism flows through a single ConcurrencyConfig::max_threads budget. Rayon’s thread pool and the backend’s own intra-op threads (once wired) draw from the same budget, so nested parallelism — Rayon fanning out across images while the backend also parallelizes inside a single model call — cannot oversubscribe the CPU. max_threads defaults to num_cpus, capped at 8. See Configuration.