Browse Source

dsv41-flash-1017-2x-tensorfold: ship it as an sm121 recipe

recipe.toml (engine = tensorfold, 2nodes-dac, 262144 ctx, upstream tuned knobs)
+ docker-images/dsv41-tf-1017 (fetch-sources at pins outside the image dir, their
Dockerfile, /opt/sm121-helpers baked in) + tensorfold_1017_prepare before_serve.
Verified end-to-end: serve -> API in ~4 min after the image sync, probe decode
49.4 tok/s, stop closes the runlock.

NOTE.md also records the TensorFold+vLLM profile attempt and its exact blocker
(NFS engram volume only contains NAS symlinks that dangle in the worker's vLLM
container; rsync mode wants 196 GiB free on the worker).
Alexander Korolev 4 ngày trước cách đây
mục cha
commit
ec17b00796

+ 24 - 0
docker-images/dsv41-tf-1017/Dockerfile

@@ -0,0 +1,24 @@
+# syntax=docker/dockerfile:1.7
+# TensorFold 2x DeepSeek-V4.1-Flash-1017 image.
+#
+# Upstream: x00byte/DeepSeek-V4.1-Flash-1017-Dual-Spark (forum 386121) —
+# TensorFold 0.6.0 with Jay Leaton's DeepSeek-family patches and the repo's
+# tensorfold-1017.patch, on NVIDIA's 26.07 PyTorch container (the stack asserts
+# triton 3.7.1 + torch nv26.07). The sources are fetched at pins by
+# fetch-sources.sh (outside this directory) and copied in as `upstream/build/tensorfold`.
+FROM nvcr.io/nvidia/pytorch:26.07-py3
+ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 HF_HUB_OFFLINE=1 TENSORFOLD_NO_UPDATE_CHECK=1
+RUN test -f /usr/include/infiniband/verbs.h || (apt-get update && apt-get install -y --no-install-recommends libibverbs-dev ibverbs-providers && rm -rf /var/lib/apt/lists/*)
+RUN pip install --no-cache-dir --no-deps xgrammar==0.2.8 && pip install --no-cache-dir transformers==5.18.0
+COPY upstream/build/tensorfold /opt/tensorfold
+# Host-side helper scripts the launcher's prepare hook pulls back out of the
+# image (engram packer, CUDA prebuild, chat template) — keeps the lane to ONE
+# artifact: the image.
+COPY upstream/build/jayleaton/scripts/pack_engram.py \
+     upstream/build/jayleaton/scripts/prebuild_ext.py \
+     upstream/scripts/prebuild-offline.py \
+     upstream/build/jayleaton/config/pfdense-table.json \
+     upstream/build/mia-runtime/files/chat_template.jinja \
+     /opt/sm121-helpers/
+RUN pip install --no-deps --no-build-isolation /opt/tensorfold && python -c "import torch,triton,tensorfold.families.deepseek_v41; assert triton.__version__ == '3.7.1'; assert 'nv26.07' in torch.__version__; print(torch.__version__)"
+ENTRYPOINT ["python", "-m", "tensorfold"]

+ 52 - 0
docker-images/dsv41-tf-1017/build.sh

@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+# Build the TensorFold 2x 1017 image (upstream sources at pins + their Dockerfile).
+#
+# Uses the classic docker builder (not buildx docker-container): the base
+# image dsv41-flash:local is local-only (never pushed to a registry), and
+# the container-driver builder cannot resolve local FROM references.
+#
+# Generic driver: forwards every KEY=VAL pin in versions.env as a
+# --build-arg to the Dockerfile (no hardcoded variable list). The sm121
+# launcher writes a merged versions.env here when a recipe carries
+# [build.versions] overrides (recipe wins over the image defaults).
+set -euo pipefail
+cd "$(dirname "$0")"
+TAG="${TAG:-dsv41-tf-1017:v1}"
+SRC="${SRC:-/tmp/dsv41-tf-1017-src}"
+
+# Sources are fetched OUTSIDE this directory (keeps the image-dir checksum stable).
+bash ./fetch-sources.sh "$SRC"
+cp Dockerfile "$SRC/Dockerfile"
+
+ARGS=()
+while IFS= read -r line; do
+    # Skip blank lines and comments (# ...).
+    case "$line" in
+        ''|\#*) continue ;;
+    esac
+    key="${line%%=*}"
+    val="${line#*=}"
+    [ -n "$key" ] && ARGS+=(--build-arg "${key}=${val}")
+done < versions.env
+
+echo "=== Building ${TAG} ==="
+docker build "${ARGS[@]}" --progress=plain -t "${TAG}" -f "$SRC/Dockerfile" "$SRC" 2>&1
+echo "=== Done: ${TAG} ==="
+
+# Publish the host-side helper files next to the model cache so the launch
+# nodes can stage (chat template, Engram packer, CUDA prebuild) WITHOUT needing
+# the image first — serve runs before_serve hooks before it syncs the image.
+PUB="${SM121_TF1017_PUBDIR:-/mnt/nas/ai/sm121/dsv41-tf-1017}"
+if mkdir -p "$PUB" 2>/dev/null; then
+    for f in pack_engram.py prebuild_ext.py prebuild-offline.py pfdense-table.json chat_template.jinja; do
+        cp -f "$SRC/upstream/$(case $f in
+            pack_engram.py) echo build/jayleaton/scripts/pack_engram.py ;;
+            prebuild_ext.py) echo build/jayleaton/scripts/prebuild_ext.py ;;
+            prebuild-offline.py) echo scripts/prebuild-offline.py ;;
+            pfdense-table.json) echo build/jayleaton/config/pfdense-table.json ;;
+            chat_template.jinja) echo build/mia-runtime/files/chat_template.jinja ;;
+        esac)" "$PUB/$f"
+    done
+    echo "$TAG" > "$PUB/IMAGE_TAG"
+    echo "=== helpers published to $PUB ==="
+fi

+ 35 - 0
docker-images/dsv41-tf-1017/fetch-sources.sh

@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# Fetch the TensorFold 2x 1017 build sources at their pins OUTSIDE the image
+# directory, so the image dir stays tiny and its build checksum stays stable.
+#
+# Pins (upstream `x00byte/DeepSeek-V4.1-Flash-1017-Dual-Spark` @ TF1017_REPO_REV,
+# whose own scripts/fetch-sources.sh pins everything else):
+#   MiaAI-Lab/DeepSeek-v4.1-Flash-EXL3-2x-DGX-Sparks  6f7d1590...
+#   jayleaton/deepseek-v41-tensorfold-spark           2dcce030...
+#     └─ vendor/TensorFold                            c4646171...
+#
+# Usage: fetch-sources.sh [target-dir]   (default /tmp/dsv41-tf-1017-src)
+set -euo pipefail
+
+TARGET="${1:-/tmp/dsv41-tf-1017-src}"
+REPO=https://github.com/x00byte/DeepSeek-V4.1-Flash-1017-Dual-Spark
+REV="${TF1017_REPO_REV:-2df9e96a886d778c3fd5ef9d3c272c3c9972d89d}"
+
+if [[ ! -d "$TARGET/upstream/.git" ]]; then
+    mkdir -p "$TARGET"
+    git clone "$REPO" "$TARGET/upstream"
+fi
+git -C "$TARGET/upstream" fetch --depth 1 origin "$REV"
+git -C "$TARGET/upstream" checkout --detach "$REV"
+
+# Their fetcher clones the Mia runtime + Jay Leaton's fork (+ the TensorFold
+# submodule) at pins and applies the 1017 patches to build/tensorfold. It
+# refuses to run twice (build/mia-runtime already exists), so a repeat build
+# reuses the prepared tree.
+if [[ -d "$TARGET/upstream/build/tensorfold" && -d "$TARGET/upstream/build/mia-runtime" ]]; then
+    echo "[fetch-sources] already prepared in $TARGET/upstream/build"
+else
+    bash "$TARGET/upstream/scripts/fetch-sources.sh"
+fi
+
+echo "[fetch-sources] sources ready in $TARGET/upstream/build (tensorfold, mia-runtime)"

+ 6 - 0
docker-images/dsv41-tf-1017/versions.env

@@ -0,0 +1,6 @@
+# versions.env - build input for the TensorFold 2x 1017 image.
+# The upstream sources are pinned by fetch-sources.sh (repo + the pins inside
+# its own scripts/fetch-sources.sh); this file carries the base image and the
+# upstream repo revision so a bump is a one-line change.
+BASE_IMAGE=nvcr.io/nvidia/pytorch:26.07-py3
+UPSTREAM_REV=2df9e96a886d778c3fd5ef9d3c272c3c9972d89d

+ 47 - 4
recipes/dsv41-flash-1017-2x-tensorfold/NOTE.md

@@ -1,8 +1,11 @@
 # DeepSeek-V4.1-Flash-1017 — TensorFold 2x (dual Spark) experimental lane
 
-Status: **measured 2026-10-19, not yet an sm121 recipe** (no `recipe.toml`; the
-lane was brought up with the upstream launcher — see below). Reports live here so
-the numbers sit next to the other 1017 lanes.
+Status: **now an sm121 recipe** — `recipe.toml` + `docker-images/dsv41-tf-1017/`
+(upstream sources at pins + their Dockerfile) + the `tensorfold_1017_prepare`
+built-in host script. `sm121 serve --recipe dsv41-flash-1017-2x-tensorfold
+--cluster 2nodes-dac` brings the lane up end-to-end: pre-serve staging, image
+build/sync, per-node containers, runlock tracking; `sm121 stop` closes the run.
+Reports live here so the numbers sit next to the other 1017 lanes.
 
 Stack (upstream `x00byte/DeepSeek-V4.1-Flash-1017-Dual-Spark`, forum thread
 386121): TensorFold 0.6.0 (Jay Leaton's DeepSeek-family patches + the repo's
@@ -64,4 +67,44 @@ effort 100.
    `cluster.py args` directly after verifying the checkpoint file-for-file
    against the Hub API (66/66 files, sizes match).
 
-Node cost on dgx1/dgx2: ~305 GB per node (210 GB weights + 94 GB engram + image).
+Node cost on dgx1/dgx2: ~305 GB per node (210 GB weights + 94 GB engram + image).
+
+## sm121 integration (2026-10-19)
+
+- `engine = "tensorfold"` → `TensorFoldDriver` (`--tp/--rank/--master/
+  --master-port`, no headless, configurable port).
+- `docker-images/dsv41-tf-1017/`: `fetch-sources.sh` clones the upstream repo at
+  `2df9e96a` OUTSIDE the image dir (so the build checksum stays stable) and runs
+  its own fetcher (Mia @6f7d1590, jayleaton @2dcce030, vendor/TensorFold
+  @c4646171 + the 1017 patches); the image also bakes `/opt/sm121-helpers`.
+- `before_serve = [{ script = "tensorfold_1017_prepare" }]`: node-local weights,
+  chat template, per-rank Engram packs (host python + numpy), CUDA prebuild.
+  Pre-serve hooks run *before* the image build/sync, so the helper files are
+  published to `/mnt/nas/ai/sm121/dsv41-tf-1017/` by the image `build.sh` and
+  the image-dependent prebuild is skipped (and deferred) on a first serve.
+- Verified: serve → API ready in ~4 min after a 24.5 GB image sync to the peer;
+  probe decode 49.4 tok/s (matches the manual 50.4); `stop` closed the runlock.
+
+## The TensorFold+vLLM profile — attempted, blocked (2026-10-19)
+
+The upstream repo also ships a vLLM profile on the same weights
+(`ghcr.io/miaai-lab/deepseek-v4.1-flash-exl3-2x-dgx-sparks@sha256:2f0cf3ad…`,
+pulled fine, `overlay verify OK`) driven by their own `build/mia-runtime/start.sh`.
+It gets as far as the NFS share and stops there for us:
+
+- `WEIGHT_SYNC=nfs` (their default): the weight volume works (the worker sees
+  `config.json` through `nfs://192.168.100.14:/dsv41-exl3`), but the **Engram
+  volume check fails** — our 1017 engram source lives in the NAS hub cache, so
+  the "slim Engram" export only ever contains *symlinks* to `/mnt/nas/...`,
+  which are dangling inside the worker's vLLM container (it does not mount the
+  NAS). 189 GiB of real shards do not fit on the head (98 GiB free).
+- `WEIGHT_SYNC=rsync`: refuses because it requires ~196 GiB *free* on the worker
+  for a copy, even though both nodes already hold an identical local copy.
+
+Next steps for that profile (in order of cheapness): (a) patch the worker's
+docker run in `mia-runtime` to bind-mount `/mnt/nas` read-only (one line, and
+exactly the kind of patch `patches/*.patch` already carries), or (b) teach the
+NFS share to export the engram volume from a node with 190 GiB to spare
+(dgx3/dgx4). The packed per-rank Engram bins (94.4 GiB) are accepted by both
+runtimes, so a `HEAD_PACKED_DIR/WORKER_PACKED_DIR` wiring plus a real-file
+probe is the likely minimal fix.

+ 157 - 0
recipes/dsv41-flash-1017-2x-tensorfold/recipe.toml

@@ -0,0 +1,157 @@
+# DeepSeek-V4.1-Flash-1017 on a dual-Spark kit — upstream TensorFold 0.6.0
+# (x00byte/DeepSeek-V4.1-Flash-1017-Dual-Spark, forum 386121) with EXL3 2.9bpw
+# weights. Measured 2026-10-19: 89/100 tool-eval quality, decode 50.4 tok/s c1
+# (vs 38.8-43.0 for the 4x SGLang fp8 lane), but ~7x slower prefill -> 6.7 s
+# median agent turn (responsiveness 23 vs 75). The shape for long generation /
+# batch work, not for interactive agents. Details:
+# specs/tech-architecture/dsv41-1017-tuning.md.
+#
+# Image: docker-images/dsv41-tf-1017 (nvcr.io/nvidia/pytorch:26.07-py3 +
+# TensorFold at pins + Jay Leaton's DeepSeek patches + tensorfold-1017.patch).
+# It also carries /opt/sm121-helpers (engram packer, CUDA prebuild, chat
+# template), so the lane is one artifact: the image.
+#
+# Host staging runs in before_serve (`tensorfold_1017_prepare`, idempotent):
+# node-local EXL3 weights from the NAS, the per-rank Engram packs (~94 GiB a
+# node), the chat template, and the CPU-only CUDA prebuild. First serve on a
+# node costs ~40 min of packing plus the 210 GB weight copy; later serves are
+# verification-only.
+
+engine = "tensorfold"
+name = "DeepSeek-V4.1-Flash-1017 (TensorFold 2x, EXL3 2.9bpw)"
+docker_image = "dsv41-tf-1017:v1"
+docker_image_dir = "dsv41-tf-1017"
+model = "/model"
+parent_model = "deepseek-ai/DeepSeek-V4.1-Flash"
+port = 30000
+required_nodes = 2
+cluster = "2nodes-dac"
+tensor_parallel_size = 2
+distributed_backend = "mp"
+shm_size = "32gb"
+
+# `python -m tensorfold serve /model` — the launcher appends the positional
+# model path, the rendezvous flags (--tp/--rank/--master/--master-port) and
+# --host/--port on the head (rank 1 accepts and ignores them).
+entrypoint = ["python", "-m", "tensorfold", "serve"]
+
+args = [
+    "--no-update-check",
+    "--context", "262144",
+    "--parallel", "8",
+    "--kv-dtype", "fp8",
+    "--max-tokens", "32768",
+    "--name", "deepseek-v4.1-flash",
+]
+
+before_serve = [{ script = "tensorfold_1017_prepare" }]
+
+[[extra_mounts]]
+host = "/home/kilork/ns/1017-tf/models/checkpoint"
+container = "/model"
+read_only = true
+
+[[extra_mounts]]
+host = "/home/kilork/ns/1017-tf/engram-packed-1017"
+container = "/engram"
+read_only = true
+
+[[extra_mounts]]
+host = "/home/kilork/ns/1017-tf/state"
+container = "/state"
+
+[[extra_mounts]]
+host = "/home/kilork/ns/1017-tf/cache"
+container = "/cache"
+
+[env]
+# Upstream tuned profile (config/tensorfold-knobs.env.example @ the pinned
+# rev). TF_DSV41_BIAS_VL and TF_DSV41_ENGRAM_DIR are the two the engine's own
+# preflight requires to match the mounts above.
+GLM53_TF_COMM_BACKEND = "roce"
+GLM53_TF_REQUEST_LOG = "/state/requests.jsonl"
+GLM53_TF_ROCE_FAST = "1"
+GLM53_TF_ROCE_MAX_KB = "1024"
+MALLOC_ARENA_MAX = "4"
+MIMALLOC_ALLOW_THP = "0"
+TF_DSV41_ATTN_CUDA = "1"
+TF_DSV41_BIAS_VL = "/model"
+TF_DSV41_BRANCHES = "1"
+TF_DSV41_BRANCHES_PRIO = "side"
+TF_DSV41_CALIB = "real"
+TF_DSV41_CALIB_DIR = "/cache/dsv41-calib"
+TF_DSV41_DEFAULT_EFFORT = "high"
+TF_DSV41_DENSE = "0"
+TF_DSV41_DENSE_V3 = "1"
+TF_DSV41_DEPTH_JOINT = "0"
+TF_DSV41_DISCONNECT = "1"
+TF_DSV41_DRAFT_DEPTH = "3"
+TF_DSV41_DRAFT_GRAPH_SLOTS = "8"
+TF_DSV41_DSPARK = "1"
+TF_DSV41_ENGRAM_DIR = "/engram"
+TF_DSV41_ENGRAM_FUSE_DEC = "1"
+TF_DSV41_ENGRAM_GATE = "1"
+TF_DSV41_ENGRAM_NATIVE = "1"
+TF_DSV41_EXPERT_BLOCK = "1536"
+TF_DSV41_EXPERT_LOADS = "1"
+TF_DSV41_EXPERT_MIN_K = "3"
+TF_DSV41_EXPERT_PREFILL = "0"
+TF_DSV41_EXPERT_RENORM = "orig"
+TF_DSV41_EXPERT_TOPP = "1.0"
+TF_DSV41_FAST_EXPERTS = "gm"
+TF_DSV41_FLOOR_GIB = "5"
+TF_DSV41_FLOOR_HARD_GIB = "4"
+TF_DSV41_FULL_CONE = "1"
+TF_DSV41_GRAMMAR = "1"
+TF_DSV41_GRAPH_MODE = "rows"
+TF_DSV41_GRAPH_ROWS_MAX = "64"
+TF_DSV41_GRAPH_WARM = "all"
+TF_DSV41_IMAGES = "native"
+TF_DSV41_INDEX_BUDGET_MIB = "64"
+TF_DSV41_INDEX_DTOPK = "1"
+TF_DSV41_INDEX_KV = "fp8"
+TF_DSV41_INDEX_STREAM_MIN = "4096"
+TF_DSV41_L2PF = "1"
+TF_DSV41_L2PF_MB = "12"
+TF_DSV41_L2PF_PACE_GBPS = "150"
+TF_DSV41_MHC_BM = "64"
+TF_DSV41_MHC_CUDA = "1"
+TF_DSV41_MHC_DEC = "1"
+TF_DSV41_MHC_FIN2 = "1"
+TF_DSV41_MHC_FN = "bf16"
+TF_DSV41_MHC_PF = "1"
+TF_DSV41_MHC_WARPS = "8"
+TF_DSV41_PARTIAL_BF16 = "1"
+TF_DSV41_PDL = "0"
+TF_DSV41_PF_COPIES = "1"
+TF_DSV41_PF_DENSE = "fused"
+TF_DSV41_PF_DENSE_TABLE = "/cache/pfdense-table.json"
+TF_DSV41_PLAIN_TRITON = "1"
+TF_DSV41_PLAN_LINK = "rdma"
+TF_DSV41_PLAN_PIN = "auto"
+TF_DSV41_POOL_TOKENS = "1048576"
+TF_DSV41_PREFETCH_AHEAD = "1"
+TF_DSV41_PREFILL = "full"
+TF_DSV41_PREFILL_ADAPT_GIB = "4.5"
+TF_DSV41_PREFILL_ATTN = "fused"
+TF_DSV41_PREFILL_ATTN_BMQ = "32"
+TF_DSV41_PREFILL_ATTN_WARPS = "8"
+TF_DSV41_PREFILL_CHUNK = "1536"
+TF_DSV41_PREFILL_GATHER = "bf16"
+TF_DSV41_PREFILL_KERNELS = "exact"
+TF_DSV41_PREFILL_ROUTER = "gemv"
+TF_DSV41_PREFILL_ROWS = "1536"
+TF_DSV41_PREPARED = "0"
+TF_DSV41_PREPARED_WRITE = "0"
+TF_DSV41_RG_NARROW = "2"
+TF_DSV41_RG_TAIL = "1"
+TF_DSV41_RMS_PAIR = "1"
+TF_DSV41_ROPE_INPLACE = "1"
+TF_DSV41_ROUTER = "gemv"
+TF_DSV41_SCHEMA_PROMPT = "1"
+TF_DSV41_SESSIONS = "0"
+TF_DSV41_SESSION_RAM_MIB = "256"
+TF_DSV41_SPEC_DRAFT = "1"
+TF_DSV41_THINKING = "1"
+TF_DSV41_TOOL_GRAMMAR = "0"
+TF_DSV41_VISION_PREP_MB = "64"