Kept / trial active in llama-swap.yaml · aliases: laguna-s-2.1-q3km-262k
| Run | Score | tok/step | R5 | Log |
|---|---|---|---|---|
| 20260803-143757 | 20/23 | 67.9 | — | results-laguna-s-20260803-143757.log |
laguna-s at UD-Q3_K_M — and this file's quant ceiling was WRONG**The claim below — "Next real expert step is IQ3_XXS at 41.2 GiB — unservable on
a 31 GiB pool. No quant of this model that fits this box will fix expr_eval" —
is FALSE and blocked a fair retest for a month.** It treated host RAM as the
ceiling. [[gpt-oss-120b-cpu-moe-spill]] served 59.0 GiB on this box, so
41 GiB was never the limit. unsloth's UD-Q3_K_M (50.3 GiB) is smaller than
that, and its bulk experts ARE IQ3_XXS (92 tensors) with **Q8_0 attention
throughout** — better than either quant tested in August.
*Generalisable:* a "will not fit" verdict is only as good as the pool it assumed.
Re-check it whenever the envelope moves.
Served entry (verified end-to-end through llama-swap):
-ngl 99 --n-cpu-moe 31 -c 131072 -fa on --tensor-split 76,24
-b 2048 -ub 512 -rea on --temp 0.6 --cache-type-k q8_0 --cache-type-v q8_0
BATCH IS WORTH 2.7x ON PREFILL — the first version of this entry got it wrong.
It shipped -b 512 -ub 128, chosen after ncmoe=32 failed on compute buffers at
-b 1024 -ub 256, then carried to ncmoe=33 without re-testing. That limit
does not bind at 31 or 33. Measured on one 11,539-token prompt, fresh server each:
| ctx | batch | prefill | free (5060/4060) |
|---|---|---|---|
| 262144 | 512/128 | 36.7 tok/s | 1878/1394 |
| 262144 | 2048/512 | 100.5 | 1650/1156 |
| 131072 | 2048/512 | 114.8 (134.7 via llama-swap) | 2774/2490 |
| 65536 | 2048/512 | 125.1 | 3426/1012 |
**BATCH dominates (2.7x); CONTEXT is minor (1.24x over a 4x range) and there is
NO paging cliff.** 131072 chosen over the native 262144: faster AND the best
margins of all four. 65k is 9% faster again but drops a card to 1012 MiB.
"Decode is flat, so context is free" was TRUE BUT MISLEADING — decode IS flat
~9.6-11 tok/s at every context (paging-bound), which is why I first took the
whole 262k window. But PREFILL is the agent metric and it is NOT flat. A 30k
turn went ~14 min -> ~3.7 min from this fix. *Never choose a serving config on
decode alone.*
### DO NOT ADD --swa-full (tested, n=3)
Laguna is a sliding-window arch (36 of 48 layers, window 512), so the
[[nvrm-xid31-mmu-fault-4060ti]] crash that makes --swa-full MANDATORY for
gpt-oss-120b was the obvious worry. It does not transfer. On R5
interrupt_replan — the reproducer that crashed gpt-oss 5/5:
| arm | crash | task |
|---|---|---|
| rolling SWA, n=3 | 0/3 | PASS, PASS, PASS, 0 redundant |
| --swa-full, n=1 | 0/1 | PASS, 0 redundant (no benefit) |
And it would cost 4x KV here (12 of 48 full-attn, vs gpt-oss's 18 of 36):
7.28 -> 29 GiB at 262144, making this window impossible. Same flag, opposite
verdict on two models — test it per model, never inherit it.
### expr_eval RE-TESTED at 3-bit, n=20 — STILL FAILS. The prediction was WRONG.
The A/B below localised expr_eval to the 2-bit experts and predicted a 3-bit
step would fix it. Measured 2026-09-01: it does not. Two builds that differ
ONLY in 92 expert tensors (attention held at Q8_0 in both — the exact complement
of the August A/B):
| build | experts | -2**2 bug | pass |
|---|---|---|---|
| Aug IQ2_XXS | 2-bit | 3/3 deterministic | 0/3 |
| Aug IQ2_M | 2-bit, identical experts | 3/3 deterministic | 0/3 |
| UD-Q3_K_M | IQ3_XXS | 1/10 sporadic | 0/10 |
| UD-IQ4_XS | IQ3_S | 5/10 sporadic | 0/10 |
What changed is the failure MODE, not the outcome: from one deterministic
arithmetic bug to assorted code-quality defects (nonlocal pos UnboundLocalError,
over/under-strict malformed-input handling, once syntactically invalid Python).
UD-IQ4_XS IS NOT BETTER — do not pull it again. Despite +3.3 GiB and a
better 3-bit expert (IQ3_S vs IQ3_XXS) it shows the August bug 5x more often
(5/10 vs 1/10, one-sided Fisher p~0.07, not significant at n=10) and passes no
more often. Note also that the build NAMED "IQ4_XS" is not 4-bit in its bulk
experts — 92 of them are IQ3_S. Read the tensor types, not the filename.
Temperature was a real confound and was controlled: grill_round1 defaults
to GRILL_TEMP=0.2 (what August measured) and the first arm was mistakenly run
at 0.6 to match the served sampler. Re-run at 0.2: still 0/5. Temperature moved
the failure MIX, not the outcome.
**So this file's conclusion — "No quant of this model that fits this box will fix
expr_eval" — is RIGHT, but its reasoning was WRONG.** It said no such quant
would FIT. They fit fine (50.3 and 53.6 GiB both serve). They just don't fix it.
*A correct conclusion resting on a false premise still needs re-testing when the
premise falls* — it took serving three quants to learn that.
### The split trap, refined
--tensor-split 76,24 was BISECTED at ncmoe=33/262144 (free MiB, 5060Ti/4060Ti):
70,30 no load | 74,26 804/2470 | 76,24 1878/1394 | 78,22 2954/318 |
80,20 no load. One expert layer is 995 MiB = ~2 points of A, which is why
the window is this narrow. And CUDA1's share is fixed by the SPLIT ALONE:
ncmoe 33->34 left CUDA1 at *exactly* 804 MiB and gave all 996 MiB to CUDA0. A
tight CUDA1 can only be relieved by raising A — raising ncmoe does nothing for it.
Target ~1.4 GiB free per card; cuBLAS died at 466 MiB on gpt-oss.
-b 512 -ub 128 is load-bearing at 262144 (the compute-buffer lesson below, again).
**CLIENT max_tokens FLOOR IS ~4000 — the "MIN CLIENT max_tokens = 1200" below is
WRONG.** Measured n=2 per rung on one identical prompt:
| max_tokens | emits content | tokens used |
|---|---|---|
| 1400 | 0/2 (finish=length; one gave content_len=0 with 4606 chars of reasoning) | 1400 cap |
| 2000 | 2/2 | 1136 / 1153 |
| 4000 | 2/2 | 1894 / 1310 |
| 8000 | 2/2 | 1964 / 1398 |
Natural usage spans 1136-1964 tok (1.7x) on the SAME prompt and never exceeds
~2000 even when given 8000 — so a generous cap costs NOTHING while a tight one
truncates intermittently. The intermittency is the trap: it reads as a broken
model rather than a truncated one, and is very likely what the August trial logged
as "~30% of requests returned EMPTY bodies" and blamed on the sampler.
Cf. [[kimi-code-max-output-size-zero-margin]].
NOT overturned: the score-per-resource case that retired it. R5
interrupt_replan x4 all PASS, but expr_eval still fails 0/20 at 3-bit (see
above) — so the capability gap the retry was meant to close is STILL THERE.
Trial ran ~3 hours and ended the same day it started. Entry removed from
llama-swap.yaml, UD-IQ2_M weights deleted. The findings below are all still
valid and are why this file is kept -- the METHOD (CPU spill) is a keeper even
though the model is not.
Ran the first --n-cpu-moe spill experiment on this box. [[laguna-xs-2.1]] said
"S-2.1 is REJECTED — unservable here, don't re-derive this". That verdict was
wrong on its stated grounds and is now corrected.
**STATUS: RETIRED. I concluded "reject"; the user overrode it for a real-use
trial (shipped 4ca478f6), and ~3h of real use ended in retirement anyway --
so the original cost case was right, but ONLY the trial produced the findings
below, none of which the grill could have surfaced.** The reject was
defensible on score-per-resource (below) but it leaned on a single grill, and
the one thing this model is unambiguously good at — a clean long-horizon
agentic loop — is the axis the 23-task score cannot see. Worth remembering as a
pattern: *a mediocre aggregate score plus a spotless round 5 is not the same
model as a mediocre score alone.*
Served config (VERIFIED end-to-end, not predicted): -c 262144 -ncmoe 24
--tensor-split 68,32 -b 1024 -ub 256 --no-mmap, port 9122, 15.70 t/s, cold
load 30.4s, free 1433/2422 MiB, ~15.5GiB host RAM. Alias laguna-s — NOT
laguna-s-2.1, which is the pre-existing poolside CLOUD provider in
models.json (full precision, their hardware). Benchmarking the wrong one
would look like a spectacular quant result.
laguna-xs-2.1 and [[model-triage-checklist]] both said --n-cpu-moe spill is
not a way out: "31GB host RAM with ~19GB in use, and 10-of-256 expert routing
per token means CPU-resident experts get hit constantly." **Measured, it is
fine: 7.4-8.1 GiB spilled to host RAM, 24 t/s decode** — not the ~10 t/s
that made [[kimi-dev-72b]] and [[devstral-2-123b-iq1s]] ungradeable. The whole
grill completed. *Spill cost ~2.5x throughput, not viability.*
Also: the "19GB in use" that made RAM look scarce was **an idle ollama holding a
retired gpt-oss-20b at keep_alive: -1** (see [[ollama-keepalive-ram-pin]]) —
a fixable leak, not a real constraint. Check what is actually resident before
concluding RAM is short.
-ncmoe / --tensor-split trap (cost the most time)-ncmoe N strips MoE from the first N layers, making them nearly weightless
(0.05 GiB vs 0.72 GiB). --tensor-split still divides by layer count, so an
even-looking split is wildly uneven in bytes. At -ncmoe 12 --tensor-split 48,52
(inherited from XS) CUDA1 got layers 23-47 = 25 full layers = **18.1 GiB on a
16 GiB card**. The aggregate fit at 27.0 GiB; only the split was wrong.
Two asymmetries load CUDA0 that layer-count ignores:
1. Non-layer tensors (embeddings/output, 0.32 GiB) all land on CUDA0.
2. KV follows full-attention layers, not layer count. Laguna is 1-in-4 full
attention (layers 0,4,8,...), so the first-N-layers half holds 8 of 12 of
them — KV splits ~2:1, not by layer count. Compounds with a measured
~1069 MiB fixed CUDA0 overhead (compute buffers + ctx + logits) vs
~-146 MiB on CUDA1.
Calibrating that overhead from one observed run made the model predictive to
4-12 MiB. Working configs (34.6-34.7 GiB weights, 32k ctx, q8_0 KV):
| quant | -ncmoe | --tensor-split | CUDA0 free | CUDA1 free | CPU | t/s |
|---|---|---|---|---|---|---|
| IQ2_XXS | 13 | 63,37 | 1183 MiB | 2548 MiB | 8.09 GiB | 21-23 |
| IQ2_M | 12 | 60,40 | 1239 MiB | 1818 MiB | 7.41 GiB | 24-25 |
--no-mmap is correct here (llama.cpp warns): CPU tensors become anonymous RAM
instead of file-backed, and there is headroom once ollama is not squatting.
Do not pick IQ2_XXS. IQ2_M costs 76 MiB (+0.2%) and is measurably better.
The two GGUFs have byte-identical expert tensors (IQ2_XXS x92 + IQ3_XXS x45
+ IQ4_XS x2 + IQ2_S x2 in both — 92% of weights, +0 MiB). All 76 MiB buys
attention Q5_K->Q6_K (94 tensors) and output head Q4_K->Q5_K. So the A/B
isolates attention/output precision with expert precision held constant.
``
IQ2_XXS round1: 5, 6, 5 mean 5.33/8 full grill 20/23 R5 6/6 0-loop 0/34-redundant
IQ2_M round1: 6, 7, 7 mean 6.67/8 full grill 21/23 R5 6/6 0-loop 0/34-redundant
`
The result that matters — two failure modes, cleanly separated:
3/3 FAIL -> 3/3 PASS. Experts identical, so this failure was*purely* attention/output-head precision. A discrimination failure, echoing
[[ternary-quant-costs-little-on-vision]]: extreme quant degrades
DISCRIMINATION, not knowledge.
3/3 FAIL in BOTH (three different errors: unexpected token / -2**2 precedence / unexpected character). Untouched by attention precision
=> it lives in the 2-bit experts. Next real expert step is IQ3_XXS at
41.2 GiB — unservable on a 31 GiB pool. **No quant of this model that fits
this box will fix expr_eval.**
Rounds 2-5 were identical across quants (5/5, 5/5, 5/5, R5 6/6). The entire
21-vs-20 gap is round 1, whose own spread is 5-7/8.
~21.7/23 projected, but it needs **both GPUs entirely plus 7.4-15.5 GiB host
RAM** at 15-24 t/s. [[pocket-35b]] scores 22/23 + R5 6/6 at 102 t/s fully in
VRAM; [[coder-agentic-default-driver]] does 31.0/33 with genuine 237k retrieval.
On score-per-resource it buys nothing and costs the whole box. That case still
stands — it is why this is a *trial*, not a promotion.
The single most expensive mistake of the session, and a repeat of what
[[laguna-xs-2.1]] already recorded ("262144 is unreachable at ANY split ...
2104.70 MiB compute buffer, not KV"). I quoted that memory and then walked into
it anyway.
I calibrated per-card overhead at 32k (CUDA0 +1069 MiB, CUDA1 -146 MiB),
which predicted the *next 32k config* to within 4-12 MiB — genuinely
accurate, which is what made it seductive. Extrapolated to 256k it was garbage:
the load died in 30s on allocating 1323.17 MiB on device 1 ... failed to
allocate compute pp buffers. That term is ~0 at 32k and ~1.3 GiB per card
at 256k.
Rule: never predict a high-context fit from a low-context calibration. The
buffer scales with BATCH, so -b 1024 -ub 256 (from 2048/512) roughly
halves it and was load-bearing — cf. [[north-mini-code]], which needed
-ub 256 for the same class of reason (there it was the logit buffer; laguna's
vocab is only 100352, so here it is the pp activations).
Measured context ladder (all loaded and generation-tested, not modelled):
| ctx | -ncmoe | split | -b/-ub | t/s | VRAM free | host RAM left |
|---|---|---|---|---|---|---|
| 32k | 12 | 60,40 | 2048/512 | 24.5 | 1239/1818 | ~24 GB |
| 131k | 16 | 62,38 | 2048/512 | 20.1 | 1260/1353 | 16 GB |
| 131k | 14 | 60,40 | 1024/256 | 22.1 | 610/817 (tight) | 17 GB |
| 256k | 24 | 68,32 | 1024/256 | 15.7 | 1433/2422 | 9 GB |
256k costs ~22% throughput and ~6 GB more host RAM than 131k. **Host RAM, not
VRAM, is the binding constraint at 256k** — 9 GB left for everything else.
Note 256k is reachable here *only because of the spill*: laguna-xs could not
load 262144 at any split, because it had no way to move weights off the GPU.
Genuinely good, and worth remembering: R5 **6/6, 0 loops, 0/34 redundant on
BOTH quants** — two independent full grills, 0/68 redundant calls combined. That
is the axis that killed [[north-mini-code]], coder and kimi-linear. Its
long-horizon agentic loop is clean.
[[single-run-scores-are-unreliable]] first1. "Leanest thinking model on the box, 10,018 tokens for the whole grill."
False — that was the lowest of three. Round-1 totals ran **4,326 / 34,968 /
18,651 (8.1x spread)**. Corrected: ~4x leaner than XS in tokens but 3.75x
slower per token = rough wall-clock parity, not the 2.5x win claimed.
2. Runaways. 3 of 24 XXS task-runs hit the exact 16,000-token cap; IQ2_M 2
of 24 — including on tasks it *passed*. Same signature that rejected
[[gemma-4-26b-a4b-moe]]. Higher attention precision fixed a correctness mode,
not the verbosity instability.
(LLM_ARCH_LAGUNA) covers S-2.1 as well as XS — same string, nonew llama.cpp work. Clean on the jinja guard; round 3 5/5 both quants, so the
[Patch: Laguna] tool parser works out of the box, no chat-templates/ patch.
MoE = 31.99 of 34.63 GiB (92%), ~0.674 GiB/layer; blk.0 is dense (no experts).
are full-attention — cf. [[kv-sizing-full-attention-layers]].
mandatory (GGUF template defaults enable_thinking=false). (IQ2_XXS), bench/results-laguna-s-iq2m-20260803-152627.log (IQ2_M).
pgrep -f / pkill -f match any process whose cmdline contains the pattern
— including the script doing the matching. pkill -f 'port 9190' killed its
own launcher shell before exec; a wait loop on pgrep -f 'grill_round[0-9].py'
blocked forever on itself because its own body contained that literal string.
Match the real process: pgrep -af 'python3 grill_round' | grep -v 'bash -c'.
The grill said 21/23 + R5 6/6 + 0 loops. Real use said otherwise, and none of
these were visible to the harness:
1. ~30% of Claude Code requests returned EMPTY bodies (9-36B after 5-6
MINUTES) because the entry served poolside's temp 1.0 / top-p 1.0 -- a
sampler the grill never exercises, since it always sends its own. Fixed by
temp 0.6, but see [[grill-does-not-validate-real-use]]; this is the general
lesson and it outlives the model.
2. --reasoning-budget does NOT fix that -- it only engages ABOVE its own
value, so 4096 does nothing for a client sending max_tokens=400. Measured
floor after fixing: MIN CLIENT max_tokens = 1200.
3. Prompt processing is the real tax at long context. ~708 tok/s, so a
183k-token agent prompt costs ~4 MINUTES before the first token, repaid
on every cache miss. Inherent to CPU-resident experts. This, not decode
speed, is what makes a spilled MoE painful for agentic work.
4. Context whiplash: dropped 256k->131k to relieve RAM pressure while
misdiagnosing the empty responses as "slow", which then broke a real 183k
job with "request exceeds available context size". Diagnose before
re-tuning; the RAM was never the problem.
Final verdict: the cost case in "why I said reject" was correct. 15.7 t/s,
both GPUs, 15.5GiB host RAM and a 4-minute prompt tax, to score under
[[pocket-35b]] (22/23 at 102 t/s, fully in VRAM). Its one genuine strength --
a spotless long-horizon agentic loop -- never got to matter, because the
per-turn latency made long-horizon work impractical.
Would try again only if: a sparse MoE lands that fits in VRAM outright, or
this box gets enough RAM/bandwidth that the prompt-processing tax disappears.
**(SUPERSEDED 2026-09-01 — retried on the third route neither condition
anticipated: the servable ENVELOPE grew. See the top of this file.)**
The -ncmoe technique itself is proven and reusable -- see the config table
above.
Weights had been parked on /mnt/wd1tb (dead USB HDD, [[storage-tiers]]); that
drive died and was replaced by /mnt/ssd1700, a genuinely fast USB SSD
(~1.8 GB/s). Re-downloaded byte-exact from unsloth/Laguna-S-2.1-GGUF
(UD-Q3_K_M), CUDA_DEVICE_ORDER=PCI_BUS_ID added per
[[gpu-card-assignment-policy]] (tensor-split 76,24 left UNCHANGED), uncommented
as laguna-s in llama-swap.yaml, and verified standalone: loaded in ~36s,
free VRAM 2882/3369 MiB, generated correctly at the documented temp 0.6 /
-rea on settings. HUP-reloaded into production llama-swap`. Not
re-grilled beyond the standalone smoke test. Note: the entry's own
"measured at ncmoe=33" split-bisection table labels its free-MiB columns
"(5060Ti/4060Ti)" in a way that doesn't cleanly reconcile with the rest of
the entry's card assumptions — flagged but not resolved during the
2026-09-11 re-enable; re-derive from a fresh sweep if the split ever needs
re-tuning rather than trusting that table's card labels.
# ===== RE-ENABLED 2026-09-11 — weights moved off the dead /mnt/wd1tb HDD
# onto /mnt/ssd1700 (fast USB SSD, confirmed non-rotational, ~1.8 GB/s
# read; re-downloaded byte-exact from unsloth/Laguna-S-2.1-GGUF UD-Q3_K_M).
# This was a genuine storage-tier block, same reasoning as gpt-oss-120b
# above: the -ncmoe spill path pages experts from disk on EVERY forward
# pass, and wd1tb's ~120-140 MB/s HDD was ~20x slower than the NVMe this
# entry was measured on ([[storage-tiers]], [[laguna-s-2-1-cpu-spill]]).
# Re-verify decode tok/s on ssd1700 against the context ladder below.
#
# CUDA_DEVICE_ORDER=PCI_BUS_ID ADDED (box-wide convention since 2026-09-01,
# [[gpu-card-assignment-policy]]) and --tensor-split 76,24 is DELIBERATELY
# UNCHANGED — do not invert it. The policy doc tested inverting on this
# exact entry class and it was wrong (the ~3.2 GiB of non-layer tensors
# follows CUDA0 regardless of which physical card that now is). CUDA0 is
# now the 5060 Ti, so the 76% share moves onto it.
# NOTE: the "measured at ncmoe=33" split-bisection table below labels its
# columns "(5060Ti / 4060Ti)", which does not match this entry's own
# tensor-split-direction assumptions elsewhere and was likely written
# loosely — do not trust the per-card labels in that table, only the
# free-MiB numbers and which split they came from. Re-verify empirically
# rather than reasoning further about which card held what.
"laguna-s":
aliases: [laguna-s-2.1-q3km-262k]
# Laguna-S-2.1, 118B MoE (256 experts top-10 + 1 shared, 48 layers), UD-Q3_K_M
# 50.3 GiB. RE-TRIED 2026-09-01 after the 2026-08-03 trial was retired
# ([[laguna-s-2-1-cpu-spill]] — that entry's weights were deleted).
#
# *** ALIAS IS `laguna-s`, NEVER `laguna-s-2.1` *** — the latter is poolside's
# CLOUD model in models.json (full precision, their hardware, 229376 ctx).
# Benchmarking the wrong one would look like a spectacular quant result.
#
# WHY THE RETRY: the August note concluded "next real expert step is IQ3_XXS at
# 41.2 GiB — unservable on a 31 GiB pool", which ruled out every quant that
# could fix its known defect. That reasoning treated host RAM as the ceiling.
# It was WRONG: [[gpt-oss-120b-cpu-moe-spill]] served 59.0 GiB on this box, so
# 50.3 GiB is comfortably in range. This build's bulk experts ARE IQ3_XXS.
#
# ROLLING SWA IS SAFE HERE — DO NOT ADD --swa-full. gpt-oss-120b needs that
# flag or it makes an illegal CUDA access and Xid-31s the 4060 Ti, and Laguna
# is also a sliding-window arch (36 of 48 layers, window 512), so it was the
# obvious worry. Tested directly on R5 interrupt_replan, the reproducer that
# crashed gpt-oss 5/5:
# rolling n=3 PASS/PASS/PASS, 0 redundant, ZERO crashes
# --swa-full n=1 PASS, 0 redundant (identical behaviour, no benefit)
# --swa-full costs 4x KV here (12 of 48 layers are full-attn, vs 18 of 36 on
# gpt-oss): 7.28 -> 29 GiB at 262144. It would make this window impossible and
# buys NOTHING. The gpt-oss bug does not transfer.
#
# --tensor-split 76,24 IS LOAD-BEARING and was bisected, not guessed. With
# -ncmoe, CUDA1's share is fixed by the SPLIT ALONE — raising ncmoe frees only
# CUDA0 (measured: ncmoe 33->34 left CUDA1 at exactly 804 MiB and gave all
# 996 MiB to CUDA0). So a tight CUDA1 can ONLY be fixed by raising A.
# Measured at ncmoe=33 / 262144, free MiB (card labels uncertain, see note above):
# 70,30 CANNOT LOAD
# 74,26 free 804/2470 10.07/10.87 tok/s too tight on one card
# 76,24 free 1878/1394 10.22/10.78 <- this entry
# 78,22 free 2954/ 318 10.26/10.03 too tight on the other card
# 80,20 CANNOT LOAD
# One expert layer is 995 MiB = ~2 points of A, which is why the window is
# this narrow. cuBLAS died at 466 MiB free on gpt-oss, so ~1.4 GiB is the
# target floor, not zero.
#
# *** BATCH IS WORTH 2.7x ON PREFILL — DO NOT SHRINK IT WITHOUT RE-TESTING ***
# Measured 2026-09-01 on one 11,539-token prompt, fresh server per arm:
# ctx 262144 -b 512 -ub 128 36.7 tok/s (314s) free 1878/1394
# ctx 262144 -b 2048 -ub 512 100.5 (115s) free 1650/1156
# ctx 131072 -b 2048 -ub 512 114.8 (100s) free 2834/2522 <- this entry
# ctx 65536 -b 2048 -ub 512 125.1 (92s) free 3426/1012
# BATCH dominates (2.7x); CONTEXT is minor (1.24x across a 4x range) and there
# is NO paging cliff, just ~10% per halving. The original entry shipped
# -b 512 -ub 128 because ncmoe=32 had failed on compute buffers at -b 1024
# -ub 256 — but that limit does NOT apply at ncmoe=33/31, and the setting was
# carried over without re-testing. It cost 2.7x prefill for nothing.
#
# 131072 rather than the native 262144: at 262k the big batch still fits but
# leaves only 1156 MiB on the tight card (cuBLAS died at 466 on gpt-oss), and
# prefill is 14% slower. 131k is faster AND has 2.5 GB free on BOTH cards.
# 65k is 9% faster again but drops one card to 1012 MiB — worse margin for
# less window. Decode is ~10.5 tok/s at every context (paging-bound), so the
# window costs nothing in decode; it costs PREFILL, which is the agent metric.
#
# PREFILL IS THE BINDING CONSTRAINT FOR AGENT USE, not decode. At 114.8 tok/s
# a 30k-token turn still costs ~4.4 min before the first token, repaid on
# every cache miss. This is the same tax that retired the model in August.
#
# (Historic: the compute buffer scales with BATCH and at 262144 it fails first —
# ncmoe=32 at -b 1024 -ub 256 died on "failed to allocate compute pp buffers".
# That is real, but it binds at ncmoe=32, NOT at 31/33. See the batch table above.)
#
# CONTEXT LADDER (all rolling SWA, q8_0 KV, measured — none modelled):
# 262144 ncmoe 33 76,24 10.2-10.8 tok/s native max (GGUF says 262144)
# 229376 ncmoe 32 74,26 9.97-11.29
# 204800 ncmoe 31 74,26 10.88-11.16
# 131072 ncmoe 31 76,24 10.51 <- this entry
# 65536 ncmoe 28 76,24 11.57
# Host side at ncmoe=33 is ~32.8 GiB against ~27 GB of RAM, so ~6 GiB pages
# off disk continuously. That is why decode is flat ~10-11 across the ladder:
# the binding constraint is expert paging, not context.
#
# *** CLIENT max_tokens FLOOR IS ~4000, NOT THE 1200 THE AUGUST NOTE RECORDS ***
# Measured 2026-09-01, n=2 per rung, identical one-line prompt:
# 1400 0/2 emit content (finish=length; one returned content_len=0 with
# 4606 chars of reasoning_content -- ALL budget
# went to the analysis channel)
# 2000 2/2 used 1136 / 1153 tok
# 4000 2/2 used 1894 / 1310
# 8000 2/2 used 1964 / 1398
# Natural usage spans 1136-1964 tok (1.7x) on the SAME prompt and never exceeds
# ~2000 even when given 8000 -- so a generous cap costs NOTHING, while a tight
# one truncates INTERMITTENTLY. That intermittency is the trap: it looks like a
# broken model, not a truncated one, and is very likely what the August trial
# logged as "~30% of Claude Code requests returned EMPTY bodies" (attributed
# then to the sampler). Set client max_tokens >= 4000.
#
# SAMPLER: --temp 0.6 is MANDATORY, not taste. Poolside's native temp 1.0 /
# top-p 1.0 returned EMPTY BODIES on ~30% of real Claude Code requests in the
# August trial (9-36 bytes after 5-6 MINUTES), and the grill never caught it
# because it always sends its own sampler. Also MIN CLIENT max_tokens = 1200;
# --reasoning-budget does NOT fix a short cap, it only engages above its own
# value. -rea on is mandatory (the GGUF template defaults enable_thinking=false).
#
# NOT YET GRILLED beyond R5 interrupt_replan x4. The August verdict (retired on
# score-per-resource: it costs the whole box while pocket-35b does 22/23 at
# 102 tok/s fully in VRAM) has NOT been overturned — only the quant ceiling
# that blocked a fair retest has. Its known 2-bit defect (expr_eval, which the
# August A/B localised to the experts) is the thing to re-test first.
cmd: |
/usr/bin/env
CUDA_DEVICE_ORDER=PCI_BUS_ID
${llama_bin}
-m /mnt/ssd1700/models/laguna-s-2.1/UD-Q3_K_M/Laguna-S-2.1-UD-Q3_K_M-00001-of-00003.gguf
--alias laguna-s
--jinja -rea on
-ngl 99 --n-cpu-moe 31 -c 131072 -fa on
--tensor-split 76,24
-b 2048 -ub 512
--temp 0.6
--cache-type-k q8_0 --cache-type-v q8_0
--host 127.0.0.1 --port 9178 --parallel 1
proxy: http://127.0.0.1:9178