MTPLX 2.4.0
The 35B speed release, plus the fan fix everyone hit.
The headline: 35B-A3B got a compiled decode stack
The 35B-A3B MoE model now runs a compiled decode route: target-prefix
compilation, whole-MoE fusion, GDN post-conv fusion, and a row-owned
router (thanks David Tai, #174). On top of that sits continuous batched
serving with fixed-shape cohorts, ragged KV, and AR row-packing (#200).
Single-stream serving stays exactly as fast as before; the new stack is
where the batched and 35B lanes pick up their headroom.
Fans no longer stay stuck at max (#201)
If you ran 2.3.0 in Smart fan mode, you probably heard this bug. After a
request finished, a failed fan restore was logged once and then
forgotten, so the fans could stay pinned at max while the engine sat
idle. Two real fixes:
- A restore that fails verification now retries with backoff until the
fan hardware actually reports the Apple auto curve again.
- The ThermalForge daemon socket path no longer trusts the daemon's "ok"
reply. It verifies the fans dropped, and falls back to the CLI in the
same call if they did not.
There is also a new watchdog: if a fan lease is somehow held while the
engine has been idle for two minutes, it gets dropped and logged loudly.
If you ever see that log line, please paste it on #201.
Tool calling keeps getting more honest
- A generation cut by max_tokens mid-tool-call now reports
finish_reason "length" instead of "tool_calls", so agent clients
continue correctly instead of executing a half-built call (#196/#197,
first two layers).
- The think-splitter no longer leaks reasoning into content when the
text contains bare "function=" or "parameter=" strings.
- The streaming parser now handles bracket-style and Poolside-dialect
tool calls, buffers incomplete calls instead of double-delivering
them, and passes through calls to tools it does not recognize, per the
OpenAI contract (David Tai, #195).
Structured output can think as long as it wants, but not forever
An unclosed block inside constrained generation could legally
run to the token limit and eat the whole response. The reasoning prelude
is now bounded at 4000 characters by default, tunable via
MTPLX_THINK_PRELUDE_MAX_CHARS (0 restores the old unbounded behavior).
Your structured request now always produces the document it asked for
(Jozef Kristek, #213).
The rest
- Forge model probes recover from slow Hugging Face config responses:
longer timeout, pinned-SHA retry, and no more treating a missing
indexed config as "no MTP" (Philip John Basile, #210).
- Laguna S-2.1 support (AR-only, exact-pin oQ4e) with a catalog entry.
- Hy3 295B hardening: full-residency lane plus generic MTP contract
fixes that also caught a real bug on the Qwen lane (David Tai, #208).
- Dependency bumps: pillow 12.3.0, checkout 7.0.1, setup-python 7.0.0,
gh-action-pypi-publish 1.14.1.
Performance
Measured against released 2.3.0 on an M5 Max (27B Optimized Speed,
turbo profile, verified max fans, alternating A/B runs): decode and
prefill are flat within run-to-run noise, with the candidate reading
about nine percent faster on 8k-prompt prefill in the clean window
(564 vs 518 tok/s) and identical at 32k (622 vs 635 tok/s, both
within spread). Decode held the 58 to 61 tok/s band on both builds.
No pillar regressed.