DS4-F Flash-0731 on a Dual DGX Spark Pair (Recipe)
Official deepseek-ai/DeepSeek-V4-Flash-0731 is the GA Flash weight drop (same structure as DSpark, speculative module attached). Mia (@MiaAI_lab) flagged the HF release the same day it landed — that heads-up is why this pair moved quickly. This Funland recipe runs those weights on two NVIDIA DGX Sparks (GB10) using the dual-Spark Anemll path Mia / MiaAI-Lab and Anemll made practical for GB10 (our 1M/6 writeup): download once, rsync once, RoCE TP=2, Hermes-ready OpenAI API on :8888.
Keys short-gen is a few percent slower than healthy DSpark preview weights. We still switched: agent/tool quality on the official weights is the higher priority for daily Hermes work. DSpark remains the one-command rollback.
RESTORE_0731_OK, tools, Hermes ROUTE_OK, KV 2,214,807). Do not treat Keys C1 as IQ; do not jump to official K=7 — checkpoint dspark_block_size=5.
What you get
- OpenAI-compatible API on the head node:
http://<spark1>:8888/v1 - Served name:
deepseek-v4-flash-0731 - Server context: 1M tokens (Hermes can still advertise a lower client cap, e.g. 200k)
- TP=2 across two Sparks over RoCE / InfiniBand HCAs
- DSpark speculative decode K=5 (probabilistic) via Anemll stack
- Tool calling + DeepSeek-V4 reasoning parser enabled
Prerequisites
| Item | Funland value / notes |
|---|---|
| Hardware | 2× DGX Spark (GB10), ~128 GB unified memory each |
| Interconnect | RoCE/IB dual HCA + bonded Ethernet for Gloo/TP sockets |
| Disk | ~160+ GiB free per node for 0731; keep DSpark (~156 GiB) if you want rollback |
| Docker | Compose + NVIDIA container runtime, network_mode: host, ipc: host |
| Image | ghcr.io/anemll/dspark-vllm-gx10:0.1.1 |
| Weights | deepseek-ai/DeepSeek-V4-Flash-0731 (rev we used: 9e165c30…) |
| SSH | Head can passwordless SSH to worker as the same user |
| Base recipe | Clone/adapt MiaAI-Lab/Anemll dual-Spark scripts (see 1M/6 recipe) |
This is not a from-scratch vLLM build guide. Start from a working Anemll dual-Spark DSpark checkout, then weight-swap. Changing runtime and weights at the same time is how you lose a weekend.
Topology
Substitute your IPs and NIC names. Wrong VLLM_HOST_IP / socket IFNAME is the classic Gloo-on-127.0.0.1 failure.
| Role | Funland | Env keys |
|---|---|---|
| Head | 192.168.1.11 / RoCE 10.0.0.1 | MASTER_ADDR=10.0.0.1, VLLM_HOST_IP=10.0.0.1, API :8888 |
| Worker | 192.168.1.12 / RoCE 10.0.0.2 | WORKER_HOST=10.0.0.2, WORKER_VLLM_HOST_IP=10.0.0.2 |
| NCCL | dual HCA + bond0 | NCCL_IB_HCA=rocep1s0f0,rocep1s0f1, NCCL_SOCKET_IFNAME=bond0, GID index 3 |
| Gloo/TP sockets | bond0 both ranks | GLOO_SOCKET_IFNAME / TP_SOCKET_IFNAME = bond0 |
Weights: download once, rsync once
Do not dual-WAN pull ~160 GB on both nodes. Pull on the head, rsync the HF hub tree to the worker over RoCE/LAN.
# On Spark1 (head)
export HF_HUB_OFFLINE=0 TRANSFORMERS_OFFLINE=0 HF_HUB_DISABLE_XET=1
hf download deepseek-ai/DeepSeek-V4-Flash-0731 --max-workers 4
MODEL_DIR=$HOME/.cache/huggingface/hub/models--deepseek-ai--DeepSeek-V4-Flash-0731
# Verify head
find "$MODEL_DIR" -name 'model-*-of-*.safetensors' | wc -l # expect 48
find "$MODEL_DIR" -name '*.incomplete' | wc -l # expect 0
ls "$MODEL_DIR/snapshots" # pin the SHA
test -f "$MODEL_DIR/snapshots/<sha>/config.json"
test -d "$MODEL_DIR/snapshots/<sha>/encoding"
# Rsync full hub model dir (blobs + snapshots) to worker
rsync -aH --info=stats2 \
"$MODEL_DIR/" \
milo@10.0.0.2:"$MODEL_DIR/"
# On worker: wipe any leftover .incomplete from a killed dual pull
ssh milo@10.0.0.2 "find $MODEL_DIR -name '*.incomplete' -delete"
ssh milo@10.0.0.2 "find $MODEL_DIR -name 'model-*-of-*.safetensors' | wc -l" # 48
Mount the full hub model directory (or let Anemll resolve the HF id with HF_CACHE). Snapshot-only bind mounts break relative symlinks into blobs/ and the loader dies with “Invalid repository ID”.
Funland pin: rev 9e165c30e2704aec5d9d593cce3eebd58bbef1cb, ~156 GiB on disk.
Checkout + env
Copy a known-good Anemll DSpark checkout; do not mutate production in place until you trust the swap.
# On head
cp -a ~/ds4-f-mia-anemll-1m6 ~/ds4-f-mia-anemll-0731
cd ~/ds4-f-mia-anemll-0731
# Edit .env.dspark (minimum deltas)
DSPARK_MODEL=deepseek-ai/DeepSeek-V4-Flash-0731
SERVED_MODEL_NAME=deepseek-v4-flash-0731
WORKER_DIR=/home/milo/ds4-f-mia-anemll-0731
WORKER_SCRIPT_DIR=/home/milo/ds4-f-mia-anemll-0731
# Keep the Anemll 1M/6 shape that actually fits this pair
MAX_MODEL_LEN=1048576
MAX_NUM_SEQS=6
MAX_NUM_BATCHED_TOKENS=8192
GPU_MEMORY_UTILIZATION=0.85 # 0.80 was short of KV for 1M on our pair
MTP_NUM_TOKENS=5
DSPARK_VLLM_IMAGE=ghcr.io/anemll/dspark-vllm-gx10:0.1.1
HF_HUB_OFFLINE=1
HF_CACHE=/home/milo/.cache/huggingface
WORKER_HF_CACHE=/home/milo/.cache/huggingface
# Sync checkout to worker
rsync -a ~/ds4-f-mia-anemll-0731/ milo@10.0.0.2:~/ds4-f-mia-anemll-0731/
Env checklist (copy-friendly)
| Knob | Value we run |
|---|---|
DSPARK_MODEL | deepseek-ai/DeepSeek-V4-Flash-0731 |
SERVED_MODEL_NAME | deepseek-v4-flash-0731 |
MAX_MODEL_LEN | 1048576 |
MAX_NUM_SEQS / batch | 6 / 8192 |
GPU_MEMORY_UTILIZATION | 0.85 |
MTP_NUM_TOKENS | 5 (dspark speculative) |
kv_cache_dtype | nvfp4_ds_mla (compose) |
moe-backend | flashinfer_b12x (compose) |
| Master / worker RoCE | 10.0.0.1 / 10.0.0.2, port 25000 |
Launch / stop
# Clean both nodes first (Aiden/legacy units masked; SHM + page cache)
# Then from head only:
cd ~/ds4-f-mia-anemll-0731
PROJECT_NAME=ds4-f-mia-anemll-0731 WAIT_ATTEMPTS=120 WAIT_SECONDS=15 \
./start-deepseek-v4-flash-dspark.sh
# Status / logs
./status-deepseek-v4-flash-dspark.sh
docker logs -f ds4-f-mia-anemll-0731-vllm-dspark-1
# Stop
./stop-deepseek-v4-flash-dspark.sh
Cold bind is often 8–15 minutes (weight load + TileLang/FlashInfer autotune + cudagraph). SHM “60 seconds” log lines during compile are often non-fatal — wait for Application startup complete and a live /v1/models.
vLLM serve shape (what Anemll runs)
Effective serve (abridged from compose; env-expanded). Useful if you re-implement without their scripts:
vllm serve deepseek-ai/DeepSeek-V4-Flash-0731 \
--served-model-name deepseek-v4-flash-0731 \
--host 0.0.0.0 --port 8888 \
--trust-remote-code \
--tensor-parallel-size 2 \
--kv-cache-dtype nvfp4_ds_mla \
--block-size 256 \
--max-model-len 1048576 \
--max-num-seqs 6 \
--max-num-batched-tokens 8192 \
--gpu-memory-utilization 0.85 \
--enable-prefix-caching \
--async-scheduling \
--enable-chunked-prefill \
--speculative-config '{"method":"dspark","num_speculative_tokens":5,"draft_sample_method":"probabilistic"}' \
--tokenizer-mode deepseek_v4 \
--distributed-executor-backend mp \
--moe-backend flashinfer_b12x \
--tool-call-parser deepseek_v4 \
--enable-auto-tool-choice \
--reasoning-parser deepseek_v4 \
--default-chat-template-kwargs '{"thinking":false}' \
--enable-flashinfer-autotune \
--nnodes 2 \
--node-rank $NODE_RANK \
--master-addr 10.0.0.1 \
--master-port 25000 \
${HEADLESS:+--headless}
Official HF card sometimes shows K=7 greedy on GB300-class gear. We kept Funland-proven K=5 probabilistic on Anemll first. Change one axis at a time if you experiment.
Gates (do these before calling it live)
# 1) models
curl -s http://127.0.0.1:8888/v1/models | jq .
# expect id deepseek-v4-flash-0731, max_model_len 1048576
# 2) exact marker (thinking off)
curl -s http://127.0.0.1:8888/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model":"deepseek-v4-flash-0731",
"messages":[{"role":"user","content":"Reply exactly PROMOTE_0731_OK."}],
"temperature":0,"max_tokens":32,
"chat_template_kwargs":{"thinking":false}
}'
# 3) tools must return tool_calls[]
# 4) stream completes
# 5) optional Keys (model is argv[3] — default is still the DSpark name):
python3 bench_concurrent.py http://127.0.0.1:8888 1 deepseek-v4-flash-0731
Funland promote gates (2026-07-31): exact PROMOTE_0731_OK, tools PASS, Hermes ROUTE_OK.
Hermes wiring
# config.yaml provider sketch
custom_providers / providers entry for spark-ds4:
api: http://192.168.1.11:8888/v1
api_key: no-key-required # not "none"
context_length: 200000
default_model: deepseek-v4-flash-0731
models:
deepseek-v4-flash-0731:
context_length: 200000
max_output_tokens: 32768
deepseek-v4-flash-dspark: # keep for rollback
context_length: 200000
max_output_tokens: 16384
# smoke
hermes chat --provider spark-ds4 -m deepseek-v4-flash-0731 \
-q 'Reply exactly ROUTE_OK.' -Q --toolsets safe
Measured numbers (this pair, same Keys harness)
| Probe | DSpark (healthy ref) | Flash-0731 | Notes |
|---|---|---|---|
| Keys C1 best-of-2 | 72.4 t/s | 68.9 t/s | −5% short-gen |
| Keys C4 | 173.8 | 135.8 | slower multi-stream |
| Keys C6 | 211.0 | 180.6 | slower |
| Draft accept C1 | ~0.74–0.78 | ~0.57–0.74 | noisier on 0731 |
| Server max len | 1M | 1M | KV ~2.1–2.16M tokens |
| Tools / multi-hop tools | PASS | PASS | |
| Hermes route | ROUTE_OK | ROUTE_OK |
Post-promote optimization (same day)
After promote we did not thrash the runtime. Goal: make official 0731 as good as possible for daily Hermes on this pair — agent correctness first, Keys second. Sources: HF card, Anemll/howtospark dual recipe, Tony/Tech2Wild Patch3 lineage, Blackwellboy model-serving-minefield (traps 11/28/56–62), NVIDIA Dev forums, SGLang cookbook (no signed dual-GB10 cell), and a Nous Opus-5 second opinion on a measured brief.
Live forensics on the production serve
| Fact | Value | Implication |
|---|---|---|
dspark_block_size | 5 | Funland K=5 matches the drafter block. Official card K=7 greedy is a separate experiment — demoted as the next canary. |
YaRN original_max_position_embeddings | 65,536 | Advertised 1M is extended; cold long retrieval is not free IQ. |
| Serve line | K=5 probabilistic, util 0.85, B12X, FlashInfer sampler, generation-config vllm, thinking default boolean false | Tony-class garble mitigations present; no server rep-penalty override. |
| KV (pre-wedge / post-restore) | ~2.20M / 2,214,807 | 1M/6 still fits with headroom. |
Trap 57 (thinking string "false") | CONFIRMED — reasoning still emitted | Clients must send JSON boolean false, never the string. |
| Cold needle ~12.6k / ~50.3k prompt tokens | HIT (6.7s / 35.7s) | Hermes client 200k not disproven at these depths. |
| Cold ~128k synthetic needle | Spark1 hard hang → power-cycle | Never first-fire ≥100–128k unwatched on sole prod head. |
| SGLang dual-GB10 | PARK | Cookbook covers B200/B300/GB200/… not dual GB10 + fused DSpark. |
Ranked next moves
| Tier | Action | Gate |
|---|---|---|
| DONE | Boolean thinking via Hermes extra_body; trap57 mitigation | Config live on milo profile |
| DONE | Sampling A/B short harness: greedy 0/1.0 vs agentic 1.0/0.95 | BOTH_PASS tools/code; accept ~0.65→0.60 under agentic |
| OPEN | Hermes auto-pin agentic temp/top_p | Provider overrides only support extra_body today — not pinned globally |
| DO-NOW | Real Hermes multi-tool replay harness + draft-accept deltas | Task-mix acceptance, not Keys alone |
| GUARDED | Cold long steps only with console + second watcher + preferably copy checkout | Step 80k→100k before 128k/200k |
| LATER | MAX_NUM_BATCHED_TOKENS 8192→16384 (log warned scheduled=8168) | One-axis canary after harness |
| NEXT CANARY | Wesche-style 40K / K=7 greedy speed lane (copy checkout) | Same image+rev; tools+Hermes+short fixture; not a 1M drop-in |
| KEEP | Prod 1M/6 K=5 probabilistic for Hermes daily | Long-ctx + multi-seq contract |
| PARK | SGLang / stock vLLM 0.26 / r0b0tlab runtime swap | Already KEEP_ANEMLL on runtime |
| NEVER | util <0.85 @1M; 1M/12; dual-WAN pulls; Keys-as-IQ demote | — |
cd ~/ds4-f-mia-anemll-0731 && PROJECT_NAME=ds4-f-mia-anemll-0731 WAIT_ATTEMPTS=120 WAIT_SECONDS=15 ./start-deepseek-v4-flash-dspark.sh. Gates: exact marker, tools, Hermes ROUTE_OK. Worker was stopped from Spark2 while head was dead so TP did not thrash.Rollback to DSpark
cd ~/ds4-f-mia-anemll-0731 && ./stop-deepseek-v4-flash-dspark.sh
# SHM + page-cache both nodes
cd ~/ds4-f-mia-anemll-1m6
PROJECT_NAME=ds4-f-mia-anemll-1m6 WAIT_ATTEMPTS=120 WAIT_SECONDS=15 \
./start-deepseek-v4-flash-dspark.sh
# Point Hermes default_model back to deepseek-v4-flash-dspark
Do not delete 0731 weights after promote; do not delete DSpark weights until you are sure you will not roll back.
Pitfalls we hit so you do not have to
- Dual-WAN download — wasteful and contends disk with live benches. Head download + RoCE rsync.
- Snapshot-only mounts — HF blob symlinks break. Full hub model dir or HF id + shared cache.
GPU_MEMORY_UTILIZATION=0.80— failed 1M KV on this pair; use 0.85.- Gloo on 127.0.0.1 — set
VLLM_HOST_IPper rank + bond0 socket IFNAMEs. - Keys 404 — harness defaults to DSpark model name; pass
deepseek-v4-flash-0731as argv[3]. - Hermes api_key must be
no-key-required, notnone. - Cold start patience — 8–15 min; do not abort on first SHM timeout log during autotune.
- Do not thrash Aiden/legacy units under compose — mask them first.
- Runtime canaries ≠ weight canaries — we previously kept Anemll vs r0b0tlab v0.26 (August DS4-F Testing). This recipe is weights on Anemll.
- Official K=7 greedy is a separate experiment from K=5 probabilistic — and on this checkpoint
dspark_block_size=5, so K=5 is the natural match. - thinking string
"false"is not off (trap 57). Send a JSON boolean. - Unwatched ≥128k synthetic cold needles on sole prod head can hard-hang the box (we power-cycled Spark1). Guarded only.
- Head-down mid-TP: stop/rm the orphaned worker on Spark2 immediately; restore from head after clean SHM/page-cache both nodes.
What we run right now (Funland end-state)
| Item | Value |
|---|---|
| Checkout | /home/milo/ds4-f-mia-anemll-0731 |
| Image | ghcr.io/anemll/dspark-vllm-gx10:0.1.1 |
| Weights | DeepSeek-V4-Flash-0731 @ 9e165c30… |
| Served | deepseek-v4-flash-0731 on http://192.168.1.11:8888/v1 |
| Hermes | provider spark-ds4, default model deepseek-v4-flash-0731 |
| Rollback checkout | /home/milo/ds4-f-mia-anemll-1m6 + DSpark weights |
Attribution
- Mia / @MiaAI_lab — public signal on the official Flash-0731 drop; ongoing dual-Spark / Anemll ecosystem work that this recipe builds on. If you get value from GB10 Flash stacks, follow and credit Mia.
- MiaAI-Lab / Anemll —
ghcr.io/anemll/dspark-vllm-gx10image and dual-node DSpark compose/script shape (1M/6 MTP5 lane). Funland runs a local adaptation; bugs in our env knobs are ours. - DeepSeek —
DeepSeek-V4-Flash-0731weights and encoding docs on Hugging Face. - vLLM / FlashInfer — serving and B12X / MLA stack under the Anemll image.