# Deploy NeAR to Hugging Face Space Target Space: [luh0502/NeAR](https://huggingface.co/spaces/luh0502/NeAR). **This file lives in the Space clone** (e.g. `hf-near`) — edit and push **from this repo**, not only from a separate template folder. ## 1. Clone and push this repo to the Space ```bash git clone https://huggingface.co/spaces/luh0502/NeAR hf-near cd hf-near # …copy or sync code, then: git add . git commit -m "Update NeAR Space" git push ``` If you maintain a separate template tree (e.g. `NeAR_space`), copy changes **into `hf-near`** before `git push`. ## 2. Space settings - **Hardware**: select a **GPU** tier. This app needs CUDA for Hunyuan3D, NeAR, and `spconv` / `xformers`. - **Secrets**: add `HF_TOKEN` (read) if you pull gated Hub models or mirrored auxiliary assets from the Hub (e.g. rembg / Hunyuan weights / DINOv2 mirror). - Checkpoints for the NeAR pipeline load from Hub repo **`luh0502/NeAR`** by default (`NEAR_PRETRAINED` in `app.py`). Public repos work without a token; a token avoids rate limits. - Keep binary wheels and mirrored auxiliary assets in separate repos: - **`luh0502/near-wheels`**: prebuilt `.whl` files - **`luh0502/near-assets`**: torch.hub-compatible mirrored repos and other runtime helper assets ### 2b. ZeroGPU (dynamic GPU allocation) [ZeroGPU](https://huggingface.co/docs/hub/spaces-zerogpu) shares NVIDIA H200 capacity across Spaces. It is **not** the same as picking a fixed T4/L4/A10G GPU. **Who can host a ZeroGPU Space** - Personal Hub account: **[PRO](https://huggingface.co/subscribe/pro)** (ZeroGPU appears in hardware when creating/editing the Space). - Organization: **Team / Enterprise** plans. **README / SDK (required)** - Use **`sdk: gradio`** in `README.md` frontmatter. Official docs state ZeroGPU targets the **Gradio SDK** path. - Do **not** use **`sdk: docker`** for the same Space if you expect ZeroGPU integration to match the documented path (Docker Spaces are a separate deployment model). **Space settings** 1. Open the Space → **Settings** → **Hardware**. 2. Select **ZeroGPU** (wording may vary slightly in the UI). 3. Save and wait for the Space to rebuild/restart. **Python / Gradio versions** - Follow the [supported versions](https://huggingface.co/docs/hub/spaces-zerogpu) in the ZeroGPU doc (e.g. Gradio 4+, specific Python patch versions on the builder). This repo sets `python_version` in `README.md` for the Gradio builder. **Dependencies** - `requirements.txt` must include the **`spaces`** package (HF injects it in some builds, but listing it explicitly is safe). - Keep **`gradio[oauth,mcp]`** (or equivalent) aligned with the Space runtime if you use OAuth/MCP features. **Application code (this repo)** - **`import spaces`** (optional `try/except` for local runs without the package). - Decorate **every Gradio callback that uses CUDA** with **`@spaces.GPU`** (same as [E-RayZer](https://huggingface.co/spaces/qitaoz/E-RayZer): no `duration=` in app code — platform defaults apply). This repo aliases it as **`GPU`** in `app.py` and uses **`@GPU`**; locally, without the `spaces` package, it is a no-op. The decorator is effectively a no-op off ZeroGPU per HF docs. - Keep **page-load defaults and HDRI preview off the heavy model path**. This repo now uses a lightweight CPU image-preprocess path and a CPU-only HDRI preview path, so first page load no longer triggers full model initialization. - **Model init**: a background thread (when **`NEAR_MODEL_CPU_PRELOAD_AT_START=1`**) loads **Hunyuan + NeAR** on **CPU** at process start (no GPU lease). **`@spaces.GPU`** callbacks call **`ensure_geometry_on_cuda()`** / **`ensure_near_on_cuda()`** to move weights to CUDA once, then run inference. **gsplat** is exercised only when the pipeline renders (first call may still JIT if no prebuilt wheel). - **Space Variables**: at the top of `app.py` (before `import spaces`), **`NEAR_ZEROGPU_MAX_SECONDS`** / **`NEAR_ZEROGPU_DURATION_CAP`** are **rewritten in `os.environ`** if they exceed **`NEAR_ZEROGPU_HF_CEILING_S`** (default **90**, max **120**) so values like `300` cannot break the Hub runtime. This does not set per-callback `duration` in Python; it only clamps env vars HF may read. ### 2b1. Recommended runtime variable matrix | Variable | ZeroGPU-first recommendation | Fixed GPU / roomier budget | |----------|------------------------------|-----------------------------| | `HF_TOKEN` | Set | Set | | `NEAR_AUX_REPO` | `luh0502/near-assets` | `luh0502/near-assets` | | `NEAR_DINO_REPO_SUBDIR` | `dinov2` | `dinov2` | | `NEAR_DINO_MODEL_ID` | leave unset unless your mirror renames hubconf entries | leave unset unless your mirror renames hubconf entries | | `NEAR_DINO_FILENAME` | optional validation file inside the mirror repo | optional validation file inside the mirror repo | | `NEAR_GSPLAT_SOURCE_SPEC` | unset unless you have a proven build path | optional if you want build-time source compile | | `NEAR_ZEROGPU_HF_CEILING_S` | `90` | tune to your tier | | `NEAR_HYSHAPE_GEOMETRY_CPU_PRELOAD_AT_START` | `1` when Space entry is **`app_hyshape.py`** (default: background thread runs `from_pretrained(..., device="cpu")` at startup — **no** `@spaces.GPU`) | `0` to defer CPU load until the first **Generate Mesh** click (inside the GPU callback; longer first click) | ### 2b2. Mirroring DINOv2 and other auxiliary assets The app no longer calls `torch.hub.load("facebookresearch/dinov2", ...)` directly against GitHub at runtime. Instead it resolves a **local or HF-cached mirror**: 1. If **`NEAR_DINO_LOCAL_REPO`** points to a local directory containing `hubconf.py`, that path wins. 2. Otherwise the app downloads **`NEAR_AUX_REPO`** (default **`luh0502/near-assets`**) with optional subdir **`NEAR_DINO_REPO_SUBDIR`** (default **`dinov2`**) through `huggingface_hub`. 3. The resolved directory must be **torch.hub-compatible** and contain `hubconf.py`. Recommended contents of `luh0502/near-assets`: - `dinov2/` — a mirror of the DINOv2 torch.hub repo, including `hubconf.py` - mirrored weights or helper files expected by that repo - any future lightweight runtime-only helper assets you do not want to place in `near-wheels` This split keeps **wheel ABI artifacts** and **runtime model/helper assets** separate, which makes upgrades and cache invalidation much easier. ### 2b3. gsplat: first-render JIT (optional mitigations) Similar to [E-RayZer](https://huggingface.co/spaces/qitaoz/E-RayZer), the first real call into **gsplat** can spend a long time compiling CUDA. This repo supports: | Variable | Purpose | |----------|---------| | **`NEAR_GSPLAT_WHEEL_URL`** | If set, `app.py` / `app_gsplat.py` runs `pip install --force-reinstall` on this URL **before** importing gsplat/trellis. Use when you host a **cp310 manylinux** wheel on `near-wheels` built against **your exact** PyTorch/CUDA pin (official gsplat wheels top out at **pt2.4+cu124**; see `requirements.txt`). | | **`NEAR_GSPLAT_SOURCE_SPEC`** | If set, `app.py` runs `pip install --no-build-isolation` on this spec **before** importing `trellis` (e.g. `./third_party/gsplat` after vendoring, or `git+https://github.com/nerfstudio-project/gsplat.git@`). Needs **nvcc** — usually absent on the default Gradio builder. | Alternatively, pin a **VCS** gsplat line in `requirements.txt` (e.g. `gsplat @ git+https://...`) so the **Space build** step compiles once; no `NEAR_GSPLAT_SOURCE_SPEC` needed. **Pushing to the Hub** - Same workflow as §1: **`git lfs install`**, commit, **`git push`** (see §4 for binaries / LFS). - Avoid **`git push -f`** with an empty template commit — it can wipe `assets/`, `trellis/`, etc. from history. **Trade-offs vs fixed GPU + Docker** - **ZeroGPU + Gradio builder**: the image may **not** include a full CUDA toolkit (`nvcc`, `CUDA_HOME`). **`git+…nvdiffrast`** (source install) often fails here. This repo uses a **prebuilt `nvdiffrast` wheel** URL in `requirements.txt` (see **§5**) so the builder only downloads a wheel. If that wheel is ABI-incompatible with our PyTorch pin, build your own wheel or add a **`Dockerfile`** with `nvidia/cuda:*-devel` and fixed GPU. - **Quota**: visitors consume **daily GPU seconds**. This repo keeps plain `@spaces.GPU` in Python and clamps **`NEAR_ZEROGPU_*`** via **`NEAR_ZEROGPU_HF_CEILING_S`** rather than setting `duration=` in app code. ### 2c. Example gallery empty on the Space (`assets/` “not deployed”) Bundled thumbnails / HDRI / SLaTs live under **`assets/`** and are tracked with **Git LFS** (see root `.gitattributes`: `*.png`, `*.exr`, `*.npz`, …). If the UI shows **no examples** or **`No PNG examples`**, usually: 1. **`assets/` was never pushed** on the branch the Space uses — add, commit, and `git push`. 2. **Only LFS pointer files reached the Hub** — on the website, open `assets/example_image/T.png`: if it is ~100–200 bytes and starts with `version https://git-lfs.github.com/spec/v1`, LFS objects were not uploaded. From a machine that has the real files: ```bash git lfs install git lfs push --all origin ``` 3. **Sparse / partial clone** — ensure you are not using a workflow that omits `assets/` (e.g. copying only `app.py` into a fresh Space). `app.py` logs **`[NeAR] WARNING: … LFS pointers`** when it detects pointer stubs at runtime so Space build logs are easier to read. ## 3. Files used by Spaces | File | Role | |------|------| | `README.md` | Must start with YAML frontmatter (`sdk: gradio`, `app_file: …`, `sdk_version`, …). The **`app_file`** value is what Spaces should launch after each build. | | `app.py` | Full NeAR entry point; uses `PORT` / `GRADIO_SERVER_*` env vars. | | `app_hyshape.py` | Optional HyShape-only diagnostic entry (see repo README). | | `app_gsplat.py` | Optional gsplat image-fitting Gradio demo (`@spaces.GPU`). | If the running Space does not match `README.md` **`app_file`**, check **Space → Settings → App file** (dashboard can disagree until you align it) and **Restart** the Space. Use the **spaces** URL (`/spaces//`), not the model repo URL. | `requirements.txt` | `pip` dependencies. | | `packages.txt` | Optional `apt` packages (OpenGL, etc.). | Root **`packages.txt`** lists **`libgl1`** and **`libopengl0`** so imports that pull in **`pymeshlab`** are less likely to spam logs about **`libOpenGL.so.0`**. Those plugins are not required for the core NeAR / HyShape paths; if warnings persist after rebuild, they are usually safe to ignore. ## 4. Git push: large files & binary files on the Hub ### 4a. Files **> 10 MiB** in plain Git HF rejects them unless you use [Git LFS](https://git-lfs.github.com/) or [Git Xet](https://huggingface.co/docs/hub/xet/using-xet-storage#git). Example: `hy3dshape/demo.glb` is a **generated demo artifact** — remove from Git and ignore it (see `.gitignore`). If it is **already in history**, rewrite history (orphan branch or `git filter-repo`) then `git push -f`. ### 4b. **Binary files** (examples) — “use Xet” rejection The Hub may reject **any** tracked binary that is not stored via **LFS / Xet**. Root **`.gitattributes`** only lists **NeAR bundle types**: images (`*.png`, …), HDRI (`*.exr`), meshes (`*.glb`, `*.gltf`), and example SLaTs (`*.npz`). Model weights stay on the Hub — no broad `*.safetensors` / `*.ckpt` rules here. **One-time setup on the machine you push from:** ```bash git lfs install # Recommended for Hub (handles Xet-backed transfers): https://huggingface.co/docs/hub/xet/using-xet-storage#git curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/huggingface/xet-core/refs/heads/main/git_xet/install.sh | sh git xet install ``` **If those files were already committed as normal Git blobs**, migrate them into LFS and rewrite history (then force-push): ```bash cd ~/code/3diclight/hf-near # your Space clone git lfs install git lfs migrate import --include="*.png,*.jpg,*.jpeg,*.gif,*.webp,*.exr,*.glb,*.gltf,*.npz" --everything git push -f origin main ``` Keep `--include` in sync with `.gitattributes`. If you later commit other large binaries, add their extensions there and re-run migrate for those patterns. ### 4c. Start clean: delete `.git` and one fresh commit (LFS from day one) Use this when history is full of plain-Git binaries and you prefer **one new root commit** over `git lfs migrate`. 1. **Save the remote** (you will need it after `rm -rf .git`): ```bash cd ~/code/3diclight/hf-near git remote -v ``` 2. **Install Git LFS** (if needed): `conda install -c conda-forge git-lfs` or `sudo apt install git-lfs`. 3. **Remove Git metadata only** (does not delete your working files): ```bash rm -rf .git ``` 4. **Re-init with LFS before the first `git add`** so every PNG/NPZ/… is stored as LFS from the start: ```bash git init git lfs install # Optional but recommended on HF: https://huggingface.co/docs/hub/xet/using-xet-storage#git # curl ... git_xet/install.sh | sh && git xet install ``` 5. **Confirm** root `.gitattributes` is present (it lists `*.png`, `*.npz`, etc.). If you add it only *after* `git add`, run `git rm --cached` on those paths and re-add. 6. **Commit and force-push** (overwrites Space `main`): ```bash git branch -M main git add . git commit -m "Initial NeAR Space (LFS from first commit)" git remote add origin git@hf.co:spaces/luh0502/NeAR # or: git remote add origin https://huggingface.co/spaces/luh0502/NeAR git push -u -f origin main ``` 7. **Check locally** before push: `git lfs ls-files | head` should list example images/slats/hdris. 8. **Verify LFS has real bytes** (not pointer stubs on disk): ```bash head -1 assets/example_image/T.png wc -c assets/example_image/T.png ``` If the first line is `version https://git-lfs.github.com/spec/v1` and size is ~100–200 bytes, your tree contains **LFS pointer text**, not the image. `git push` will fail with **`(missing)`** because `.git/lfs/objects/...` was never filled. Fix: copy **full binaries** from a complete source tree (e.g. `NeAR_space` or an old `hf-near` backup), then re-stage: ```bash git lfs install git rm -rf --cached assets trellis/representations/mesh/flexicubes/images git add assets trellis/representations/mesh/flexicubes/images git commit --amend -m "Initial NeAR Space (LFS from first commit)" # or make a new commit if you do not want to amend git lfs fsck # should not report missing objects for paths you care about git push -u -f origin main ``` Do **not** set `lfs.allowincompletepush` to true — that pushes broken history. **Caveats:** `-f` replaces all history on the Hub Space. Anyone with an old clone should re-clone. Uncommitted work: commit or stash before step 3 (here you only remove `.git`, so working tree stays). ### 4d. Push fails: `LFS upload failed` / `(missing) …` Git is trying to upload LFS objects listed in your commits, but **the large files are not in the local LFS store**. Common causes: - **`GIT_LFS_SKIP_SMUDGE=1`** clone (or a clone from a Space that never stored LFS on the server): working tree stays as tiny pointer files; a later `git add` / commit keeps that broken state. - **Copying only `.git`** to another machine without copying `.git/lfs/objects`. - **`git add` before `git lfs install`** in a fresh repo (then fixing `.gitattributes` without unstaging and re-adding). **Fix:** Restore **real** PNG/JPG/GIF/EXR/NPZ files on disk (see §4c step 8), then `git rm --cached` + `git add` those trees and commit again. Confirm with `git lfs fsck` before `git push`. **Alternative (smaller Space repo):** do not commit the whole `assets/example_image/` gallery — keep only `T.png` (and one HDRI), add the rest to `.gitignore`, and use an orphan commit so history contains no offending blobs. ## 5. `nvdiffrast` on Gradio / ZeroGPU (prebuilt wheel) Many 3D repos use **nvdiffrast**. The HF **Gradio** builder usually **lacks `nvcc`**, so **`git+https://github.com/NVlabs/nvdiffrast`** (source build) often fails. **What this repo does** - `requirements.txt` installs **`nvdiffrast` from a direct `.whl` URL** (same pattern as [microsoft/TRELLIS.2](https://huggingface.co/spaces/microsoft/TRELLIS.2) — wheel hosted under [JeffreyXiang/Storages releases](https://github.com/JeffreyXiang/Storages/releases)). The builder only **downloads** the wheel; no local compile. **ABI / version caveat** - Third-party wheels (e.g. from [JeffreyXiang/Storages](https://github.com/JeffreyXiang/Storages/releases)) are often built for **another** torch/CUDA stack. Mismatches show up as **`ImportError` / `undefined symbol: _ZN3c10...`** when loading `_nvdiffrast_c`. **Fix:** use a wheel you built with the **same** torch major/minor as `requirements.txt` (here **torch 2.8 + cu128 + cp310 + Linux x86_64**). **Hosting your own `nvdiffrast` wheel** The Space builder only sees **URLs** in `requirements.txt`, not files on your laptop. After you have e.g. `nvdiffrast-0.4.0-cp310-cp310-linux_x86_64.whl`: 1. **Upload** the file to a stable HTTPS location, for example: - **Hugging Face Hub** — create a small repo (e.g. `yourname/near-wheels`), then: ```bash pip install -U "huggingface_hub[cli]" hf auth login hf upload yourname/near-wheels /path/to/nvdiffrast-0.4.0-cp310-cp310-linux_x86_64.whl ./ ``` - Or **GitHub Releases** — attach the `.whl` as a release asset (same idea as TRELLIS v1 wheels). 2. **Put the raw download URL** on its own line in **`requirements.txt`** (replace the old nvdiffrast URL). Examples: - Hub: `https://huggingface.co/yourname/near-wheels/resolve/main/nvdiffrast-0.4.0-cp310-cp310-linux_x86_64.whl` - You may append `?download=true` if a host requires it for `pip`. 3. **Do not** commit multi‑MB wheels into the **Space** git repo unless you use **Git LFS** / Xet and accept the history size; a separate **wheels** Hub repo is usually simpler. 4. Re‑push the Space; **`pip` will download and install** the wheel during the image build (no `nvcc` needed). **Uploading mirrored aux assets** Use the same CLI for `near-assets`. Example: ```bash pip install -U "huggingface_hub[cli]" hf auth login hf upload-large-folder luh0502/near-assets ./near-assets-local ``` Suggested layout: ```text near-assets-local/ dinov2/ hubconf.py ... ``` **Fallback: Docker + devel image** - If wheels are not viable, use **`sdk: docker`**, an **`nvidia/cuda:*-devel`** base, **`git+…nvdiffrast`**, and a **fixed GPU** Space. That path is **not** ZeroGPU in the usual doc sense; add a `Dockerfile` locally when needed. **`diffoctreerast`** Not listed in `requirements.txt`; `trellis/renderers/octree_renderer.py` degrades with a warning if it is missing. Add a separate install only if you need that renderer on the Space. ## 6. If the build fails - **PyTorch / CUDA mismatch**: this repo pins **CUDA 12.8** (`cu128`) and **torch 2.8.0** in `requirements.txt`. If the Space image’s driver is older, switch hardware or relax the pins. - **`nvdiffrast` import/runtime errors**: the third-party wheel may not match **torch 2.8/cu128** — build a matching wheel or use a **Docker** Space with `nvidia/cuda:*-devel` and source install. - **`./vox2seq` in `requirements.txt`**: Hugging Face’s Gradio image runs `pip install -r /tmp/requirements.txt` **before** the repo is copied, so **path dependencies are invalid** (`Expected package name…`, file not found). Do **not** list `./vox2seq`; this repo uses a **vendored pure-PyTorch** fallback (`trellis/.../vox2seq_pytorch/`) in `serialized_attn.py`. Optionally `pip install ./vox2seq` **locally** if you have `nvcc` and want the CUDA extension. - **`spconv` / `xformers`**: versions must match the installed PyTorch + CUDA. - **Disk / first boot**: the NeAR checkpoint snapshot is large; cold start may time out—increase hardware or pre-cache in a custom image. - **DINOv2 mirror not found**: confirm `NEAR_DINO_LOCAL_REPO` or `NEAR_AUX_REPO` points to a directory/repo containing `hubconf.py` (default subdir `dinov2/`). The runtime now expects a local or HF-cached mirror rather than direct GitHub `torch.hub` access. ## 7. Local check before push ```bash export NEAR_PRETRAINED=luh0502/NeAR # or a local folder with pipeline.yaml python app.py --host 0.0.0.0 --port 7860 ```