Laguna s2.1 YAML for Eugr's Repo (or sparkrun)

Hi all,
Recipe for Eugr’s repo (will work with Sparkrun also) to serve the new Laguna model. It’s still running on my own benchmarking tool but seems very good so far.
I tried fiddling with settings but their defaults were optimal.

First:

git pull
./build-and-copy.sh --tf5

Then run (takes ~15 mins on first run, use tensor_parallel: 1 for single spark):

recipe_version: "1"
name: Laguna-S-2.1-NVFP4
description: vLLM serving Laguna S 2.1 in native NVFP4 with DFlash speculation
model: poolside/Laguna-S-2.1-NVFP4
container: vllm-node

defaults:
  port: 8000
  host: 0.0.0.0
  tensor_parallel: 2
  gpu_memory_utilization: 0.85
  max_model_len: 262144

env:
  CUTE_DSL_ARCH: "sm_121a"
  MAX_JOBS: "4"

# Replace gpu_memory_utilization config with `--kv-cache-memory=32449423258 to fit into memory

command: |
  vllm serve poolside/Laguna-S-2.1-NVFP4 \
    --port {port} \
    --host {host} \
    --tensor-parallel-size {tensor_parallel} \
    --gpu-memory-utilization {gpu_memory_utilization} \
    --max-model-len {max_model_len} \
    --speculative-config '{{"model":"poolside/Laguna-S-2.1-DFlash-NVFP4","num_speculative_tokens":15}}' \
    --enable-auto-tool-choice \
    --tool-call-parser poolside_v1 \
    --reasoning-parser poolside_v1 \
    --override-generation-config '{{"temperature":0.7,"top_p":0.95}}' \
    --max-num-seqs 32

Benchmarks:

============ Serving Benchmark Result ============
Successful requests:                     50
Failed requests:                         0
Benchmark duration (s):                  83.41
Total input tokens:                      12173
Total generated tokens:                  10229
Request throughput (req/s):              0.60
Output token throughput (tok/s):         122.63
Peak output token throughput (tok/s):    96.00
Peak concurrent requests:                50.00
Total token throughput (tok/s):          268.58
---------------Time to First Token----------------
Mean TTFT (ms):                          3287.16
Median TTFT (ms):                        1033.71
P99 TTFT (ms):                           14485.45
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms):                          166.99
Median TPOT (ms):                        167.42
P99 TPOT (ms):                           351.18
---------------Inter-token Latency----------------
Mean ITL (ms):                           368.15
Median ITL (ms):                         393.69
P99 ITL (ms):                            440.30
---------------Speculative Decoding---------------
Acceptance rate (%):                     11.71
Acceptance length:                       2.76
Drafts:                                  3717
Draft tokens:                            55755
Accepted tokens:                         6529
Per-position acceptance (%):
  Position 0:                            64.89
  Position 1:                            37.77
  Position 2:                            22.33
  Position 3:                            13.80
  Position 4:                            9.25
  Position 5:                            6.43
  Position 6:                            4.90
  Position 7:                            3.98
  Position 8:                            2.99
  Position 9:                            2.34
  Position 10:                           2.02
  Position 11:                           1.67
  Position 12:                           1.35
  Position 13:                           1.16
  Position 14:                           0.78
==================================================

I’ll get round to doing the two spark fp8 one at some point but it’s probably default also.

Whats the performance for c=1 ?

Hi. Thanks for posting. Are this acceptance rates for prose? Code? Other? Thanks!

Sorry i wasn’t very clear, the benchmark is for speed, my own benchmarks are for code but takes a couple of days to run and are kind of specific for my usecase.

Will try to remember to do a run when i get home. Most of my use-cases use many parralel agents for coding so just do the default vllm one normally.

Spolier alert: You will quickly notice that in fact it is unusable, in 50% of cases it will think for years until thinking about how to write “Hello” in 30% of cases it will make a lot of syntax errors in the files and in the rest of cases it will come out stuffs not really good…

I dropped the case obviously the benchmark is completely wrong…

This recipe doesn’t really work for me. It does serve it, but for some reason the thinking is messed up. It prints without an opening . Also you are building --tf5, which by default creates a different container name than the container name you are supplying at the top of the recipe.

This github recipe works better, and downloads the same model so it didn’t need to redownload anything after I tried this recipe, though it did need to rebuild a few things:

My experience with this NVFP4 model have been very mixed, but I’ll report that in another topic.

Apologies I pasted the old file I had by accident, just add -tf5 to the end ofvllm-node

The benchmark is tokens per second opposed to some sort of skill test.

It’s a pure coding model which the author notes can over-think with small prompts like “hello” (surprisingly they didn’t train it for such prompts).

I made a small correction as i pasted in an old file but it works extremely well on things like Autoresearch, best model i’ve run for that.

Wierd, the recipe looks exactly the same to me with a small difference in speculative tokens. I’ve made a quick correction to the recipe to add the -tf5 as i accidently pasted in the odl version but good that you have a working version now anyway.

Also add this line if you are getting it crash out:
```

env:
VLLM_ENGINE_READY_TIMEOUT_S: “3600”
```

Bench results from a downloader, not mine. I seriously have not even got around to doing my benches.

inclusionAI/Ling-3.0-flash VS poolside/Laguna-S-2.1-NVFP4 Opinions regarding quality vs Qwen 27b 35b 122b ?

I’ve not tried ling to a decent level, laguna is the best coder when compared to the qwen’s for my use-cases but it doesn’t have their multimodal benefits.

I really only use smaller parameter models (below ~120b) for non coding agentic tasks as i find the jaggedness of small models too jarring for coding.

All great models though so there’s likely not much in it.

Laguna is amazing for karpathys autoresearch.

I tried this one out. Didn’t actually have issues with looping surprisingly, but it takes an incredibly long amount of time reasoning. I tried it thrice on my task and it didn’t converge on a solution even once in 45k tokens. Throughput was about 20tok/s.

Did you specifically disable prefix caching because of issues with it and this model? I noticed weird behavior with repeat queries on the previous Lagunas I tried. One time it also decided to just not think, and well… that went terribly. Also it doesn’t separate its thinking and output with the executable I tried:

docker run -d   --name vllm-laguna   --gpus all   --net=host   --ipc=host   -v ~/.cache/huggingface:/root/.cache/huggingface   vllm-node   vllm serve JasonW2025/Laguna-S-2.1-ModelOpt-NVFP4-W4A4-vllm     --served-model-name laguna-w4a4     --tensor-parallel-size 1     --dtype bfloat16     --attention-backend FLASHINFER     --kernel-config '{"moe_backend":"FLASHINFER_CUTLASS"}'     --gpu-memory-utilization 0.85     --max-model-len 260000     --max-num-seqs 4     --kv-cache-dtype auto     --no-enable-prefix-caching     --trust-remote-code     --enable-auto-tool-choice     --tool-call-parser poolside_v1     --reasoning-parser poolside_v1

This is using the Spark specific built VLLM container that ships in eugr’s repo.

Another thing I also noticed is that during startup, this one also said this:

(EngineCore pid=167) INFO 08-08 21:19:31 [weight_utils.py:897] Auto-prefetch is disabled because the filesystem (EXT4) is not a recognized network FS (NFS/Lustre) and the checkpoint size (64.38 GiB) exceeds 90% of available RAM (49.22 GiB)

But it still loaded, and VLLM’s total used ram/vram was only 88GB. free -h showed 110GB used:

free -h
               total        used        free      shared  buff/cache   available
Mem:           121Gi       110Gi        10Gi       227Mi       1.7Gi        10Gi
Swap:           15Gi       1.0Gi        14Gi

Pretty confusing.

I’m wondering if the lack of convergence is a templating issue, because I noticed that on llama.cpp, they the q4 quants did slightly better at converging on a solution, and one time the output was actually really good. The caching seemed to be messed up though because repeat queries usually yielded way worse results.

I think Laguna is just dead in the water for my task, though unfortunately. The weird “think/don’t think” hybrid is also not too great; my task always needs it to reason otherwise it outputs garbage.

Edit: Just noticed “(APIServer pid=1) INFO 08-08 21:19:19 [cache.py:282] Using fp8_e4m3 data type to store kv cache. It reduces the GPU memory footprint and boosts the performance. Meanwhile, it may cause accuracy drop without a proper scaling factor
(APIServer pid=1) WARNING 08-08 21:19:19 [modelopt.py:379] Detected ModelOpt fp8 checkpoint (quant_algo=FP8). Please note that the format is experimental and could change.”

I thought specifying the kv cache type in your run example was to get around the fp8 issue in your documentation but I guess it just ignores it.

Nevermind I think I botched that run statement, noticed those environmental variables and that the patch for the config file has to be applied. I think this is closer to what’s intended, after these patches:

python3 -c "
import json, pathlib
c = json.loads(pathlib.Path('config.json').read_text())
c['quantization_config'].pop('kv_cache_scheme', None)
pathlib.Path('config.bf16kv.json').write_text(json.dumps(c, indent=2))
"
diff config.json config.bf16kv.json
1306,1310d1305
<     "kv_cache_scheme": {
<       "dynamic": false,
<       "num_bits": 8,
<       "type": "float"
<     },
@spark-b60e:~/.cache/huggingface/hub/models--JasonW2025--Laguna-S-2.1-ModelOpt-NVFP4-W4A4-vllm/snapshots/eaa6b03f2e26f57452928e7a934cde8e3ddd1994$ mv config.json someotherfile.bak
@spark-b60e:~/.cache/huggingface/hub/models--JasonW2025--Laguna-S-2.1-ModelOpt-NVFP4-W4A4-vllm/snapshots/eaa6b03f2e26f57452928e7a934cde8e3ddd1994$ mv  config.bf16kv.json config.json

And I think this is the intended docker executable:

docker run -d \
  --name vllm-laguna \
  --gpus all \
  --net=host \
  --ipc=host \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  -e HF_HUB_OFFLINE=1 \
  -e CUTE_DSL_ARCH=sm_121a \
  -e VLLM_TRUST_REMOTE_CODE=1 \
  vllm-node \
  vllm serve JasonW2025/Laguna-S-2.1-ModelOpt-NVFP4-W4A4-vllm \
    --served-model-name laguna-w4a4 \
    --tensor-parallel-size 1 \
    --dtype bfloat16 \
    --attention-backend FLASHINFER \
    --kernel-config '{"moe_backend":"FLASHINFER_CUTLASS"}' \
    --gpu-memory-utilization 0.85 \
    --max-model-len 260000 \
    --max-num-seqs 4 \
    --kv-cache-dtype auto \
    --no-enable-prefix-caching \
    --trust-remote-code \
    --enable-auto-tool-choice \
    --tool-call-parser poolside_v1 \
    --reasoning-parser poolside_v1

I’ll give it another shot.

Edit: No difference really, just runs slower (~15 tok/s at 120k context input) and still has a lot of trouble converging regardless of temp.

There was a KV calibration issue on that which results in undeterministic behavior , try this JasonW2025/Laguna-S-2.1-ModelOpt-NVFP4-W4A4-KVcal-vllm · Hugging Face

Cheers.

This one works.

Note, it is for a single user which fits my workflow but uses official models.

It is critical to have kv_cache_dtype: fp8 for dflash to work at all.

Does occasionally get loopy.

# Recipe: poolside/Laguna-S-2.1-NVFP4
# poolside Laguna-S-2.1 (117.6B total / 8.5B active MoE) in NVFP4 (W4A4) with DFlash
# draft speculation. Fits a single DGX Spark. Requires a recent vLLM (main) build
# with the poolside architecture + dflash speculative method.

# ID                                        REVISION                                   SIZE LAST_MODIFIED REFS
# ----------------------------------------- ---------------------------------------- ------ ------------- ----
# model/poolside/Laguna-S-2.1-DFlash-NVFP4  b3b5921a900b9e0a1e27e50bdaeb480692a6d19b   2.2G 1 day ago     main
# model/poolside/Laguna-S-2.1-NVFP4         f8fdfcdc4e7b0c474a0102430a8cae0a3a358669  99.7G 2 days ago    main


recipe_version: "1"
name: Laguna-S-2.1
description: vLLM serving poolside/Laguna-S-2.1-NVFP4 with DFlash speculation

# HuggingFace model to download (optional, for --download-model)
model: poolside/Laguna-S-2.1-NVFP4

solo_only: true

# Container image to use
container: vllm-node

# No mods required
mods: []

# Default settings (can be overridden via CLI)
defaults:
  port: 8000
  host: 0.0.0.0
  tensor_parallel: 1
  gpu_memory_utilization: 0.90
  max_model_len: 262144
  max_num_batched_tokens: 2048
  max_num_seqs: 1
  num_speculative_tokens: 3
  kv_cache_dtype: fp8

# Environment variables
env:
  TORCH_CUDA_ARCH_LIST: 12.1f
  CUTE_DSL_ARCH: sm_121a
  VLLM_USE_FLASHINFER_SAMPLER: "1"
  MAX_JOBS: "4"

# The vLLM serve command template
command: |
  vllm serve poolside/Laguna-S-2.1-NVFP4 \
    --served-model-name poolside/Laguna-S-2.1 \
    --host {host} \
    --port {port} \
    --max-model-len {max_model_len} \
    --max-num-batched-tokens {max_num_batched_tokens} \
    --max-num-seqs {max_num_seqs} \
    --gpu-memory-utilization {gpu_memory_utilization} \
    --tensor-parallel-size {tensor_parallel} \
    --enable-chunked-prefill \
    --enable-prefix-caching \
    --enable-auto-tool-choice \
    --tool-call-parser poolside_v1 \
    --reasoning-parser poolside_v1 \
    --kv-cache-dtype {kv_cache_dtype} \
    --trust-remote-code \
    --speculative-config '{{"method": "dflash", "model": "poolside/Laguna-S-2.1-DFlash-NVFP4", "num_speculative_tokens": {num_speculative_tokens}}}' \
    --default-chat-template-kwargs '{{"enable_thinking": true}}' \
    --override-generation-config '{{"temperature": 0.7, "top_p": 0.95}}'

Thanks, I will try it when I get a moment.

Is this actually using the official model? I notice your config says “~71GB weights” but I’m pretty sure the official one is at 99 now.

What’s frustrating about this model is that I’ve occasionally seen it output actually really good stuff for my task, and in a reasonable amount of time, too. But it’s hard to get it to stop at a good enough solution that it already reached 10 full length essays ago. I really wanna hold out some hope for it because when it works it’s good.

Oh sorry, that comment was when I created it with the original model before the update.

This is with (uvx hf cache ls --revisions):

ID                                        REVISION                                   SIZE LAST_MODIFIED REFS
----------------------------------------- ---------------------------------------- ------ ------------- ----
model/poolside/Laguna-S-2.1-DFlash-NVFP4  b3b5921a900b9e0a1e27e50bdaeb480692a6d19b   2.2G 1 day ago     main
model/poolside/Laguna-S-2.1-NVFP4         f8fdfcdc4e7b0c474a0102430a8cae0a3a358669  99.7G 2 days ago    main

Note, i also reduced dflash to only 3 tokens ahead. acceptance drops off pretty steeply after that