NeAR / DEPLOY_HF_SPACE.md
luh1124's picture
docs: clarify Space app_file vs dashboard; fix README structure bullets
1ec3c4e

A newer version of the Gradio SDK is available: 6.12.0

Upgrade

Deploy NeAR to Hugging Face Space

Target Space: 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

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 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 (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 → SettingsHardware.
  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 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: 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, 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@<tag>). 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:
    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/<user>/<name>), 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 or Git Xet.

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:

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):

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):

    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):

    rm -rf .git
    
  4. Re-init with LFS before the first git add so every PNG/NPZ/… is stored as LFS from the start:

    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):

    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):

    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:

    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

ABI / version caveat

  • Third-party wheels (e.g. from JeffreyXiang/Storages) 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:
      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:

pip install -U "huggingface_hub[cli]"
hf auth login
hf upload-large-folder luh0502/near-assets ./near-assets-local

Suggested layout:

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

export NEAR_PRETRAINED=luh0502/NeAR   # or a local folder with pipeline.yaml
python app.py --host 0.0.0.0 --port 7860