Skip to main content

unlimited-ocr-max

baidu/Unlimited-OCR served through MAX as an OpenAI-compatible endpoint on Metal, CUDA or ROCm. Code: github.com/kthr/unlimited-ocr-max · Weights: kthierbach/unlimited-ocr-max.

  • bf16 — baidu's model.safetensors, unchanged.
  • int8 — model-int8.safetensors, this port's weight-only quantisation of the routed experts (symmetric, per-group G=128).
  • config.json is baidu's minus auto_map and model_type, so MAX loads it without remote code.

Prerequisites

Python 3.12 or 3.13, plus one of:

# requires full Xcode (App Store); the Command Line Tools alone are not enough
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
# MAX compiles Metal kernels with `xcrun metallib`, shipped in the Metal Toolchain
xcodebuild -downloadComponent MetalToolchain
xcrun -f metallib          # must print a path

NVIDIA — Linux (glibc 2.34+, e.g. Ubuntu 22.04+), Ampere or newer, driver 580+

Follow NVIDIA's package-manager installation; for example, on Ubuntu 24.04 x86_64:

# MAX loads these at runtime but does not ship them; without them the
# first request fails with: symbol not found: cublasCreate_v2
# 1. add NVIDIA's CUDA repository (ubuntu2404/x86_64 shown; use your distro/arch)
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt-get update
# 2. libcublas + libcublasLt (one package), CUDA 13
sudo apt-get install -y libcublas-13-0
# 3. check: MAX loads these exact files
ls -l /usr/local/cuda-13.0/lib64/libcublas.so.13 /usr/local/cuda-13.0/lib64/libcublasLt.so.13

AMD — Linux, driver 6.3.3+ (MI355X: ROCm 7+)

MAX also loads ROCm's rocBLAS, hipBLASLt and MIOpen from /opt/rocm/lib; untested beyond compilation.

Serve

uv tool install --extra-index-url https://whl.modular.com/nightly/simple/ unlimited-ocr-max
# or: pip install --pre --extra-index-url https://whl.modular.com/nightly/simple/ unlimited-ocr-max
unlimited-ocr-max serve --devices gpu

This installs max[all]==26.7.0.dev2026100105 from Modular's nightly index. The extra index is required: 26.7 has no stable release yet. Endpoint: http://127.0.0.1:8010/v1/chat/completions, model unlimited-ocr-max.

flag values default what it does
--devices gpu required Metal, CUDA or ROCm; cpu is refused
--weights bf16 | int8 bf16 int8: quantised routed experts, faster decode
--model Hub repo or local dir kthierbach/unlimited-ocr-max a local dir needs this repository's layout
--revision tag v0.4.0 the model-repo tag this package version was validated against
--port integer 8010
--ngram-size integer 35 no-repeat n-gram guard; 0 disables it
--max-batch-size 1–8 1 concurrent requests decoded together each step; a lone request runs slower; output is independent of what else is in flight

A prompt may be at most 512 tokens, the page's image tokens included: 274 in base mode, which leaves about 238 tokens of text. A longer prompt gets an HTTP 400 and never reaches the model. The server allocates its KV page pool at startup and holds it: 90 MiB at --max-batch-size 1, 615 MiB at 8.

First run

The first run compiles every graph for this build, which can take several minutes. On Apple silicon the int8 first compile briefly raises the server's physical footprint far above its steady level -- a transient during compilation; steady state is much lower. Apple M4, with the graphs not yet cached (one draw each):

  • the first bf16 request took 100 s (the server was ready after 29 s);
  • the first int8 request took 331 s, almost all of it compiling the int8 language graphs. During that compile macOS reported a footprint of about 42 GiB, which counts memory it compresses; the resident memory peaked at about 16.6 GiB and swap grew by less than 1 GiB on the 24 GB M4. Afterwards the footprint is 9–13 GiB;
  • with --max-batch-size 8, startup compiles the batched graphs: 212 s (bf16) and 181 s (int8) uncached, against 20–31 s cached.

Later runs read the compiled graphs from the cache.

Measure it on your machine

unlimited-ocr-max profile --devices gpu            # add --weights int8
unlimited-ocr-max profile --devices gpu --max-batch-size 8   # concurrency defaults to 8

Starts its own server, sends the 12 bundled pages, prints one row of the table below and writes profile.json. It refuses to run unless no other process is using the GPU. --out DIR sets the output directory (default ./unlimited-ocr-max-profile-<UTC time>). --concurrency N (profile-only) keeps N requests in flight over the corpus instead of one; it defaults to --max-batch-size and must not exceed it.

One page to Markdown:

curl -s http://127.0.0.1:8010/v1/chat/completions -H 'Content-Type: application/json' -d @- <<EOF
{"model": "unlimited-ocr-max", "temperature": 0, "max_tokens": 1766,
 "messages": [{"role": "user", "content": [
   {"type": "text", "text": "<|grounding|>Convert the document to markdown."},
   {"type": "image_url", "image_url": {"url": "data:image/png;base64,$(base64 < page.png | tr -d '\n')"}}]}]}
EOF

Offline:

uvx --from huggingface_hub hf download kthierbach/unlimited-ocr-max --revision v0.4.0 --local-dir ocr-model
unlimited-ocr-max serve --devices gpu --model ocr-model

Where it has run

temperature 0, default guard. Text is compared against the fp32 PyTorch reference (transformers 4.46.3, CPU); CER = edited characters / reference characters over all pages.

hardware weights status decode prefill memory, peak / steady text vs reference
Apple M4 24 GB bf16 12 pages 66.8 tok/s 2.12 s 16.6 / 10.8 GiB 12/12 byte-identical
Apple M4 24 GB int8 12 pages 69.3 tok/s 3.05 s 13.0 / 9.9 GiB 6/12; CER 0.0011, all edits bbox digits
NVIDIA A100 80 GB bf16 12 pages 141.8 tok/s 1.94 s device 10.1 / 10.1 GiB 10/12; CER 0.022
NVIDIA A100 80 GB int8 12 pages 123.3 tok/s 1.68 s device 10.4 / 10.4 GiB 6/12; CER 0.025
AMD gfx90a / gfx942 / gfx950 / gfx1100 both compiles, never served — — — —

Continuous batching (--max-batch-size 8, Apple M4)

unlimited-ocr-max profile --devices gpu [--weights int8] --max-batch-size 8 --concurrency N, 12 pages, one draw each. In every row the text is byte-identical to the same weights at --max-batch-size 1, so bf16 stays 12/12 against the reference and int8 6/12 (CER 0.0011).

weights concurrency aggregate tok/s per-page latency (median) decode step (full batch)
bf16 1 35.8 14.2 s 23.9 ms (a lone request, padded to 2 rows)
bf16 2 58.3 17.1 s 23.3 ms
bf16 4 74.8 25.0 s 31.0 ms
bf16 8 85.7 40.9 s 47.6 ms
int8 1 38.4 13.3 s 21.0 ms (a lone request, padded to 2 rows)
int8 2 57.6 17.8 s 21.1 ms
int8 4 72.5 25.0 s 24.9 ms
int8 8 82.2 45.7 s 37.9 ms

Not supported

gundam (tiled) mode; multi-GPU. A new request's prefill pauses every running decode -- there is no in-flight batching.

CPU serving was removed: serve, profile and a plain max serve refuse --devices cpu. The MAX 26.7 nightlies miscompile a CPU reshape(matmul, split N) plus broadcast, so served CPU pages came out wrong with no error.

On Apple silicon, MAX does not report a GPU allocation it cannot satisfy. The computation returns wrong values instead of an error.

References

Unlimited OCR Works (Yin et al., 2026), arXiv:2606.23050. This port changes how the model is served, not the model (int8 excepted, as quantified above); model-level behaviour, limitations and biases are those of baidu/Unlimited-OCR.

@misc{yin2026unlimitedocrworks,
      title={Unlimited OCR Works},
      author={Youyang Yin and Huanhuan Liu and YY and Qunyi Xie and Chaorun Liu and Shiqi Yang and Shaohua Wang and Zhanlong Liu and Hao Zou and Jinyue Chen and Shu Wei and Jingjing Wu and Mingxin Huang and Zhen Wu and Guibin Wang and Tengyu Du and Lei Jia},
      year={2026},
      eprint={2606.23050},
      archivePrefix={arXiv}
}

Disclaimer

This port is a research project by Konstantin Thierbach and Claude (Anthropic's AI model, working in Claude Code). It asks how far one gets migrating a current model to MAX. baidu/Unlimited-OCR is the test case: a SAM and CLIP vision tower feeding a 64-expert mixture-of-experts decoder. The port rebuilds it as MAX graphs, with custom Mojo kernels where MAX has none, and serves it locally on Apple silicon as well as on NVIDIA and AMD GPUs.

The code, the measurements and this documentation came out of that collaboration. Every change is gated against pinned reference transcripts, and every figure comes from a recorded run. It remains a research artefact rather than a supported product, though. It tracks MAX's own development closely, at times pinning a nightly build, and can change or break with it. It is not affiliated with or endorsed by Baidu or Modular.

License

MIT — see LICENSE: this port's MIT notice and baidu's verbatim. The bf16 weights, tokenizer files and config.json are baidu's, from baidu/Unlimited-OCR (MIT, Copyright (c) 2026 Baidu), redistributed under that license; the bf16 weights are unchanged, config.json has two keys removed, and model-int8.safetensors is derived from those weights by this port's quantiser. The profile corpus bundles renders of four pages of that paper (arXiv:2606.23050); see unlimited_ocr_max/profile_data/NOTICE.md.

Metadata

Release files for unlimited-ocr-max 0.4.0

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

Source distribution (sdist)

Source distribution for unlimited-ocr-max 0.4.0
File Size Uploaded
unlimited_ocr_max-0.4.0.tar.gz 2.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for unlimited-ocr-max 0.4.0
File Interpreter ABI Platform
unlimited_ocr_max-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 5.1 MB

Release files / unlimited_ocr_max-0.4.0.tar.gz

Download URL unlimited_ocr_max-0.4.0.tar.gz
Size 2.6 MB
Tags Source
SHA-256 checksum
How to use checksums
f956cc885ace17f383b9ba3564aec2ac13b0a2bbd232e7a127d14962c3ceeb8d
BLAKE2b-256 checksum
How to use checksums
cbfbe0de5f8fa890ac5e6e3ef915c9480fa95a1e9781111b2810831498087bc5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / unlimited_ocr_max-0.4.0-py3-none-any.whl

Download URL unlimited_ocr_max-0.4.0-py3-none-any.whl
Size 2.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
1f60cd1042e1454d7a8ca4c708260e616677c75606ac8b5f5d65363bac8ef1e1
BLAKE2b-256 checksum
How to use checksums
992cb8c497855450f9ad104e9b7cc8c0c49dc0b53a302bc5c7841f9e6ab17ebd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.1.0

2 release files

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