Skip to main content

Netra Runtime

The shared inference runtime for Netra Lab projects (Netra-OCR, Netra-Lens, ...). Research code stays in PyTorch. Each model is exported once into a bundle, and every app loads bundles through one small API that runs on CPU or GPU, with no torch at runtime:

import netra_runtime

recognizer = netra_runtime.load("hf://Netra-Lab/Netra-OCR/recognition", device="auto")
result = recognizer("line.png")
print(result.text, result.confidence)

Install

Install exactly one ONNX Runtime flavour. The CPU and GPU builds conflict and can't be installed side by side.

pip install "netra-runtime[cpu]"        # CPU
pip install "netra-runtime[gpu]"        # NVIDIA GPU + CPU (CUDA 13, cuDNN 9)
pip install "netra-runtime[openvino]"   # Intel CPU / iGPU

On a GPU machine, the CUDA libraries can come from the nvidia-* pip wheels (the ones torch installs). ONNX Runtime also needs nvidia-curand, which torch doesn't install.

If a GPU provider can't load, the runtime logs a warning and runs on CPU. It never silently claims to be on the GPU.

Devices

device= Engine
auto (default, or $NETRA_DEVICE) cuda if usable, else cpu
cpu ONNX Runtime CPU
cuda, cuda:1 CUDA
tensorrt TensorRT, falling back to CUDA, then CPU, per op. The engine is cached in <bundle>/.trt_cache/ after the first (slow) build.

On GPUs, detectors pad every image to the full imgsz square. ONNX Runtime's CUDA convolutions redo their setup every time the input shape changes, which made varying page shapes about 6× slower. CPUs keep Ultralytics' tighter rectangle. Square padding can change borderline detections slightly: 5 of 272 line boxes on the test pages. | openvino | Intel OpenVINO |

The same bundles run in the browser through web/ (@netra-ai-lab/runtime-web) on WebGPU or WebAssembly. Its outputs are checked against this package: identical recognized text, and identical boxes with the same padding. Graphs only use basic opset-18 ops so they stay portable; see spec/bundle.md.

How it fits together

research repo (torch)  --export script-->  bundle/  --netra_runtime.load()-->  Task
                                           netra-model.json
                                           *.onnx, vocab.json
  • Bundle: graphs plus a JSON manifest naming the components that surround them: preprocessing, the decoding loop, the tokenizer, post-processing. Format: spec/bundle.md.
  • Task: what an app calls, e.g. TextRecognizer. Each task has a fixed output type, so apps don't depend on model internals.
  • Components: small classes registered by name (@register("decoder", "kv-cache")).
  • Backend: Session maps a device to ONNX Runtime execution providers. Tensors like a decoder's KV cache stay on the GPU between steps.

Adding a model

  1. Write an exporter in the model's research repo. It builds the graphs and calls netra_runtime.bundle.write_manifest(). Example: Netra-OCR's netra_ocr/recognition/scripts/export_runtime_bundle.py.
  2. Check parity: the runtime's output vs the PyTorch model on a test set, per device.
  3. If the model only needs existing components, you're done. If it needs something new, add one component class and register it. A new kind of output (e.g. detection boxes) needs one new task class.

Threads

ONNX Runtime's idle worker threads busy-wait ("spin") by default. When several sessions share a process, such as a detector, a layout model and a recognizer, the spinning threads of one session steal CPU from whichever session is running. In Netra-OCR's CPU pipeline this made the detectors 2–3× slower than in isolation. Sessions therefore start with spinning off. Set NETRA_ORT_SPIN=1 to turn it back on for a process that runs a single model; batched recognition alone is about 15% faster with it.

Precision variants

netra-runtime make-variant BUNDLE fp16 [--prefer-on cuda]   # half precision, float32 inputs/outputs kept
netra-runtime make-variant BUNDLE int8 [--prefer-on cpu]    # dynamic int8 on MatMul/Gemm
netra-runtime make-variant BUNDLE int8 --remove

A variant can always be selected with load(..., variant="fp16"). It only becomes a device's default after --prefer-on, and you should only do that after measuring accuracy and speed. Results on Netra-OCR's models (RTX 3060 / i7-13700KF):

Bundle Variant Result Decision
netra-ocr-layout fp16, GPU 65 → 39 ms/page, 115/115 boxes matched default on cuda
netra-ocr-lines fp16, GPU 8.6 → 6.4 ms/page, 269/269 boxes matched default on cuda
netra-ocr-recognition fp16, GPU identical text, but 13.1 → 16.5 ms/line not used
netra-ocr-recognition int8, CPU CER 1.17% → 1.26%, no speed gain not used

Small, latency-bound models (the recognizer's LSTM and per-token steps) don't gain from lower precision. Convolution-heavy detectors on a GPU do.

Bundles available so far

A bundle is a folder, or hf://<org>/<repo>[/<folder>][@revision] for one on Hugging Face (downloaded once into the Hugging Face cache; load fetches only the graphs for the device, e.g. not the fp16 copies on a CPU). Netra OCR's bundles are published together in Netra-Lab/Netra-OCR, one folder each:

Bundle Task From
netra-ocr-recognition (recognition/) text-recognition Netra-OCR export_runtime_bundle.py
netra-ocr-lines (lines/) object-detection (yolo-nms) netra-runtime export-yolo best.pt ...
netra-ocr-recognition-blockwise (recognition-blockwise/) text-recognition (blockwise-kv) the same script, on the blockwise checkpoint
netra-ocr-layout (layout/) object-detection (yolo-end2end) netra-runtime export-yolo ... --loader doclayout_yolo:YOLOv10

Blockwise decoding gives output identical to greedy (325/325 lines). It isn't faster here, though: with a KV cache each greedy step is already cheap, the per-line encoder dominates, and blockwise can't batch across lines. It averages 2.32 tokens per forward pass. It's worth trying where each decoder call is expensive: bigger decoders, or slow per-call runtimes.

Ultralytics-based detectors carry the AGPL-3.0 license into their bundles (recorded in metadata.license).

CLI

netra-runtime devices                 # usable devices + registered components
netra-runtime info BUNDLE             # manifest summary, file hash check
netra-runtime run BUNDLE IMG... --device cuda
netra-runtime export-yolo WEIGHTS OUT --name NAME [--imgsz 640] [--loader module:Class]

Development

pip install -e ".[cpu,dev]"
pytest                                       # unit tests
NETRA_TEST_BUNDLE=path/to/bundle pytest      # + a real-bundle smoke test

Metadata

Release files for netra-runtime 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for netra-runtime 0.1.1
File Size Uploaded Provenance Actions
netra_runtime-0.1.1.tar.gz 32.1 kB Without provenance Details for netra_runtime-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for netra-runtime 0.1.1
File Interpreter ABI Platform Actions
netra_runtime-0.1.1-py3-none-any.whl Python 3 none any Details for netra_runtime-0.1.1-py3-none-any.whl

Total release size: 62.0 kB

Release files / netra_runtime-0.1.1.tar.gz

Download URL netra_runtime-0.1.1.tar.gz
Size 32.1 kB
Tags Source
SHA-256 checksum
How to use checksums
bb18bdfe32054958da6a9cc55e88c2f8860ca94ce671c676cbb9fb9fc304aa16
BLAKE2b-256 checksum
How to use checksums
dbc4ed67977ae0a191859da29cb0f11c24c1cfe9b9a8324822e10c7d8a944910
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / netra_runtime-0.1.1-py3-none-any.whl

Download URL netra_runtime-0.1.1-py3-none-any.whl
Size 29.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
93f7c50be9ee424604143cb6adbac1002bcdd40189d57c4e20f1404a5b4a96e6
BLAKE2b-256 checksum
How to use checksums
0167fa40c2ef1279fbb033e1739da451cbbd2a0704818b2fe01f979cb4461f50
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for netra-runtime 0.1.1

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page