SDK(minicpmvocr) handles layout detection (PP-DocLayoutV3), chunking, stitching, and concurrent scheduling, and calls the inference service over HTTP. The SDK and the inference service are installed in separate venvs.
1. Determine the approach
First ask the user which inference backend to use and whether the SDK and the service are on the same machine. When the user has no preference, use the default approach.
| User goal | Environment |
|---|---|
| Parse documents (default) | sdk-env + vllm-ocr-env, DSpark-accelerated version |
| Do not want to compile vLLM | sdk-env + tf-ocr-env |
| Already have a llama.cpp / OpenAI-compatible service | Install only sdk-env |
| Verbatim verification on both vLLM and transformers | Two inference environments, or a merged ocr-env(see Section 7) |
- Install only one inference environment, unless the user wants to verify on both backends.
sdk-envcannot be installed together with torch / vLLM, otherwise the numpy and opencv versions will conflict.- The SDK can run on a machine without a GPU; use
--server-urlor an SSH tunnel to connect to the remote service.
2. Pre-installation checks
nvidia-smi # 显存 ≥ 24 GB,驱动 ≥ 580
nvidia-smi --query-compute-apps=pid,used_memory --format=csv # 找一张空闲卡
df -h . # 剩余 ≥ 20 GB
- Drivers 535–579 cannot run the default combination (torch cu130). Tell the user they need to upgrade the driver, or switch to torch cu126 and compile vLLM themselves (verified on A100; not covered by this skill).
- The vLLM environment requires Python ≥ 3.11; the SDK and transformers environments require ≥ 3.10.
Paths that need to be provided by the user (ask first if not given; do not guess):
| Placeholder | Meaning |
|---|---|
<SDK_REPO> |
Official repository URL:https://github.com/OpenBMB/MiniCPM-OCR.git |
<VLLM_SRC> |
feat-ocr vLLM source directory (commit 5c2b92a5dc) |
<TRANSFORMERS_SRC> |
feat-ocr transformers source directory (commit 69eafbc5bd) |
The following commands are all run in the same working directory: environments are created under ./sdk-env and other directories, models under ./models/, the repository is cloned to ./minicpmvocr-sdk/, and the SDK source is in its sdk/ subdirectory.
3. Installation
3.1 sdk-env
python3 -m venv sdk-env && source sdk-env/bin/activate
pip install paddlepaddle-gpu==3.3.0 -i https://www.paddlepaddle.org.cn/packages/stable/cu129/
# 没有 GPU 时改装 CPU 版:pip install paddlepaddle==3.2.1
git clone <SDK_REPO> minicpmvocr-sdk
pip install -e "./minicpmvocr-sdk/sdk[all]"
pip install paddlex==3.4.2
minicpmvocr list-backends
Pass criteria: the output contains the vllm、llamacpp、openai backend and the ppdoclayout3_paddlex、ppdoclayout3_torch、ppdoclayout3_server three layout providers.
If the CPU version of paddle is installed, then every subsequent minicpmvocr command must add --set layout.device=cpu。
3.2 vllm-ocr-env
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update && sudo apt-get install -y gcc-13 g++-13 libdw-dev libelf-dev
python3.13 -m venv vllm-ocr-env && source vllm-ocr-env/bin/activate
pip install torch==2.14.0 torchaudio==2.11.0 torchvision==0.29.0
pip install "transformers>=5.16.1,<5.20.0"
pip install "setuptools-rust>=1.9.0" "setuptools-scm>=8.0" "setuptools>=77.0.3,<81.0.0" \
"cmake>=3.26.1" ninja packaging wheel jinja2
pip install nvidia-cuda-nvcc==13.0.48 nvidia-cuda-crt==13.0.48 nvidia-nvvm==13.0.48 \
nvidia-cuda-runtime==13.0.96 nvidia-cuda-nvrtc==13.0.88 nvidia-nvjitlink==13.4.92 \
nvidia-cuda-cccl==13.3.4.3.1
nvidia-cuda-cccl cannot be omitted; if missing, it reports cuda_fp16.h: nv/target: No such file or directory。
Complete the directory layout for pip CUDA packages:
CU="$VIRTUAL_ENV/lib/python$(python -c 'import sys; print("%d.%d" % sys.version_info[:2])')/site-packages/nvidia/cu13"
mkdir -p "$CU/nvvm/bin" && ln -sf "$CU/bin/cicc" "$CU/nvvm/bin/cicc"
ln -sfn lib "$CU/lib64"
ln -sf "$CU/lib/libcudart.so.13" "$CU/lib/libcudart.so"
ln -sf "$CU/lib/libnvrtc.so.13" "$CU/lib/libnvrtc.so"
mkdir -p "$CU/lib64/stubs" && ln -sf /usr/lib/x86_64-linux-gnu/libcuda.so.1 "$CU/lib64/stubs/libcuda.so"
If the 13.0.48 package is missing bin/crt/link.stub、prelink.stub or bin/nvdisasm, copy them from an environment on the same machine that has 13.4.92 installed to the corresponding location under $CU/bin/ .
Compilation (about 20 minutes on 96 cores; lower it when memory is small: MAX_JOBS):
cd <VLLM_SRC>
# 无法访问 GitHub 时,先设置下面“离线编译”中的变量
CUDA_HOME="$CU" PATH="$VIRTUAL_ENV/bin:$CU/bin:$PATH" \
CC=gcc-13 CXX=g++-13 CUDAHOSTCXX=g++-13 MAX_JOBS=64 \
pip install -e . --no-build-isolation --progress-bar off
cd -
python -c "from vllm import LLM; print('vLLM OK')"
Do not use import vllm._C to determine whether compilation succeeded. This branch's extension name is _C_stable_libtorch.abi3.so。
The DSpark main model uses the minicpm_ocr architecture, which the transformers on PyPI does not support. Install feat-ocr transformers after compiling vLLM; the order cannot be reversed:
pip install --no-deps -e <TRANSFORMERS_SRC>
python -c "import transformers; print(transformers.__version__)" # 应为 5.19.0.dev0
Offline compilation (when CMake times out fetching dependencies such as CUTLASS):
DEPS=<已有 vLLM 源码>/.deps
export VLLM_CUTLASS_SRC_DIR="$DEPS/cutlass-src"
export VLLM_FLASH_ATTN_SRC_DIR="$DEPS/vllm-flash-attn-src"
export FLASH_KDA_SRC_DIR="$DEPS/flashkda-src"
export DEEPSELECT_SRC_DIR="$DEPS/deepselect-src"
export FLASH_MLA_SRC_DIR="$DEPS/flashmla-src"
export TRITON_KERNELS_SRC_DIR="$DEPS/triton_kernels-src/python/triton_kernels/triton_kernels"
export TML_FA4_SRC_DIR="$DEPS/tml_fa4-src"
export FMHA_SM100_SRC_DIR="$DEPS/fmha_sm100-src"
export QUTLASS_SRC_DIR="$DEPS/qutlass-src"
export DEEPGEMM_SRC_DIR="$DEPS/deepgemm-src"
export CMAKE_ARGS="-DCUTLASS_ENABLE_TESTS=OFF" # 否则还会拉取 googletest
Warnings that can be ignored after compilation:
vllm.third_party.deep_gemm ... undefined symbol: _ZSt8to_charsPcS_DF128_: the OCR model does not use DeepGEMM.FlashInfer's precompiled kernels are missing or do not match: cold start will be slower (measured initialization is about 104 seconds), and results are not affected.
3.3 tf-ocr-env
python3 -m venv tf-ocr-env && source tf-ocr-env/bin/activate
pip install torch==2.14.0 torchvision
pip install -e <TRANSFORMERS_SRC> # 需要 OpenAI 兼容服务时装 "<TRANSFORMERS_SRC>[serving]"
pip install accelerate
# 可选加速内核,不装时回退参考实现,结果相同但更慢:
# pip install causal-conv1d flash-linear-attention
3.4 Download the model
Main model:MiniCPM-OCR-1.0; DSpark draft model:MiniCPM-OCR-1.0-DSpark。
# DSpark:主模型和 draft 必须来自同一次发布,不能和其他 checkpoint 混用
HF_ENDPOINT=https://hf-mirror.com hf download openbmb/MiniCPM-OCR-1.0 --local-dir ./models/MiniCPM-OCR-1.0
HF_ENDPOINT=https://hf-mirror.com hf download openbmb/MiniCPM-OCR-1.0-DSpark --local-dir ./models/MiniCPM-OCR-1.0-DSpark
sha256sum ./models/MiniCPM-OCR-1.0-DSpark/model.safetensors # 对照 release_manifest.json 的 weight_sha256
curl -L -o PP-DocLayoutV3_infer.tar \
https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/PP-DocLayoutV3_infer.tar
tar xf PP-DocLayoutV3_infer.tar
export OCR_LAYOUT_MODEL_DIR=$PWD/PP-DocLayoutV3_infer
test -f "$OCR_LAYOUT_MODEL_DIR/inference.pdiparams" && echo "layout OK"
- ModelScope:
modelscope download --model <MS_REPO_ID> --local_dir <同名目录>。 - The main model directory should contain
config.json、model.safetensors、tokenizer.json、preprocessor_config.json、chat_template.jinja。 OCR_LAYOUT_MODEL_DIRmust be a Paddle inference directory (containinginference.pdiparams、inference.json、inference.yml), not a safetensors directory.
4. Start the inference service
Before starting vLLM in each new shell, set CUDA_HOME, otherwise the first startup on Blackwell GPUs will report Could not find nvcc:
source vllm-ocr-env/bin/activate
CU="$VIRTUAL_ENV/lib/python$(python -c 'import sys; print("%d.%d" % sys.version_info[:2])')/site-packages/nvidia/cu13"
export CUDA_HOME="$CU" PATH="$VIRTUAL_ENV/bin:$CU/bin:$PATH"
4.1 DSpark accelerated version (default)
CUDA_VISIBLE_DEVICES=<空闲卡> python -m vllm.entrypoints.openai.api_server \
--model ./models/MiniCPM-OCR-1.0 \
--served-model-name minicpm-ocr \
--port 18499 \
--max-model-len 40960 \
--max-num-batched-tokens 40960 \
--max-num-seqs 1 \
--attention-backend FLASH_ATTN \
--mm-encoder-attn-backend FLASH_ATTN \
--mm-processor-kwargs '{"downsample_mode":"4x"}' \
--gpu-memory-utilization 0.85 \
--trust-request-mm-kwargs \
--speculative-config '{"method":"dspark","model":"./models/MiniCPM-OCR-1.0-DSpark","num_speculative_tokens":15,"enable_adaptive_verification":false}' \
--default-chat-template-kwargs '{"enable_thinking":false}'
--max-num-seqs 1means only one request is processed at a time; concurrent requests from the SDK will queue up. When the user needs high throughput, start multiple instances and repeatedly pass--server-url, or switch to the standard version.- When starting the service, use
--served-model-name minicpm-ocr; the SDK parses the command and correspondingly uses--model minicpm-ocr. When changing the name, both places must be changed together.
4.2 Standard version (no speculative sampling)
CUDA_VISIBLE_DEVICES=<空闲卡> python -m vllm.entrypoints.openai.api_server \
--model ./models/MiniCPM-OCR-1.0 \
--served-model-name minicpm-ocr \
--port 18499 \
--max-model-len 40960 \
--limit-mm-per-prompt '{"image": 36}' \
--gpu-memory-utilization 0.85 \
--trust-request-mm-kwargs
Both versions must include --trust-request-mm-kwargs. If it is omitted, the service returns 400 for every request, and the SDK silently retries until it reports an error after 600 seconds, which looks like it is stuck.
4.3 transformers serve
source tf-ocr-env/bin/activate
transformers serve ./models/MiniCPM-OCR-1.0 --port 18499 --host 0.0.0.0 --continuous-batching
4.4 Readiness check
curl --noproxy 127.0.0.1 http://127.0.0.1:18499/v1/models # 首次启动约 3 分钟
Only when the response contains "id":"minicpm-ocr" is it considered ready. If the first startup times out during the CUDA graph capture stage or is terminated by SIGTERM, restart it once directly (the compilation cache has already been generated); if it still fails, add --enforce-eager。
5. Parse documents
source sdk-env/bin/activate
export OCR_LAYOUT_MODEL_DIR=$PWD/PP-DocLayoutV3_infer
export PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK=True
minicpmvocr parse --config minicpmvocr-sdk/configs/backends/vllm.yaml \
--server-url http://127.0.0.1:18499 \
--model minicpm-ocr \
--set input.limit=1 --input <文件> --output out.md
- First use
--set input.limit=1to parse only one page; after confirming it passes, remove it and parse all pages. - The configuration files are all in
minicpmvocr-sdk/configs/backends/: for vLLM usevllm.yaml; for transformers serve and other OpenAI-compatible services useopenai.yaml; for llama.cpp usellamacpp.yaml。 - When using
openai.yamlwith--set engine.extra_body='{"chat_template_kwargs":{"enable_thinking":false}}', otherwise the output may be empty or garbled.vllm.yamlhas thinking mode automatically disabled. - On multi-GPU machines, layout detection uses GPU 0 by default. When GPU 0 is occupied, add
--set layout.device=cuda:N。 - Except for
parse, all commands (run、parse-layout、prepare、infer、show-config) must use--set input.file_path、input.files_dirorinput.image_dirto specify the input. --setare resolved relative to the directory containing the configuration file; always write absolute paths (e.g.$PWD/doc.pdf)。--inputis resolved relative to the current directory.runThe output directory ofrun.run_tagin the configuration; pages that already have results are skipped. Before rerunning, change--set run.run_tag=<新名字>。- Artifacts are written by default under
runs/in the SDK directory. When disk space is tight, add--set run.run_root=<其他盘的绝对路径>。
6. Verify results
python - <<'PY'
from pathlib import Path
md = Path("out.md").read_text(encoding="utf-8")
assert md.strip(), "输出为空"
assert "<otsl>" not in md, "表格仍是 OTSL,没有转成 HTML"
print("OK", len(md), "chars", "| table" if "<table>" in md else "")
PY
- Table pages should have
<table>; formula pages should have LaTeX (e.g.\frac); body-text pages should be readable text. - For the DSpark version, also confirm that speculative sampling is taking effect. After sending a few requests:
curl --noproxy 127.0.0.1 -s http://127.0.0.1:18499/metrics | grep spec_decode_num
You should see spec_decode_num_accepted_tokens_total and spec_decode_num_drafts_total, and the values should increase with requests. accept length = 1 + accepted / drafts; in the validation environment it is 5.2–7.0. If these two items are absent, the service was not started with --speculative-config. The service responding does not mean DSpark is taking effect.
7. Optional: word-by-word verification on both ends
Do this only when the user requests it.verify_transformers.py and verify_vllm.py are provided with the feat-ocr source code and are not in the SDK. If the user does not have these two scripts, skip it and do not claim that both ends have been verified to be consistent.
If you want to run both ends in one environment, execute in the environment where vLLM has been compiled (the order cannot be reversed):
pip install --no-deps -e <TRANSFORMERS_SRC>
pip install --no-deps accelerate
python -c "import transformers, vllm, torch; print(transformers.__version__, vllm.__version__, torch.__version__)"
Run the two ends separately:
# transformers 端(tf-ocr-env 或合并环境)
CUDA_VISIBLE_DEVICES=<空闲卡> python verify_transformers.py \
--model ./models/MiniCPM-OCR-1.0 --image table.png \
--prompt "请识别图中的表格并输出 OTSL" --attn sdpa --max-new-tokens 512
# vLLM 端(vllm-ocr-env 或合并环境,先按第 4 节设置 CUDA_HOME)
CUDA_VISIBLE_DEVICES=<空闲卡> python verify_vllm.py \
--model ./models/MiniCPM-OCR-1.0 --image table.png \
--prompt "请识别图中的表格并输出 OTSL" --max-tokens 512
Pass criteria:
- When loading on the transformers side, there are no missing, unexpected, or mismatched keys, and the parameter count is 1.300B.
- Both ends have the same number of prompt tokens, and the outputs are identical character by character, both ending with
</otsl>.
Reference values (1080×1080 table image): prompt 1975 tokens, vision 1936 tokens, generation 284 tokens, output 677 characters. The values change after switching images or prompts; the criterion is that both ends match.
8. Error handling
Installation and compilation
| Symptom | Handling |
|---|---|
Failed to clone https://github.com/nvidia/cutlass.git |
Cannot access GitHub; use the offline compilation setup in 3.2 for *_SRC_DIR |
After disabling CUTLASS fetching, still need to fetch googletest |
Set CMAKE_ARGS="-DCUTLASS_ENABLE_TESTS=OFF" |
Could not find a package configuration file provided by "MLIR" |
TRITON_KERNELS_SRC_DIR level is wrong; it should point to .../python/triton_kernels/triton_kernels |
During compilation, many FAILED: [code=9] |
compile processes are killed due to insufficient memory. Lower MAX_JOBS(use 8–32 for 147 GB of memory), install first ccache to avoid recompiling |
ld: cannot find -lcudart |
Add the lib64、libcudart.so、stubs/libcuda.so link from 3.2 |
CUDA_nvrtc_LIBRARY ... NOTFOUND |
Add libnvrtc.so link |
cuda_fp16.h ... nv/target: No such file |
Install nvidia-cuda-cccl==13.3.4.3.1 |
No module named vllm._C |
It is not a compilation failure; verify with from vllm import LLM Verify |
In the vLLM environment, transformers is 5.19.0 rather than 5.19.0.dev0 |
The editable install was overwritten by a vLLM dependency; reinstall pip install --no-deps -e <TRANSFORMERS_SRC> |
Layout reports missing pypdfium2 |
pip install "pypdfium2>=4", which is also needed when only processing images |
Start the service
| Symptom | Handling |
|---|---|
model type 'minicpm_ocr' ... does not recognize this architecture |
The vLLM environment is missing feat-ocr transformers; install it per 3.2. No need to recompile vLLM |
Could not find nvcc |
Not set CUDA_HOME, see Section 4 |
ptxas fatal: Unsupported .version 9.4 or CUDA compiler and CUDA toolkit headers are incompatible |
Unify nvcc, runtime, nvrtc, and nvjitlink to 13.4.92, then add the links per 3.2 |
flashinfer-cubin version does not match |
pip uninstall flashinfer-cubin;vllm download-kernels run it when it exists |
CUDA driver version is insufficient |
Driver is lower than 580, see Section 2 |
When the driver is < 580, it reports TopKMaskLogits ... device kernel image is invalid |
Set VLLM_USE_FLASHINFER_SAMPLER=0; greedy decoding gives the same result |
When the driver is < 580, it reports nvcc fatal: Unknown option '--compress-mode=size' |
CUDA_HOME point to a toolchain that matches torch (cu126 corresponds to 12.8) |
When importing vLLM, it reports libtorchcodec_image.so |
pip uninstall torchcodec |
Free memory on device ... less than desired GPU memory utilization |
This card is occupied; switch to CUDA_VISIBLE_DEVICES |
| OOM | --gpu-memory-utilization lower to 0.7, or --max-model-len lower to 16384 |
| CUDA graph capture times out or is terminated by SIGTERM | Restart once; if it still fails, add --enforce-eager |
Parsing
| Symptom | Handling |
|---|---|
Parsing produces no result for a long time, and the log shows request failed until timeout |
The service omitted --trust-request-mm-kwargs; after the request was rejected, the SDK kept retrying until the 600-second timeout |
After waiting for a long time, the SDK reports no ... endpoint became ready within 1800s |
The service is not up or the address is wrong; first check with curl /v1/models Check |
| Image requests return 400 | The standard version omitted --limit-mm-per-prompt '{"image": 36}' |
| Output is empty or garbled | Thinking mode is not disabled, see Section 5 |
The table is <otsl> instead of <table> |
It is not converted when calling the API directly; use the SDK pipeline |
crop of a small image reports assign N multimodal tokens to M placeholders |
The vLLM version is too old; switch to the feat-ocr build |
| Layout reports a CUDA error | The CPU version of paddle is installed; add --set layout.device=cpu |
At least one of input.image_dir / input.files_dir / input.file_path is required |
Add --set input.file_path=<文件的绝对路径> |
input.file_path not found, but the file exists |
--set uses a relative path; change it to an absolute path |
run Reran it and the result is unchanged |
Switch to --set run.run_tag=<新名字> |
| pip timed out | Switch to a domestic mirror. When accessing localhost, set no_proxy=127.0.0.1,localhost,::1 |
| The service is on a remote machine | ssh -L 18499:127.0.0.1:18499 user@host; for the SDK use --server-url http://127.0.0.1:18499 |
9. Verified versions
| Component | Version |
|---|---|
| vLLM environment | Python 3.13.9, torch 2.14.0+cu130, vLLM source 5c2b92a5dc,feat-ocr transformers 69eafbc5bd |
| transformers environment | Python 3.10.12 / 3.13.9,torch 2.14.0+cu130,transformers 5.19.0.dev0(69eafbc5bd) |
| SDK environment | minicpmvocr 0.2.3, paddlepaddle-gpu 3.3.0 or paddlepaddle 3.2.1, paddlex 3.4.2 |
| Hardware | 3×RTX 5090, driver 590.48.01, Ubuntu 22.04.5 (2026-10-19) |
10. Reporting rules
- The 5090 is only the machine used for verification; do not say that a 5090 is required. The threshold is VRAM ≥ 24 GB and driver ≥ 580.
- Report only the checks that were actually run. If you have not run verification on both ends, do not say the two ends are consistent; if you have not checked
/metrics, do not say DSpark is in effect. - There is no Web UI script in the SDK repository; do not mention to users or guide users to run any
demo_*.py。