Ryan Chesler commited on
Commit
65abd8a
·
1 Parent(s): 196b410

improve install docs and process

Browse files

The .gitignore excludes *.so files, which caused hatchling to silently
drop the compiled CUDA extension from the wheel.

pyproject.toml: add artifacts glob so hatchling includes the .so
despite gitignore; add ninja to build hook deps for faster compilation.

hatch_build.py: set a platform-specific wheel tag (cpXY-cpXY-linux_*)
instead of py3-none-any when the extension is built.

README.md + quickstart.md: rewrite install instructions to recommend
installing torch first with the correct CUDA index URL, then using
--no-build-isolation to avoid CUDA version mismatches during the
extension build.

README.md CHANGED
@@ -158,36 +158,53 @@ Our AI models are designed and/or optimized to run on NVIDIA GPU-accelerated sys
158
  #### Prerequisites
159
 
160
  - **OS**: Linux amd64 with NVIDIA GPU
161
- - **CUDA**: CUDA Toolkit 12.8 and compatible NVIDIA driver installed (for PyTorch CUDA). Verify with `nvidia-smi`.
162
- - **Python**: 3.12 (both subpackages require `python = ~3.12`)
163
- - **Build tools (when building the C++ extension)**:
 
 
 
164
  - GCC/G++ with C++17 support
165
- - CUDA toolkit headers (for building CUDA kernels)
166
- - OpenMP (used by the C++ extension)
167
-
168
 
169
  #### Installation
170
- The model requires torch, and the custom code available in this repository.
 
 
 
171
 
172
  1. Clone the repository
173
 
174
  - Make sure git-lfs is installed (https://git-lfs.com)
175
  ```
176
  git lfs install
 
177
  ```
178
 
179
  2. Installation
180
 
181
  ##### With pip
182
 
183
- - Create and activate a Python 3.12 environment (optional)
 
 
 
 
 
 
184
 
185
- - Run the following command to install the package:
186
 
187
  ```bash
188
  cd nemotron-ocr
189
- pip install hatchling
190
- pip install -v .
 
 
 
 
 
191
  ```
192
 
193
  ##### With docker
 
158
  #### Prerequisites
159
 
160
  - **OS**: Linux amd64 with NVIDIA GPU
161
+ - **CUDA toolkit** with `nvcc` on `PATH`. The toolkit version must be compatible with
162
+ the version of PyTorch you install (same major version). For example, if you install
163
+ `torch` with CUDA 12.8 bindings, you need CUDA toolkit 12.x. Verify with
164
+ `nvcc --version` and `nvidia-smi`.
165
+ - **Python**: 3.12 (the package requires `>=3.12,<3.13`)
166
+ - **Build tools** (for the C++ CUDA extension compiled at install time):
167
  - GCC/G++ with C++17 support
168
+ - CUDA toolkit headers
169
+ - OpenMP
 
170
 
171
  #### Installation
172
+ The package includes a C++ CUDA extension that is compiled during installation.
173
+ Because the extension must be built against the **same PyTorch CUDA version** as
174
+ your system's CUDA toolkit, **install PyTorch first**, then install this package
175
+ with `--no-build-isolation` so it uses your existing PyTorch.
176
 
177
  1. Clone the repository
178
 
179
  - Make sure git-lfs is installed (https://git-lfs.com)
180
  ```
181
  git lfs install
182
+ git clone https://huggingface.co/nvidia/nemotron-ocr-v2
183
  ```
184
 
185
  2. Installation
186
 
187
  ##### With pip
188
 
189
+ - Create and activate a Python 3.12 environment
190
+ - Install PyTorch matching your CUDA toolkit (see https://pytorch.org/get-started/locally/):
191
+
192
+ ```bash
193
+ # Example for CUDA 12.8:
194
+ pip install torch torchvision --index-url https://download.pytorch.org/whl/cu128
195
+ ```
196
 
197
+ - Install the package:
198
 
199
  ```bash
200
  cd nemotron-ocr
201
+ pip install --no-build-isolation -v .
202
+ ```
203
+
204
+ - Verify the C++ extension loads:
205
+
206
+ ```bash
207
+ python -c "from nemotron_ocr.inference.pipeline_v2 import NemotronOCRV2; print('OK')"
208
  ```
209
 
210
  ##### With docker
nemotron-ocr/hatch_build.py CHANGED
@@ -41,31 +41,45 @@ def _extension_up_to_date(project_root: Path) -> bool:
41
  return newest_so_mtime >= newest_src_mtime
42
 
43
 
 
 
 
 
 
 
 
44
  class CustomBuildHook(BuildHookInterface):
45
  def initialize(self, version: str, build_data: dict) -> None:
46
  project_root = Path(__file__).parent
47
  script_path = project_root / "scripts" / "build-extension.py"
48
 
49
  env = os.environ.copy()
50
- # Ensure the extension actually builds during package build
51
  env.setdefault("BUILD_CPP_EXTENSION", "1")
52
 
53
- # Allow users to force rebuild or skip if up-to-date
54
  force_rebuild = env.get("BUILD_CPP_FORCE", "0") == "1"
55
  build_enabled = env.get("BUILD_CPP_EXTENSION", "1") == "1"
56
 
57
- if build_enabled and not force_rebuild and _extension_up_to_date(project_root):
58
- # Cached build found and sources unchanged; skip rebuild
59
  return
60
 
61
- subprocess.run(
62
- [
63
- os.fspath(sys.executable),
64
- os.fspath(script_path),
65
- ],
66
- cwd=os.fspath(project_root),
67
- env=env,
68
- check=True,
69
- )
 
 
 
 
 
 
 
 
 
 
70
 
71
 
 
41
  return newest_so_mtime >= newest_src_mtime
42
 
43
 
44
+ def _get_platform_tag() -> str:
45
+ """Return a PEP 425 platform tag for the current system."""
46
+ import sysconfig
47
+ platform = sysconfig.get_platform().replace("-", "_").replace(".", "_")
48
+ return platform
49
+
50
+
51
  class CustomBuildHook(BuildHookInterface):
52
  def initialize(self, version: str, build_data: dict) -> None:
53
  project_root = Path(__file__).parent
54
  script_path = project_root / "scripts" / "build-extension.py"
55
 
56
  env = os.environ.copy()
 
57
  env.setdefault("BUILD_CPP_EXTENSION", "1")
58
 
 
59
  force_rebuild = env.get("BUILD_CPP_FORCE", "0") == "1"
60
  build_enabled = env.get("BUILD_CPP_EXTENSION", "1") == "1"
61
 
62
+ if not build_enabled:
 
63
  return
64
 
65
+ if not force_rebuild and _extension_up_to_date(project_root):
66
+ pass # skip rebuild, but still set the tag below
67
+ else:
68
+ subprocess.run(
69
+ [
70
+ os.fspath(sys.executable),
71
+ os.fspath(script_path),
72
+ ],
73
+ cwd=os.fspath(project_root),
74
+ env=env,
75
+ check=True,
76
+ )
77
+
78
+ # Tag the wheel as platform-specific so the .so is usable
79
+ python_tag = f"cp{sys.version_info.major}{sys.version_info.minor}"
80
+ abi_tag = python_tag
81
+ platform_tag = _get_platform_tag()
82
+ build_data["tag"] = f"{python_tag}-{abi_tag}-{platform_tag}"
83
+ build_data["pure_python"] = False
84
 
85
 
nemotron-ocr/pyproject.toml CHANGED
@@ -28,6 +28,9 @@ dev = [
28
  requires = ["hatchling", "editables"]
29
  build-backend = "hatchling.build"
30
 
 
 
 
31
  [tool.hatch.build.targets.wheel]
32
  packages = [
33
  "src/nemotron_ocr",
@@ -36,7 +39,7 @@ packages = [
36
 
37
  [tool.hatch.build.targets.wheel.hooks.custom]
38
  path = "hatch_build.py"
39
- dependencies = ["setuptools>=68", "torch>=2.0"]
40
 
41
  [tool.hatch.build.targets.sdist]
42
  include = [
 
28
  requires = ["hatchling", "editables"]
29
  build-backend = "hatchling.build"
30
 
31
+ [tool.hatch.build]
32
+ artifacts = ["src/nemotron_ocr_cpp/*.so"]
33
+
34
  [tool.hatch.build.targets.wheel]
35
  packages = [
36
  "src/nemotron_ocr",
 
39
 
40
  [tool.hatch.build.targets.wheel.hooks.custom]
41
  path = "hatch_build.py"
42
+ dependencies = ["setuptools>=68", "torch>=2.0", "ninja"]
43
 
44
  [tool.hatch.build.targets.sdist]
45
  include = [
quickstart.md CHANGED
@@ -1,16 +1,43 @@
1
  # Quickstart
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ## Installation
4
 
5
- Create a Python 3.12 environment and install the package:
 
 
6
 
7
  ```bash
 
 
 
 
8
  cd nemotron-ocr
9
- pip install hatchling
10
- pip install -v .
11
  ```
12
 
13
- Verify the installation:
 
 
 
 
 
14
 
15
  ```bash
16
  python -c "from nemotron_ocr.inference.pipeline_v2 import NemotronOCRV2; print('OK')"
@@ -54,8 +81,8 @@ ocr_profile = NemotronOCRV2(verbose_post=True)
54
  ### Example script
55
 
56
  ```bash
57
- uv run python example.py ocr-example-input-1.png
58
- uv run python example.py ocr-example-input-1.png --merge-level word
59
- uv run python example.py ocr-example-input-1.png --detector-only
60
- uv run python example.py ocr-example-input-1.png --skip-relational
61
- ```
 
1
  # Quickstart
2
 
3
+ ## Prerequisites
4
+
5
+ - **Python 3.12** (the package requires `>=3.12,<3.13`)
6
+ - **CUDA toolkit** with `nvcc` on `PATH` (the package compiles a CUDA C++ extension at install time)
7
+ - **A CUDA GPU** (or set `TORCH_CUDA_ARCH_LIST` to cross-compile, e.g. `TORCH_CUDA_ARCH_LIST="8.0 9.0"`)
8
+
9
+ The CUDA toolkit version must share the same **major version** as the CUDA
10
+ bindings in your PyTorch install (e.g. toolkit 12.4 with `torch+cu128` is fine;
11
+ toolkit 12.4 with `torch+cu130` will fail).
12
+
13
+ On Slurm clusters, run the install on a GPU node or load the CUDA module first:
14
+
15
+ ```bash
16
+ module load cuda12.4/toolkit/12.4.1 # example; adjust for your cluster
17
+ export CUDA_HOME=/usr/local/cuda # or wherever the toolkit lives
18
+ ```
19
+
20
  ## Installation
21
 
22
+ Install PyTorch **first** with bindings matching your CUDA toolkit, then install
23
+ this package with `--no-build-isolation` so it builds the C++ extension against
24
+ your existing PyTorch:
25
 
26
  ```bash
27
+ # 1. Install PyTorch (adjust the index URL for your CUDA version)
28
+ pip install torch torchvision --index-url https://download.pytorch.org/whl/cu128
29
+
30
+ # 2. Install nemotron-ocr
31
  cd nemotron-ocr
32
+ pip install --no-build-isolation -v .
 
33
  ```
34
 
35
+ > **Why `--no-build-isolation`?** Without it, pip creates a temporary build
36
+ > environment and installs the latest PyTorch from PyPI. That PyTorch's CUDA
37
+ > version may not match your system's `nvcc`, causing the C++ extension build
38
+ > to fail with a CUDA version mismatch error.
39
+
40
+ Verify the installation (the C++ extension must load without errors):
41
 
42
  ```bash
43
  python -c "from nemotron_ocr.inference.pipeline_v2 import NemotronOCRV2; print('OK')"
 
81
  ### Example script
82
 
83
  ```bash
84
+ python example.py ocr-example-input-1.png
85
+ python example.py ocr-example-input-1.png --merge-level word
86
+ python example.py ocr-example-input-1.png --detector-only
87
+ python example.py ocr-example-input-1.png --skip-relational
88
+ ```