Skip to content

Commit 7f8e3e4

Browse files
User Nameclaude
andcommitted
Add MiniMax-M2.7-NVFP4 (N=5, TP=2): temp serving-trap + exhaustive-completer findings
- harness.py: add --top-p/--top-k plumbing + retain reasoning_content across turns (interleaved-thinking models); run_microbench.sh: BENCH_TEMP/TOP_P/TOP_K env overrides (default temp=0.3 unchanged for other models). - Finding 1: MiniMax-NVFP4 runs away (repetition loop -> model_exceeded_max_tokens) at the bench default temp=0.3 (74% of coding cells; testwrite 10/10); at the model-card temp=1.0/top_p=0.95/top_k=40 the SAME cells on the SAME 131072 cap are 0/10 runaway, 58/60 done_signal. Clean A/B => sampling, not capability, not cap. - Finding 2: exhaustive-completer temperament. Aggregate 7/12 (35/60). p2 analysis 20/20; scope-constrained coding 0/5 (testwrite+refactor edit files told to leave alone); p3_market ctx-exhaustion. Complementary to 397B's surgical restraint. - Power: TP=2 heaviest simultaneous draw (median ~896W active, peak 1089W=91% cap, both GPUs >400W 64% of samples) vs 397B pipeline 670W/985W never >1000W. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent fd27f37 commit 7f8e3e4

5 files changed

Lines changed: 192 additions & 6 deletions

File tree

hardware-tests/qwen3.5-397b-vs-step3.7-flash-2026-05-29/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Step-3.7-Flash-NVFP4 entry on the same box.
2121

2222
## Files
2323
- [findings.md](findings.md) — N=10 scorecard + headline findings + power + cross-model qualitative.
24+
- [findings-minimax-m2.7.md](findings-minimax-m2.7.md)**MiniMax-M2.7-NVFP4 (N=5, vLLM TP=2):** the
25+
temp=0.3→1.0 serving-trap (0 runaways at spec vs 74% at default), the "exhaustive completer" temperament
26+
(p2 analysis 20/20, scope-constrained coding 0/5, p3_market ctx-exhaustion), and the heaviest TP=2 power draw.
2427
- [findings-n10.md](findings-n10.md) — auto-generated replicate-stability table (flags small-N flips) + finish-reason audit.
2528
- [power-analysis.md](power-analysis.md) — dual-GPU power percentiles, pipeline asymmetry, %-of-cap.
2629
- [QUALITATIVE.md](QUALITATIVE.md) — behavioral analysis beyond pass/fail (token economy, packaging,
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# MiniMax-M2.7-NVFP4 on 2× RTX PRO 6000 Blackwell — microbench N=5 (+ the temp serving-trap)
2+
3+
MiniMax-M2.7 (230B-A10B MoE), served as **NVFP4 on vLLM tensor-parallel (TP=2)**, run through the MMBT
4+
12-family agentic microbench. Added to the [397B vs Step-3.7-Flash entry](findings.md) as a fifth model.
5+
6+
**N=5** per cell (60 cells). Comparators in the main entry are N=10 (397B) / N=1 (Step, 27B-Q4, Coder-Q4) —
7+
so MiniMax is **N=5, an asymmetry to read with the same caution this entry already documents for small N.**
8+
9+
## TL;DR — two findings, the first is the bigger one
10+
11+
1. **A serving trap, not a capability gap (the headline).** Run at the bench's cross-model default
12+
**`temperature=0.3`**, MiniMax-NVFP4 looked *broken* on coding: it ran the agent loop correctly for
13+
~29 iterations, then on the **final text turn entered a repetition loop that generated tens of
14+
thousands of tokens to the `max_tokens` cap** (`finish_reason: model_exceeded_max_tokens`). Across the
15+
two coding families the temp=0.3 run reached before it was stopped: **14/19 cells ran away (74%)**
16+
`p1_bugfix` 4/9, `p1_testwrite` **10/10**. Re-run at MiniMax's **model-card sampling
17+
(`temperature=1.0, top_p=0.95, top_k=40`)**, the *same cells on the same 131072 cap* produce **0/10
18+
runaways** — clean `done_signal`. Across all **60** N=5 cells: **58 `done_signal`, 0 runaways**, 2
19+
context-exhaustion. The clean A/B on the identical cell/cap proves the runaway was **sampling**, not
20+
the model and not a stingy token cap. *Greedy-ish decode on a reasoning model is a documented
21+
repetition-loop trap; MiniMax's card mandates temp=1.0.*
22+
23+
2. **MiniMax is an "exhaustive completer" — a genuinely double-edged temperament.** Its instinct is to do
24+
the maximum: fix every bug it sees, write a full test suite + audit/ADR/CHANGELOG docs, research
25+
exhaustively. This **dominates open analysis** and **sinks scope-constrained edits** (see scorecard).
26+
It is the mirror image of 397B's surgical restraint.
27+
28+
## The serving trap — before/after on the same cells
29+
30+
| family | temp=0.3 (broken) | temp=1.0 (spec) |
31+
|---|---|---|
32+
| p1_bugfix | 5 `done_signal` / **4 runaway** (44%) | **5 `done_signal` / 0 runaway** |
33+
| p1_testwrite | 0 / **10 runaway (100%)** | **5 `done_signal` / 0 runaway** |
34+
| all 60 cells (spec) || **58 `done_signal`, 0 runaway, 2 ctx-exhaustion** |
35+
36+
A temp=0.3-runaway cell generated **71k–106k tokens in a single final turn** (100% of the per-turn cap).
37+
That is degenerate repetition, not legitimate output — and the identically-capped 397B (also 131072,
38+
also temp 0.3-equivalent path) had **0 runaways in 240 cells**, so the cap is not the cause.
39+
40+
## Scorecard (N=5; PASS = grader verdict, majority ≥3/5 marks the family ✓)
41+
42+
| family | PASS/5 | avg iters | dominant fail-reason |
43+
|---|---|---|---|
44+
| p2_extract | **5/5**| 11 ||
45+
| p2_ci | **5/5**| 41 ||
46+
| p2_hallucination | **5/5**| 15 ||
47+
| p2_triage | **5/5**| 13 ||
48+
| p3_business | **5/5**| 18 ||
49+
| p3_doc | **4/5**| 19 | 1× word-limit |
50+
| p3_pm | **3/5**| 14 ||
51+
| p1_bugfix | 2/5 | 131 | `ruff_no_regression` ×3 (lint nits in its own new tests) |
52+
| p3_writing | 1/5 | 30 | length/quality |
53+
| p1_testwrite | 0/5 | 86 | `logalyzer_unchanged` ×5 (**scope violation**) |
54+
| p1_refactor | 0/5 | 75 | `tests_unchanged` ×5, `non_output_files_unchanged` ×2 (**scope violation**) |
55+
| p3_market | 0/5 | 51 |**ctx-exhaustion (HTTP 400)** + 3 fail |
56+
57+
**Aggregate: 7/12 families pass majority; 35/60 cells (58%).** This lands in the same **~7–8/12 band** as
58+
397B (8/12 no-think), Step-3.7-Flash, 27B-Q4 and Coder-Q4 — MiniMax **aggregate-ties the field, but the
59+
per-family *shape* is distinctive** (and that shape, not the tie, is the finding).
60+
61+
## Qualitative — the "exhaustive completer" pattern (every claim cited to graded cells)
62+
63+
- **Open analysis = pure upside. p2 is a perfect 20/20** (extract/ci/hallucination/triage all 5/5), fast
64+
(~11–41 iters). When there is no scope or length constraint, thoroughness only helps. Best-in-class here.
65+
- **Scope-constrained coding = real liability, not a grader artifact. `p1_testwrite` 0/5 and `p1_refactor`
66+
0/5**, failing on **substantive** criteria: the test-writing task says *"add tests, leave the production
67+
code unchanged"* → MiniMax rewrites the production code anyway (`logalyzer_unchanged` fails 5/5); the
68+
refactor task says *"only touch the output package"* → it modifies tests and out-of-scope files
69+
(`tests_unchanged` 5/5, `non_output_files_unchanged` 2/5). It **cannot resist improving everything**,
70+
which is exactly what these guardrails forbid. This is the **opposite of 397B**, whose surgical restraint
71+
*passes* `p1_refactor`.
72+
- **`p1_bugfix` is the in-between case (2/5):** it fixes the planted bugs correctly (the O(n²) `load()`
73+
measured 11.9s→0.57–0.63s by the grader; the `collections.Iterable` import removed; 69–82 tests pass)
74+
but trips `ruff_no_regression` (2→3, 2→5) on **unused-import nits in its own newly-written tests**
75+
a lint technicality. *Here* the binary score understates it; on testwrite/refactor it does not.
76+
- **Open-ended sinks exhaust context. `p3_market` 0/5**, with **2 cells hitting the 131072 ceiling
77+
outright (HTTP 400)** — it keeps issuing research tool-calls until the conversation won't fit. The p3
78+
analog of over-delivery.
79+
- **Structured synthesis is fine**`p3_business` 5/5, `p3_doc` 4/5, `p3_pm` 3/5. Where the deliverable
80+
is well-bounded, the thoroughness lands.
81+
- **Failure texture is its own category:** not 397B's quiet *stall* (omission), not Coder/Flash's
82+
*runaway* (over-generation cutoff) — MiniMax's misses are **self-inflicted: scope violations and context
83+
exhaustion from doing too much.** And it is **expensive** — 75–131 iters/cell on p1 (the high end of the
84+
field) vs ~11–18 on the analysis cells it aces.
85+
86+
**One-line verdict:** complementary to 397B. MiniMax aces analysis where 397B is average; 397B respects
87+
guardrails where MiniMax bulldozes them. A task-class strengths finding, not a scaling-law tie.
88+
89+
## GPU power — TP=2 is the heaviest simultaneous draw of the bench
90+
91+
| | serving | combined draw | both GPUs hot at once | crosses 1000W |
92+
|---|---|---|---|---|
93+
| **MiniMax-M2.7** | vLLM **TP=2** (NVFP4) | active-decode median **~896W**, peak **1089W (91% of 1200 cap)** | **64% of samples** (>400W each) | **yes** |
94+
| 397B | llama.cpp **pipeline** | median 670W, max 985W | rarely | **never** (0 / 3868) |
95+
96+
The cause is parallelism mode, not size: **tensor-parallel makes both GPUs compute the same layers
97+
simultaneously** (so they fire together and push toward the PSU ceiling), whereas **pipeline-parallel
98+
alternates** them (GPU0 leads, combined draw stays well under the cap). MiniMax is balanced per-GPU
99+
(320W/363W mean) — TP splits evenly — vs 397B's GPU0-leads pipeline asymmetry.
100+
101+
## Caveats (read these with the numbers)
102+
103+
- **Sampling deviation:** MiniMax ran at its **card-specified `temp=1.0/top_p=0.95/top_k=40`**, *not* the
104+
bench's cross-model `temp=0.3`. This is a deliberate, documented per-model deviation — at temp=0.3 the
105+
model is off-spec and degenerates (finding #1), so a temp=0.3 score would be meaningless. The receipt
106+
schema records `temperature` but **not** `top_p`/`top_k`; the full profile is recorded here and in the
107+
launch command.
108+
- **N=5 vs comparators' N=10/N=1** — directional. The per-family rates above (esp. the 0/5 and 5/5 ones)
109+
are stable enough to characterize, but treat the aggregate as ±1 family.
110+
- **131072 context** (vs Step/27B/Coder at 262144) — a real cap asymmetry, footnoted; it did not cause the
111+
runaways (those were sampling) but it is where `p3_market` exhausted context. MiniMax's native max is ~196k.
112+
- **NVFP4 on Blackwell SM120** is a maturity-rough surface (vLLM has open repetition/kernel issues for
113+
MiniMax-NVFP4); we did **not** use `--enable-expert-parallel` (the named trigger in vLLM #31856).
114+
- This build emits **no separate `<think>`/`reasoning_content`** despite MiniMax-M2 being an
115+
interleaved-thinking model — so the interleaved-thinking history requirement was moot here (the harness
116+
retains `reasoning_content` defensively regardless).
117+
118+
## Reproduce
119+
```bash
120+
# Serve (vLLM TP=2, NVFP4). minimax_m2 parsers; no expert-parallel.
121+
docker run -d --name vllm-minimax --gpus all --shm-size 16g -e NCCL_P2P_DISABLE=1 \
122+
-v $HOME/models:/models:ro -p 127.0.0.1:8001:8000 vllm/vllm-openai:latest \
123+
--model /models/nvidia-MiniMax-M2.7-NVFP4 --served-model-name minimax-m2.7 \
124+
--tensor-parallel-size 2 --gpu-memory-utilization 0.92 --quantization modelopt \
125+
--trust-remote-code --max-model-len 131072 --disable-custom-all-reduce \
126+
--tool-call-parser minimax_m2 --reasoning-parser minimax_m2 --enable-auto-tool-choice \
127+
--host 0.0.0.0 --port 8000
128+
129+
# Run at the model card's sampling (BENCH_* env overrides the default temp=0.3):
130+
BENCH_TEMP=1.0 BENCH_TOP_P=0.95 BENCH_TOP_K=40 \
131+
bash tooling/scripts/run_microbench.sh minimax-m2.7 8001 minimax-m2.7-spec 5 "" "" 131072
132+
bash tooling/scripts/grade_microbench.sh minimax-m2.7-spec
133+
```

hardware-tests/qwen3.5-397b-vs-step3.7-flash-2026-05-29/findings.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,26 @@ analyzed with `tooling/bench_power.py`. 3,868 paired samples.
153153
the cards and fire in sequence, so the two rarely peak simultaneously. Combined with the CPU-bound tool
154154
phases, this is *why* the rig never thermally stresses on this workload.
155155
- **Why it matters:** the "both GPUs at 600W = 1200W" worst case simply does not occur for single-stream
156-
agentic inference under pipeline parallelism. (The MiniMax-M2.7 run uses vLLM **tensor**-parallel, which
157-
fires both GPUs per token — the expected contrast is a higher, more simultaneous combined draw; that
158-
comparison is added when the MiniMax run lands.)
156+
agentic inference under pipeline parallelism. (Contrast confirmed by the MiniMax-M2.7 run, which uses
157+
vLLM **tensor**-parallel: both GPUs fire per token, combined active-decode draw **median ~896W, peak
158+
1089W (91% of cap), both GPUs >400W in 64% of samples, crossing 1000W** — the heaviest simultaneous
159+
draw of the bench. TP-vs-pipeline topology, not model size, drives it. See
160+
[findings-minimax-m2.7.md](findings-minimax-m2.7.md).)
161+
162+
## MiniMax-M2.7-NVFP4 (added — N=5, vLLM TP=2) — see [findings-minimax-m2.7.md](findings-minimax-m2.7.md)
163+
164+
Two findings, full detail in the linked doc:
165+
1. **A temperature serving-trap, not a capability gap.** At the bench's default `temp=0.3`, MiniMax-NVFP4
166+
runs the agent loop correctly then degenerates into a repetition loop on its final turn
167+
(`model_exceeded_max_tokens`): **14/19 coding cells ran away (testwrite 10/10)**. At the model card's
168+
`temp=1.0/top_p=0.95/top_k=40`, the *same cells on the same 131072 cap* are **0/10 runaway**; all 60
169+
N=5 cells: **58 `done_signal`, 0 runaway**. Clean A/B → sampling, not the model, not the cap.
170+
2. **An "exhaustive completer" temperament.** Aggregate **7/12 (35/60 cells)** — ties the ~7–8/12 band —
171+
but the shape is distinctive and **complementary to 397B**: **p2 analysis a perfect 20/20** (thoroughness
172+
pure upside), **scope-constrained coding 0/5 on both testwrite & refactor** (it edits files it was told
173+
to leave alone — real over-reach, opposite of 397B's surgical restraint), `p3_market` exhausts context
174+
(2× HTTP 400). *Deviation footnote: MiniMax alone runs temp=1.0 (off the temp=0.3 cohort) — justified
175+
because temp=0.3 is off-spec for it; and N=5 vs the cohort's N=10/N=1.*
159176

160177
## Cross-model qualitative (vs 27B / Coder-Next at Q4)
161178

tooling/harness.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def execute_tool(name, args, log_dir, require_files=None, require_git_tag=False)
337337

338338
def agent_loop(api_url, model, system_prompt, task, log_dir, max_iters=10000,
339339
max_completion_total=10**12, max_model_len=262144,
340-
stuck_threshold=30, temperature=0.0,
340+
stuck_threshold=30, temperature=0.0, top_p=None, top_k=None,
341341
require_files=None, require_git_tag=False, reasoning_effort=None,
342342
enable_thinking=None):
343343
"""Run the agent until done() or limits hit. Returns final state dict."""
@@ -377,6 +377,13 @@ def agent_loop(api_url, model, system_prompt, task, log_dir, max_iters=10000,
377377
"max_tokens": max_tokens_safe,
378378
"stream": False,
379379
}
380+
# Optional nucleus / top-k sampling. Some models specify a required operating point
381+
# (e.g. MiniMax-M2 card: temperature=1.0, top_p=0.95, top_k=40) and degenerate into
382+
# repetition loops under greedy decode. Sent only when set; seed=42 keeps it reproducible.
383+
if top_p is not None:
384+
payload["top_p"] = top_p
385+
if top_k is not None:
386+
payload["top_k"] = top_k # vLLM OpenAI server accepts top_k as a top-level sampling param
380387
# Top-level chat_template_kwargs are passed through to the template render
381388
# (vLLM and llama.cpp --jinja both honor them). Each key is a no-op for
382389
# models whose template doesn't reference it.
@@ -445,8 +452,17 @@ def agent_loop(api_url, model, system_prompt, task, log_dir, max_iters=10000,
445452
finish_reason = f"model_exceeded_max_tokens_{max_tokens_safe}"
446453
break
447454

448-
# Append assistant message to history (preserve tool_calls so the model sees its own tool intents)
455+
# Append assistant message to history (preserve tool_calls so the model sees its own tool intents).
456+
# Interleaved-thinking models (e.g. MiniMax-M2, served with --reasoning-parser minimax_m2) split
457+
# the <think> block out into a separate reasoning_content field. The MiniMax card REQUIRES
458+
# retaining it across turns: "Do not remove the <think>...</think> part, otherwise the model's
459+
# performance will be negatively affected" (their numbers: -36% tau2, -40% BrowseComp when prior
460+
# thinking is stripped — state drift + weakened self-correction on long-horizon toolchains, which
461+
# is exactly a clean agentic run that derails on a final turn). Pass reasoning_content back so the
462+
# template re-renders the prior thinking. No-op for models that don't emit the field.
449463
assistant_msg = {"role": "assistant", "content": msg.get("content") or ""}
464+
if msg.get("reasoning_content"):
465+
assistant_msg["reasoning_content"] = msg["reasoning_content"]
450466
if msg.get("tool_calls"):
451467
assistant_msg["tool_calls"] = msg["tool_calls"]
452468
messages.append(assistant_msg)
@@ -561,6 +577,12 @@ def main():
561577
"At temp=0 with seed=42, models can fall into fixed-point loops on long-horizon "
562578
"tasks (same context → same response → same tool result → same response). "
563579
"0.3-0.5 is typical for agentic work and breaks these traps without much off-task drift.")
580+
ap.add_argument("--top-p", type=float, default=None,
581+
help="Nucleus sampling top_p, sent only when set. Pair with --temperature/--top-k to "
582+
"match a model's specified operating point (e.g. MiniMax-M2: temp=1.0 top_p=0.95 "
583+
"top_k=40). Leave unset to use the server default.")
584+
ap.add_argument("--top-k", type=int, default=None,
585+
help="Top-k sampling, sent only when set (vLLM extension). See --top-p.")
564586
ap.add_argument("--stuck-threshold", type=int, default=30,
565587
help="Iterations of unchanged workspace state hash before the harness aborts the run. "
566588
"Default 30 was tuned on the memo/board/code tasks (whole job fits in ~100 iters, "
@@ -705,6 +727,7 @@ def main():
705727

706728
summary = agent_loop(api_url, args.model, system_prompt, task, log_dir,
707729
max_iters=args.max_iters, temperature=args.temperature,
730+
top_p=args.top_p, top_k=args.top_k,
708731
stuck_threshold=args.stuck_threshold,
709732
require_files=require_files,
710733
require_git_tag=bool(args.require_git_tag),

tooling/scripts/run_microbench.sh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ MAXLEN="${7:-}" # optional: served context window (e.g. 131072 for t
6565
MAXLEN_FLAG=""
6666
[ -n "$MAXLEN" ] && MAXLEN_FLAG="--max-model-len $MAXLEN"
6767

68+
# Sampling overrides via env (default keeps the cross-model temp=0.3 protocol). Some models
69+
# specify a required operating point and loop under low temp — e.g. MiniMax-M2 card mandates
70+
# temperature=1.0, top_p=0.95, top_k=40. Set BENCH_TEMP / BENCH_TOP_P / BENCH_TOP_K to deviate;
71+
# the deviation is recorded per-run in receipt.json (temperature) and must be footnoted in findings.
72+
TEMP="${BENCH_TEMP:-0.3}"
73+
TOPP_FLAG=""; [ -n "${BENCH_TOP_P:-}" ] && TOPP_FLAG="--top-p $BENCH_TOP_P"
74+
TOPK_FLAG=""; [ -n "${BENCH_TOP_K:-}" ] && TOPK_FLAG="--top-k $BENCH_TOP_K"
75+
6876
# Guard: run names + the idempotent skip check are keyed by LABEL only. If an
6977
# effort is set but not encoded in the label, a later effort would reuse the same
7078
# run names and be skipped as "already complete". Fail fast instead.
@@ -157,7 +165,9 @@ for entry in "${TASKS[@]}"; do
157165
"$TOOLING/tasks/$task_file" \
158166
--model "$MODEL" \
159167
--port "$PORT" \
160-
--temperature 0.3 \
168+
--temperature "$TEMP" \
169+
$TOPP_FLAG \
170+
$TOPK_FLAG \
161171
--stuck-threshold 500 \
162172
$REASONING_FLAG \
163173
$THINKING_FLAG \

0 commit comments

Comments
 (0)