Selaa lähdekoodia

feat(recipe): add GLM-5.2 Int4-Int8Mix recipe with overlay image

Add recipe for QuantTrio/GLM-5.2-Int4-Int8Mix on 4-node DGX Spark
cluster, reusing sparkrun-vllm-ds4-gb10:fixed as the base image.

Create docker-images/glm52-int4-int8/ with:
- Dockerfile: overlay wrapper that unsets VLLM_PREFIX_CACHE_RETENTION_INTERVAL
  (baked at 4096 in base image, breaks GLM-5.2) before calling the
  original /opt/nvidia/nvidia_entrypoint.sh, preserving conda/CUDA paths
- build.sh: auto-build via launcher --build-node

Fix dry-run for build subcommand: handle_build now checks opts.dry_run
at entry and prints intent without SSH connections.
Alexander Korolev 3 kuukautta sitten
vanhempi
sitoutus
b85d797be1

+ 18 - 0
docker-images/glm52-int4-int8/Dockerfile

@@ -0,0 +1,18 @@
+# GLM-5.2 Int4-Int8Mix overlay on sparkrun-vllm-ds4-gb10:fixed
+#
+# Unsets VLLM_PREFIX_CACHE_RETENTION_INTERVAL (baked at 4096 in the base
+# deepseek image, breaks GLM-5.2) via a wrapper that sources the original
+# nvidia_entrypoint.sh first, then hands off with exec after the unset.
+#
+# Build:
+#   cd docker-images/glm52-int4-int8 && bash build.sh
+#   Tags: sparkrun-vllm-ds4-gb10:glm52
+
+FROM sparkrun-vllm-ds4-gb10:fixed
+
+# Wrapper: unset the baked var, then hand off to the original entrypoint.
+# The original nvidia_entrypoint.sh does `exec "$@"` at the end, so this
+# runs as PID 1 with conda/CUDA paths sourced and the baked var gone.
+RUN printf '#!/bin/bash\nunset VLLM_PREFIX_CACHE_RETENTION_INTERVAL\nexec /opt/nvidia/nvidia_entrypoint.sh "$@"\n' > /entrypoint-wrapper.sh && chmod +x /entrypoint-wrapper.sh
+
+ENTRYPOINT ["/entrypoint-wrapper.sh"]

+ 9 - 0
docker-images/glm52-int4-int8/build.sh

@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Build GLM-5.2 Int4-Int8Mix overlay image.
+# Requires sparkrun-vllm-ds4-gb10:fixed (built from deepseek-v4-flash-dspark overlay).
+set -euo pipefail
+cd "$(dirname "$0")"
+TAG="${TAG:-sparkrun-vllm-ds4-gb10:glm52}"
+echo "=== Building ${TAG} ==="
+docker build --progress=plain -t "${TAG}" . 2>&1
+echo "=== Done: ${TAG} ==="

+ 66 - 77
recipes/glm52-int4-int8/recipe.toml

@@ -1,41 +1,26 @@
 # GLM-5.2 QuantTrio Int4-Int8Mix — 4× DGX Spark / GB10 (sm_121), TP=4.
-# MTP k=4, cudagraph FULL, prefix caching, fp8_ds_mla KV, 350k ctx.
+#
+# Image: sparkrun-vllm-ds4-gb10:glm52
+#   Built from docker-images/glm52-int4-int8/Dockerfile, which overlays
+#   sparkrun-vllm-ds4-gb10:fixed with an entrypoint wrapper that unsets
+#   the baked VLLM_PREFIX_CACHE_RETENTION_INTERVAL before calling the
+#   original /opt/nvidia/nvidia_entrypoint.sh.
 #
 # Model: QuantTrio/GLM-5.2-Int4-Int8Mix (mixed Int4/Int8, 10% expert-pruned)
 #   → https://huggingface.co/QuantTrio/GLM-5.2-Int4-Int8Mix
-# MTP draft: CosmicRaisins/GLM-5.2-MTP-INT4-aligned
-#   → https://huggingface.co/CosmicRaisins/GLM-5.2-MTP-INT4-aligned
-#
-# BUILD THE IMAGE FIRST:
-#   Reference: https://github.com/CosmicRaisins/glm-5.2-gb10
-#   The custom image vllm-node-tf5-glm52-b12x:probe is built from eugr/spark-vllm-docker
-#   fork at pinned ref ab666069935c1f23e8ef56038b4659ac9e8f19f8, then applies mods
-#   glm52-sm12x-sparse and glm52-b12x-sparse on top. See the repo's bootstrap.sh
-#   for the full build-and-copy workflow.
-#
-#   1) git clone https://github.com/eugr/spark-vllm-docker
-#   2) cd spark-vllm-docker && ./build-and-copy.sh \
-#        --vllm-ref ab666069935c1f23e8ef56038b4659ac9e8f19f8 \
-#        -t vllm-node-tf5-glm52-b12x:probe --tf5
-#   3) Bake mods glm52-sm12x-sparse + glm52-b12x-sparse into the image
-#      (see Dockerfile.glm52-consolidated in the repo)
+#   Includes in-checkpoint MTP draft model.
 #
 # PREREQUISITES:
-#   - NCCL 2.30.4 staged on every node at /root/.cache/huggingface/hub/nccl-2.30.4/libnccl.so.2
-#     (the image LD_PRELOADs it). Get it with:
-#       pip install --no-deps -d /tmp/ncclwheel nvidia-nccl-cu13==2.30.4 && mkdir -p ... && unzip ...
 #   - Weights cached on every node:
 #       huggingface-cli download QuantTrio/GLM-5.2-Int4-Int8Mix
-#       huggingface-cli download CosmicRaisins/GLM-5.2-MTP-INT4-aligned
 #
-# TUNING NOTES (from community):
-#   - gpu-memory-utilization 0.92 is the sweet spot for 350k ctx on 4× GB10 with MTP
-#   - MTP k=4 is more reliable on startup than k=5 on GB10
-#   - cudagraph FULL requires b12x installed; without it, use PIECEWISE
+# REFERENCE:
+#   https://forums.developer.nvidia.com/t/glm-5-2-int4-int8-on-8x-gb10-1200-t-s-prefill-33-54-t-s-avg-decode-generic-coding-structured/376831
+#   https://github.com/ciprianveg/gb10-glm-5.2
 
 engine = "vllm"
-name = "GLM-5.2 Int4-Int8Mix (vLLM)"
-docker_image = "vllm-node-tf5-glm52-b12x:probe"
+name = "GLM-5.2 Int4-Int8Mix (vLLM, DS4 image)"
+docker_image = "sparkrun-vllm-ds4-gb10:glm52"
 model = "QuantTrio/GLM-5.2-Int4-Int8Mix"
 port = 8210
 required_nodes = 4
@@ -43,81 +28,85 @@ tensor_parallel_size = 4
 pipeline_parallel_size = 1
 distributed_backend = "mp"
 shm_size = "64gb"
+docker_image_dir = "glm52-int4-int8"
 
 entrypoint = ["vllm", "serve"]
 
 args = [
-    # Reasoning & tools
-    "--reasoning-parser", "glm45",
-    "--tool-call-parser", "glm47",
-    "--enable-auto-tool-choice",
     # KV cache & memory
-    "--kv-cache-dtype", "fp8_ds_mla",
-    "--max-model-len", "262144",
+    "--kv-cache-dtype", "fp8",
+    "--max-model-len", "150000",
     "--max-num-seqs", "1",
     "--max-num-batched-tokens", "4096",
-    "--gpu-memory-utilization", "0.92",
-    # Compilation (b12x required for FULL cudagraph)
+    "--gpu-memory-utilization", "0.91",
+    # Engine backends
+    "--attention-backend", "B12X_MLA_SPARSE",
+    "--moe-backend", "b12x",
+    # Compilation
     "--compilation-config", """
-'{"cudagraph_mode":"FULL"}'""",
-    # MTP speculative decoding
-    "--speculative-config", """
-'{"model":"/root/.cache/huggingface/hub/glm52-mtp-int4-aligned","method":"mtp","num_speculative_tokens":4,"attention_backend":"FLASHMLA_SPARSE"}'""",
-    # Other
-    "--trust-remote-code",
+'{"cudagraph_mode":"FULL_AND_PIECEWISE","custom_ops":["all"]}'""",
+    # Chunked prefill + prefix caching
+    "--enable-chunked-prefill",
     "--enable-prefix-caching",
     "--enable-flashinfer-autotune",
+    # MTP speculative decoding (in-checkpoint draft model)
+#    "--speculative-config", """
+#'{"method":"mtp","num_speculative_tokens":4,"moe_backend":"b12x","draft_sample_method":"probabilistic"}'""",
+    # Tokenizer / reasoning (GLM-5.2 family)
+    "--tool-call-parser", "glm47",
+    "--reasoning-parser", "glm45",
+    "--enable-auto-tool-choice",
+    "--trust-remote-code",
     "--served-model-name", "glm52",
 ]
 
 [env]
+# b12x suite (image requires this)
+VLLM_USE_B12X_MOE = "1"
+VLLM_USE_B12X_MHC = "1"
+VLLM_USE_B12X_WO_PROJECTION = "1"
+VLLM_USE_B12X_FP8_GEMM = "1"
+VLLM_USE_B12X_SPARSE_INDEXER = "1"
+VLLM_USE_V2_MODEL_RUNNER = "1"
+VLLM_USE_FLASHINFER_SAMPLER = "1"
+VLLM_USE_AOT_COMPILE = "1"
+VLLM_USE_MEGA_AOT_ARTIFACT = "0"
+VLLM_USE_BREAKABLE_CUDAGRAPH = "0"
+VLLM_MEMORY_PROFILE_INCLUDE_ATTN = "1"
+B12X_MLA_SM120_UNIFIED = "1"
+B12X_DENSE_SPLITK_TURBO = "1"
+B12X_W4A16_TC_DECODE = "1"
+B12X_MHC_MAX_TOKENS = "16384"
+
+# GLM-5.2 support flags
+VLLM_ALLOW_LONG_MAX_MODEL_LEN = "1"
+VLLM_SPARSE_INDEXER_MAX_LOGITS_MB = "256"
+
 # Timeout safety net for cold boot (cudagraph + JIT)
 VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS = "1800"
-# NCCL shm_broadcast fix
-LD_PRELOAD = "/root/.cache/huggingface/hub/nccl-2.30.4/libnccl.so.2"
 
 # Offline mode — weights must be cached on each node
-HF_HOME = "/root/.cache/huggingface"
-TRITON_CACHE_DIR = "/root/.cache/huggingface/.tritoncache"
 HF_HUB_OFFLINE = "1"
-TRANSFORMERS_OFFLINE = "1"
 
-# GLM-5.2 sparse-MLA suite (these guard the Triton fallback paths on sm_121)
-VLLM_ALLOW_LONG_MAX_MODEL_LEN = "1"
-VLLM_SPARSE_INDEXER_MAX_LOGITS_MB = "256"
-GLM52_BIND_HOST_TRITON = "1"
-GLM52_MQA_LOGITS_TRITON = "1"
-GLM52_PAGED_MQA_TRITON = "1"
-GLM52_PAGED_MQA_TOPK_CHUNK_SIZE = "8192"
-GLM52_B12X_MLA = "1"
+# Architecture
 TORCH_CUDA_ARCH_LIST = "12.1a"
+CUTE_DSL_ARCH = "sm_121a"
+OMP_NUM_THREADS = "4"
 
-# NCCL / RoCE (launcher auto-sets most; recipe env wins last)
-NCCL_NET = "IB"
-NCCL_IB_DISABLE = "0"
-NCCL_IB_GID_INDEX = "3"
+# GPU memory management
+PYTORCH_CUDA_ALLOC_CONF = "expandable_segments:True"
+
+# NCCL overrides
 NCCL_CROSS_NIC = "1"
 NCCL_CUMEM_ENABLE = "0"
 NCCL_IGNORE_CPU_AFFINITY = "1"
 NCCL_DEBUG = "WARN"
 
-# MoE & compute
-OMP_NUM_THREADS = "8"
+# PCIe allreduce not needed on GB10 single-NIC
+VLLM_ENABLE_PCIE_ALLREDUCE = "0"
+VLLM_PCIE_ALLREDUCE_BACKEND = "cpp"
 
-[mods]
-commands = [
-    # Copy Triton sparse-MLA kernel files into the vLLM tree inside the container.
-    # These are mounted via the mods system at /mods/glm52-sm12x-sparse/ and copied
-    # into the vLLM Python package paths before the engine starts.
-    "cp /mods/glm52-sm12x-sparse/sparse_mla_kernels.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/backends/mla/",
-    "cp /mods/glm52-sm12x-sparse/sparse_mla_env.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/backends/mla/",
-    "cp /mods/glm52-sm12x-sparse/sm12x_sparse_mla_attn.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/backends/mla/",
-    "cp /mods/glm52-sm12x-sparse/patch_flashmla_ops.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/backends/mla/",
-    "cp /mods/glm52-sm12x-sparse/flashmla_sparse.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/backends/mla/",
-    "cp /mods/glm52-sm12x-sparse/sm12x_deep_gemm_fallbacks.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/ops/deepseek_v4_ops/",
-    "cp /mods/glm52-sm12x-sparse/sm12x_mqa.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/ops/deepseek_v4_ops/",
-    "cp /mods/glm52-sm12x-sparse/b12x_sparse_helpers.py /usr/local/lib/python3.12/dist-packages/vllm/v1/attention/ops/deepseek_v4_ops/",
-    # upstream vLLM #46862: fused indexer Q rope+fp8-quant
-    "cp /mods/glm52-sm12x-sparse/sparse_attn_indexer.py /usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/",
-    "cp /mods/glm52-sm12x-sparse/deepseek_v2.py /usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/",
-]
+# GPU device isolation
+CUDA_VISIBLE_DEVICES = "0"
+CUDA_DEVICE_ORDER = "PCI_BUS_ID"
+CUDA_CACHE_MAXSIZE = "4294967296"

+ 9 - 0
src/docker/build.rs

@@ -136,6 +136,15 @@ pub async fn handle_build(
     opts: &ServeOpts,
     build_node_name: &str,
 ) -> Result<()> {
+    if opts.dry_run {
+        println!(
+            "[dry-run] would build '{}' on '{}' from docker-images/{}/build.sh",
+            recipe.docker_image,
+            build_node_name,
+            recipe.docker_image_dir.as_deref().unwrap_or(recipe_name)
+        );
+        return Ok(());
+    }
     let image_dir = recipe
         .docker_image_dir
         .clone()