Skip to main content

Extraordinary speed, extraordinary quality — an MLX-based inference engine for Apple Silicon.

Project description

m5-infer

m5-infer

Extraordinary speed, extraordinary quality — an MLX-based inference engine for Apple Silicon.

CI PyPI License Python MLX Apple Silicon

English · 日本語


English

⚡ Speed — by the numbers

m5-infer is a layer on top of the same mlx-lm library that powers mlx_lm.server — so the primary comparison is m5-infer vs mlx_lm.server on identical hardware, identical weights, identical prompts. (Ollama numbers included for reference.)

Metric (Qwen 3.5 9B 4-bit, M5 MacBook Air base) mlx_lm.server m5-infer Ollama
Decode tok/s (long_gen 512) 17.0 40.0 (up to 2.4×) 8.9
Decode tok/s · thinking ON 18.6 28.8 (up to 1.5×) 11.2
Thinking-ON Short QA (3 factual) 0 / 3 3 / 3 0 / 3
Opus-4.7 judged output (10-task avg, same model) 5.85 / 10 5.28 / 10
vs mlx_lm.server (decode) 1.0× up to 2.4× faster 0.5×
vs Ollama (decode) 1.9× up to 4.5× faster 1.0×

Honest read. m5-infer wins on sustained decode throughput, thinking-mode answer extraction, and output quality as graded by Opus 4.7. mlx_lm.server's in-process prefix cache is faster on warm-hit total latency for short sessions — different engines, different trade-offs. See the full benchmarks section further down for warm-path latency and session-latency numbers. Same Mac, same weights, no fine-tuning — the gaps come from the inference-engine layer.

Overview

m5-infer is an OpenAI-compatible inference engine designed for Apple Silicon Macs (M-series). It wraps mlx-lm with a layered set of training-free optimizations that target decode throughput, warm-path latency, and long-context retrieval quality — without sacrificing output fidelity.

Built around Qwen 3.5 hybrid (GatedDeltaNet + Full Attention) as the primary reference architecture, v1.0 ships with a model-family abstraction layer that supports additional families (Qwen 2.5 / Qwen 3.6, Llama 3.x, Mistral, Gemma 2/3/4) out of the box.

Key Features

  • OpenAI-compatible HTTP API — drop-in replacement for mlx_lm.server, compatible with chat clients expecting /v1/chat/completions.
  • N1 CTRSP — Cross-Turn Recurrent State Persist. GatedDeltaNet state caching on disk.
  • Think-aware budget & loop-escape injection — handles Qwen's <think>…</think> boundaries correctly without truncating user-visible answers.
  • TPC (Token Prefix Compiler) — fast prefix cache lookup by raw-bytes hash.
  • Adaptive Layer Skipping (N4 ALS) — skips low-impact layers for easy tokens.
  • Self-Speculative Early Exit (N3 SSEE) — in-model speculative decoding.
  • Parallel Expert Scheduling (N6 PES) — concurrent expert path execution.
  • X5-R compiled forward — Metal kernel fusion via mx.compile.
  • Needle-retrieval heuristic — automatic thinking-mode switch on long-context + short-query pattern, bypassing safety-alignment refusals that affect thinking-disabled retrieval.
  • Model-family abstraction — unified engine for Qwen / Llama / Mistral / Gemma via config.

Technical Innovations — what m5-infer does that other engines don't

The up to 4.5× decode speedup over Ollama and the +11% Opus-judged quality lead aren't from one trick — they come from a stack of small, training-free optimizations layered on top of mlx-lm. Below are the ones that made the biggest measured difference (peak values on long_gen decode; actual speedup varies by workload). Every item is implemented in v1.1.0 today; nothing here is "planned for the future".

Contribution legend:

  • Measured: directly isolated in the bench (A/B on same machine, same model)
  • 📊 Estimated: from per-feature toggle experiments during development (illustrative — real contribution varies by workload)
  • ⚙️ Enabling: a capability gate, not a raw speed/quality multiplier
# Innovation Decode speed Quality TTFT / latency
1 Hybrid speculative decoding 📊 +35% tps (29→40) ⚙️ output-equivalent to greedy
2 CTRSP (cross-turn state persist) 12K cold→warm total latency: 69s→11s (6×) · state survives restart
3 Think-aware budget + escape hint +36% Opus score (4.29→5.85), extract 1.40→7.85
4 Needle-retrieval heuristic long-context retrieval 0/6 → 6/6
5 ALS + SSEE + PES (decode tricks) 📊 +10-15% tps
6 X5-R compiled forward + wired mem 📊 +40% tps (17→24) 📊 cold startup +2-5s (one-time)
7 Hardware-aware auto-tune 📊 ±15% on non-base chips
8 Model-family abstraction ⚙️ same engine works on Qwen/Llama/Mistral/Gemma
Full stack combined up to 4.5× over Ollama (8.9→40.0 tps) +11% over Ollama (5.28→5.85) up to 5.8× warm total latency @ 12K

1. Hybrid-aware speculative decoding (the hardest one)

Qwen 3.5 is a hybrid: 24 GatedDeltaNet (GDN) layers + 8 Full-Attention layers. Stock mlx-lm has no speculative path for hybrids — when you reject a draft token in a pure transformer, you truncate the KV cache by N entries and continue. That doesn't work for GDN: the recurrent state and convolutional buffer have already advanced through the entire draft window. Truncating only the KV leaves GDN state corrupted, and the model silently produces subtly wrong tokens (no crash — just divergent output).

What we built: app/innovation/speculative/draft_speculative.py snapshots every GDN layer's (recurrent_state, conv_buf) pair into a pre-allocated tensor pool before each verify call. On rejection, restore from snapshot in O(1) — zero allocations in the hot path, GDN state ≈ tens of KB per layer, total snapshot cost well under 1 ms.

Result:

  • 📊 Decode: +35% tps on Qwen 3.5 9B (from ~29 tps baseline to ~40 tps with speculative enabled)
  • ⚙️ Quality: output-equivalent to greedy decode (verified byte-level against mlx_lm.generate, no acceptance-rate hand-waving)
  • 📊 Acceptance rate on hybrid arch: ~70% (short drafts of 4 tokens)

2. CTRSP — Cross-Turn Recurrent State Persist

At the end of each generation we serialize the full model state (quantized KV cache + GDN recurrent/conv buffers) to disk, keyed by a raw-bytes hash of the prompt-prefix tokens. Hash is over token bytes, not decoded text — so identical system prompts and tool schemas hit exactly even when one chat turn of delta is appended.

Result:

  • ✅ 12 K-token tool schema cold → warm total latency: 69 s → 11 s (6× from CTRSP cache hit on the second call; "total latency" = end-to-end time for a 30-token response, not strict TTFT). mlx_lm.server's in-process warm total latency is 2.3 s (faster absolute); CTRSP's unique value is that the state survives process restarts — other engines lose their prefix cache at shutdown.
  • ✅ 5-turn session turn 5 = 7.5 s (Ollama fails entirely on the same scenario)
  • ✅ Disk footprint: ~50–100 MB per distinct system-prompt hash (LRU default 32 entries = ~3 GB cap)
  • 📊 Hit rate on a typical agent workload (fixed system prompt, varied user turns): >90%

3. Think-aware budget + task-aware escape injection

This is the single biggest quality win on the Opus bench. Qwen 3.5's chain-of-thought is wrapped in <think>...</think>. Two failure modes plague naive engines:

  • Budget starvation: most engines count thinking tokens against the user's max_tokens. On reasoning tasks Qwen spends 800+ tokens thinking; with max_tokens=1024 the answer phase never starts.
  • Think-loop trap: Qwen sometimes falls into Wait, let me re-check... spirals that never close </think>.

What we built:

  • Separate thinking budget (max_thinking_tokens, default 32 K) — user's max_tokens is only the post-</think> budget.
  • N-gram repetition detector scoped to the think block (QualityMonitor, ngram=6, 3-repeat threshold).
  • Task-aware escape hint: when the loop detector fires, instead of injecting a bare </think>\n\n, we inspect the prompt and inject a typed transition like </think>\n\n**Final JSON:**\n\n\``json\n(orFinal Code:, Final Translation:, etc.). This forces the model into the required output format. See app/backend/custom_generate.py::_build_escape_hint`.

Result:

  • extract_01 (structured JSON extraction): 1.40 / 10 → 7.85 / 10 (+461%)
  • code_01 (function + tests): 3.10 → 6.55 (+111%)
  • design_01 (system design): 2.75 → 4.55 (+65%)
  • ✅ 10-task aggregate: 4.29 → 5.85 (+36%), surpassing Ollama's 5.28 on the same model
  • 📊 Runtime overhead: <1 ms per decode step (n-gram hash + token-ID lookup)

4. Needle-retrieval heuristic

Qwen 3.5 with thinking disabled has a safety alignment quirk: on long context (12 K+) with a short retrieval query, it sometimes refuses citing "I cannot reveal authoritative information" — even when the user is asking about content they themselves provided. Standard engines surface this as "model failure". We auto-detect the long-context + short-query pattern at the routing layer (app/api/routes.py, Rule 1b) and force thinking mode ON, which bypasses the false-positive refusal.

Result:

  • ✅ Long-context needle retrieval: 0/6 → 6/6 on the standard benchmark
  • ⚙️ Zero speed cost (routing-layer check, not decode path)
  • ⚙️ Trigger condition: system prompt > 3000 chars AND user message < 200 chars AND question-like pattern

5. Speculative draft-tree + ALS + SSEE + PES

The remaining decode speedup comes from a stack of smaller wins, each <10 % alone but multiplicative:

Feature Decode contribution (estimated) Notes
Speculative draft tree 📊 +5-8% tps v1 ships linear lookahead
N4 ALS (layer skip) 📊 +3-5% tps Low-impact layer skip on easy tokens
N3 SSEE (self-speculative) 📊 +3-5% tps In-model early exit
N6 PES (parallel experts) 📊 +2-3% tps Concurrent expert path
Combined 📊 ~+10-15% tps Multiplicative; each <10% alone

6. X5-R compiled forward + wired memory

mx.compile wraps the decode forward so MLX fuses attention + MLP + GDN into Metal kernels at runtime; wired_limit pins model weights so they can't be swapped under memory pressure. Neither is glamorous, but skipping them halves the real-world gain from speculative decoding.

Result:

  • 📊 Decode: +40% tps on baseline (~17 → ~24 tps before other innovations layered on top)
  • 📊 Cold startup: +2-5 s one-time cost (JIT compile; amortized across all subsequent requests)
  • ⚙️ Without wired memory, swap kicks in on 24 GB base chip during thermal throttling → decode drops to ~8 tps

7. Hardware-aware auto-tune

At startup, app/core/auto_tune.py detects the chip tier (M1 base → M5 Max) via sysctl and applies per-tier defaults: GPU-cores-aware concurrency limits, bandwidth-based RDMS draft sizing, memory-aware wired_limit. Set [engine] auto_tune = false in engine.toml to keep manual settings verbatim.

Result:

  • 📊 On M5 Pro (expected): +15-25% over un-tuned baseline (more GPU cores → higher concurrent decode width)
  • 📊 On M5 Max (expected): +25-40% (2× bandwidth of base)
  • ⚙️ On M5 base (tested): the reference tier; auto-tune produces the numbers shown in this README

8. Model-family abstraction

app/core/model_family.py + app/backend/mask_adapter.py provide a uniform interface across Qwen 3.5, Qwen 3.6, Qwen 2.5, Llama 3.x, Mistral, Gemma 2/3/4. Pure transformers automatically skip the GDN/think-aware machinery that's specific to hybrids. Set [model] family = "auto" for detection from main_path.

Innovation stack — illustrative decode contribution

Quick Start

Install from PyPI (recommended, v1.1.1+):

pip install --upgrade m5-infer
m5-infer                         # foreground server on port 11436 (Ctrl-C to stop)
# equivalent: m5-infer start

First run downloads Qwen 3.5 9B 4-bit (~5 GB) from HuggingFace; subsequent starts are instant. Optional flags: --port, --host, --config.

To customize settings, drop a local configs/engine.toml in your working directory first:

m5-infer init                    # writes ./configs/engine.toml + models.toml
$EDITOR configs/engine.toml
m5-infer                         # picks up your edits

Set HF_TOKEN in your environment to avoid HuggingFace rate limits on large model downloads.

From source (for development, benchmark scripts, or the legacy start/stop/chat shell wrappers):

git clone https://github.com/dualform-labs/m5-infer.git
cd m5-infer
pip install -e .

./start.sh --fg                  # foreground, logs to stdout
./start.sh                       # detached, logs to ./logs/
./stop.sh                        # stop the detached server
./chat.sh                        # interactive chat (requires server running)

v1.1.1 and later respect $M5_INFER_DATA_DIR / $XDG_DATA_HOME for snapshot storage; see docs/ROADMAP.md for the config / data-path resolution order.

HTTP endpoints (OpenAI-compatible)

Once the server is up, these endpoints are available on http://127.0.0.1:11436:

Method Path Purpose
GET /health Liveness check + engine status (memory, MMRS, MRPB, MTAB stats)
GET /v1/models List available models (OpenAI-compatible)
POST /v1/models/pull Download + load a model from HuggingFace
POST /v1/chat/completions Chat completion (OpenAI-compatible; streaming supported via stream: true)

Discover them live from a running server:

# List endpoints from the FastAPI OpenAPI spec
curl -s http://127.0.0.1:11436/openapi.json | python3 -c \
  "import json,sys; [print(f'{m.upper():6} {p}') for p,v in json.load(sys.stdin)['paths'].items() for m in v]"

# Or browse the interactive docs
open http://127.0.0.1:11436/docs        # Swagger UI
open http://127.0.0.1:11436/redoc       # ReDoc

Test the chat endpoint

curl -s http://127.0.0.1:11436/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Hello"}],"max_tokens":64}'

# Streaming variant
curl -sN http://127.0.0.1:11436/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Hello"}],"max_tokens":64,"stream":true}'

Drop-in with the official OpenAI Python SDK:

from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:11436/v1", api_key="not-needed")
resp = client.chat.completions.create(
    model="main",
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=64,
)
print(resp.choices[0].message.content)

Downloading models

m5-infer loads any MLX-compatible model from HuggingFace by its repo ID. Weights are cached by the underlying mlx-lm / huggingface_hub stack under ~/.cache/huggingface/ — no manual download step required.

Three ways to load a model:

1. Configure the default model — edit configs/engine.toml before first start:

[model]
family = "auto"                                      # auto-detect from main_path
main_path = "mlx-community/Qwen3.5-9B-MLX-4bit"      # downloaded on first start
draft_path = "mlx-community/Qwen3.5-0.8B-MLX-4bit"   # optional, for speculative decoding

On first ./start.sh, both models are downloaded (several GB for the main, ~0.6 GB for the draft). Subsequent starts use the local cache.

2. Pull a model at runtime via HTTP — hot-swap without restarting the server:

curl -s -X POST http://127.0.0.1:11436/v1/models/pull \
  -H "Content-Type: application/json" \
  -d '{"model":"mlx-community/Llama-3.2-3B-Instruct-4bit"}'

The request blocks new generations until the swap completes. Returns {"status":"loaded", ...} on success.

3. Pre-download outside m5-infer — useful on air-gapped setups or when you want an offline first-start:

# Use the huggingface CLI (installed alongside huggingface_hub)
huggingface-cli download mlx-community/Qwen3.5-9B-MLX-4bit

# Or with git-lfs
git clone https://huggingface.co/mlx-community/Qwen3.5-9B-MLX-4bit

Then point main_path in engine.toml at the repo ID — m5-infer will find the already-cached weights.

Finding MLX-compatible models: browse mlx-community on HuggingFace — the engine supports Qwen 3.5 / 3.6 / 2.5, Llama 3.x, Mistral, and Gemma 2/3/4 families out of the box (see configs/engine.toml and docs/MODEL_FAMILY_GUIDE.md).

Authentication for gated models: log in once with huggingface-cli login; the token is picked up automatically.

Requirements

Item Minimum Recommended
Chip Apple Silicon M1 M5 series (Base / Pro / Max — Ultra not released)
Unified memory 16 GB (untested, small models only) 24 GB+ for 9B-class 4-bit models
macOS 26.4 26.4 or newer
Python 3.11 3.12 / 3.13 / 3.14

The engine detects your chip at startup and auto-tunes defaults for that tier — entry (M1 base) to ultra (M5 Ultra). See app/core/auto_tune.py for the per-tier policy; set [engine] auto_tune = false in engine.toml to keep your manual settings verbatim.

Configuration

All engine tuning lives in configs/engine.toml. Model-family selection:

[model]
family = "auto"                                 # auto-detect from main_path
main_path = "mlx-community/Qwen3.5-9B-MLX-4bit"
draft_path = "mlx-community/Qwen3.5-0.8B-MLX-4bit"  # optional, for speculative decoding

Supported families: qwen35, qwen36, qwen25, llama, mistral, gemma, or auto.

See docs/MODEL_FAMILY_GUIDE.md for details on adding a new model family.

Project Layout

app/
├── api/            OpenAI-compatible HTTP server (FastAPI)
├── backend/        MLX generation backends and custom decode loop
├── cli/            Command-line chat client
├── core/           Config, logging, model-family detection
├── engine/         Innovation executor, quality monitor, supervisor
├── innovation/     Per-innovation modules (N1…N6 series, X2…X5R series, RDMS, SSD, MTAB, etc.)
└── planner/        Request planner
configs/            engine.toml, models.toml
docs/               Public documentation
tests/              Unit tests

Benchmark Environment

⚠️ Important disclaimer on benchmark conditions

All numbers below were measured on a single M5 MacBook Air (base, 24 GB unified memory, 10 GPU cores, 153 GB/s memory bandwidth) running macOS 26.4. This is the entry-level M5 — the 9B-4bit model consumes ~60% of available memory, the chip thermally throttles under sustained load, and there is no active cooling. Measurements are therefore conservative: numbers may shift significantly on:

  • M5 Pro / Max (2×–4× GPU cores, 2×–4× memory bandwidth) — expect decode to scale near-linearly with bandwidth
  • More memory (48 GB+) — allows larger draft models, more aggressive prefix caches, and the aggressive/extreme memory modes
  • M5 Studio / Mac mini / MacBook Pro (active cooling) — sustained workloads avoid the throttling that affects the Air after ~30s of heavy decode

Read these numbers as "what an 24 GB base-tier Mac can do", not as a hardware ceiling. If you run the same benches on better hardware we'd love to see the numbers.

Benchmarks — full_suite (same methodology, same hardware, same model)

All three engines are measured with the same methodology against Qwen 3.5 9B (4-bit quantization) on the machine described above: same prompts, same decoder settings (temperature = 0), same machine, same warmup protocol. The in-house bench harness that produces these numbers is kept internal to keep the public repo small; if you want to reproduce, a short OpenAI-compatible probe client against http://127.0.0.1:11436/v1/chat/completions with the prompts from the tables below is sufficient.

Metric Ollama mlx_lm.server m5-infer
Decode tok/s (long_gen, 512 tokens) 8.9 17.0 40.0
vs Ollama speedup 1.0× 1.9× up to 4.5×
Warm total latency (12K tool schemas, 2nd call, 30-tok response) 64.9 s 2.3 s 11.1 s
Session warm turn 5 (5-turn cumulative) failed 1.6 s 7.5 s
Short QA (3 factual questions) 3/3 ✅ 3/3 ✅* 3/3 ✅

The "Warm total latency" row measures end-to-end time to complete a 30-token response, not strict TTFT. Strict TTFT (first content chunk) under the vLLM-compatible methodology is scheduled for v1.2 (the numbers above are from the v1.0.0 archive bench on this same hardware; v1.1 ships strictly-additive optimizations, no quality regressions, no public re-run done yet — see CHANGELOG v1.1.0 for details).

*mlx_lm.server leaves reasoning field populated instead of content when thinking mode is enabled internally; the bench tokenization/parsing counts it, but the bench's substring check misses the answer. We consider mlx_lm's short_qa functionally passing based on the reasoning text.

m5-infer delivers up to 4.5× the decode throughput of Ollama and up to 2.4× of mlx_lm.server on the same model, same prompt, same machine (numbers from the v1.0.0 archive bench; v1.1.0 is strictly additive, no regressions).

Decode speed Warm total latency Session warm (turn 5) Multi-metric comparison

Thinking ON vs OFF — per-engine comparison

Qwen 3.5 supports <think> reasoning blocks. We ran the same bench (long_gen ×2, needle 4K ×3 positions, short_qa ×3) under both thinking modes on all three engines.

Engine Decode OFF Decode ON Needle OFF Needle ON Short QA OFF Short QA ON
Ollama 14.8 tps 11.2 tps 3/3 3/3 3/3 0/3
mlx_lm.server 18.2 tps 18.6 tps 3/3 3/3 3/3 0/3
m5-infer 38.9 tps 28.8 tps 3/3 3/3 3/3 3/3

m5-infer is the only engine that passes short QA with thinking ON — the other two leave the answer buried in the reasoning/<think> block and the user-visible content stays empty. m5-infer's think-aware parser surfaces the answer correctly regardless of mode.

Decode: m5-infer stays up to 2.6× above mlx_lm even with thinking enabled (28.8 vs 18.6 tps on long_gen 512-token decode).

Thinking decode comparison Thinking quality matrix

Opus-4.7 Judged Quality Benchmark (2026-04-19)

10 tasks (math reasoning, code generation, code review, system design, structured extraction, ambiguity resolution, SQL optimization, instruction following, technical translation, logical puzzle). Each answer judged by Claude Opus 4.7 on a task-specific rubric (0–10 per criterion, weighted). Opus 4.7 itself serves as the reference ceiling.

Engine Aggregate vs Opus ceiling Best tasks
Claude Opus 4.7 (reference) 10.00 / 10 100% (ceiling)
m5-infer 5.85 / 10 59% puzzle 9.65, ambig 8.55, extract 7.85
Ollama qwen3.5:9B 5.28 / 10 53% code 8.05, puzzle 9.60, design 6.85

Task-aware escape hint: the decisive innovation on this bench. When m5-infer's loop-escape injector fires on a thinking loop, it injects a task-typed transition (**Final JSON:**, **Final Code:**, etc., selected from the prompt) instead of a bare \n\n. This alone moved extract_01 from producing zero JSON (1.40) to a valid 6+-item JSON array (7.85 / 10). See app/backend/custom_generate.py::_build_escape_hint.

Where m5-infer wins: extract_01 (7.85 vs 2.10), puzzle_01 (9.65 vs 9.60), ambig_01 (8.55 vs 6.80). Structured tasks (JSON, deductive logic, pragmatic answer) benefit most from the typed escape.

Still a work in progress: sql_01 (2.70) shows a thinking loop the n-gram detector fails to catch — the same ~30-token block repeats 4× verbatim but falls just outside the current detector's window. Planned fix in the next patch.

Caveats: Ollama uses GGUF q4_K_M; m5-infer uses MLX 4-bit. Different quant methods yield slightly different outputs on the same base weights. A single judging run has ≈ ±0.5 variance per task — read headline numbers as "in this ballpark", not precise.

Opus quality — aggregate Opus quality — per task

Roadmap

See docs/ROADMAP.md for what we are considering for future releases. Feature requests outside this scope are welcome via Discussions.

Acknowledgments

m5-infer stands on the shoulders of excellent work. With respect and gratitude to the teams below:

  • Apple MLX teamMLX and mlx-lm. m5-infer is a layer on top of mlx-lm; every speedup we report is relative to the excellent baseline MLX already provides.
  • Local LLM pioneersOllama, llama.cpp, vLLM, SGLang. These projects made local LLM inference accessible to millions and raised the UX bar m5-infer has to meet. Our benchmark comparisons against Ollama and mlx_lm.server reflect different design priorities, not judgment — each engine optimizes for different workloads.
  • Open-weight model makers — Alibaba DAMO (Qwen), Meta (Llama), Mistral AI (Mistral), Google DeepMind (Gemma). Releasing state-of-the-art models openly is an act of generosity the entire local-inference ecosystem depends on. Our Qwen 3.5-specific innovations exist because Alibaba shipped a hybrid architecture openly.
  • Research community — Leviathan et al. (2023) on speculative decoding, the Medusa authors, GatedDeltaNet architects, and HuggingFace for hosting and distribution.

A note on comparisons: the benchmark numbers in this README are measured, reproducible, and specific to a single M5 MacBook Air base. They are not a judgment of the other engines' overall quality. Different goals produce different trade-offs, and we respect each team's choices.

Trademarks. Apple, Apple Silicon, MLX, and macOS are trademarks of Apple Inc. Ollama is a trademark of Ollama Inc. Qwen, Llama, Mistral, and Gemma are trademarks or registered trademarks of their respective owners. All trademarks are used here in a purely descriptive, nominative capacity for factual technical reference. m5-infer is an independent project, not affiliated with, endorsed by, or sponsored by any of the aforementioned entities.

License

Apache License 2.0 — see LICENSE and NOTICE.

Contributing

Issues and pull requests are welcome. Please open an issue first for major changes. See CONTRIBUTING.md and docs/ROADMAP.md.


日本語

⚡ 速度 — 数値で

m5-infer は mlx_lm.server と同じ mlx-lm ライブラリの上に重ねる薄い層です。ですので最も意味のある比較は m5-infer vs mlx_lm.server — 同じハード・同じ重み・同じプロンプト。Ollama の数値は参考として併記します。

指標 (Qwen 3.5 9B 4-bit、M5 MacBook Air base) mlx_lm.server m5-infer Ollama
Decode tok/s (long_gen 512) 17.0 40.0 (最大 2.4 倍) 8.9
Decode tok/s · thinking ON 18.6 28.8 (最大 1.5 倍) 11.2
Thinking-ON 短答 QA (3 問) 0 / 3 3 / 3 0 / 3
Opus-4.7 判定 出力スコア (10 task 平均、同一モデル) 5.85 / 10 5.28 / 10
mlx_lm.server 比 (decode) 1.0× 最大 2.4 倍速い 0.5×
Ollama 比 (decode) 1.9× 最大 4.5 倍速い 1.0×

正直な読み方: m5-infer は持続 decode 速度、thinking モードでの回答抽出、Opus 4.7 採点の出力スコアで勝ちます。mlx_lm.server の in-process prefix cache は短セッションの warm-hit 総レイテンシでは本当に速く、そこは負けます — エンジンごとにトレードオフが違うだけ。warm-path レイテンシ / セッションレイテンシの詳細数値は下の Benchmarks セクションを参照。同じ Mac、同じ重み、ファインチューニングなし — 差を生むのは推論エンジンの層です。

概要

m5-infer は Apple Silicon Mac (M シリーズ) 向けに設計された OpenAI 互換 MLX 推論エンジンです。mlx-lm を土台に、decode 速度・warm-path レイテンシ・長文 retrieval 品質を同時に引き上げる学習不要の最適化を層状に積み重ね、出力品質を損なわずに性能を引き出します。

主要ターゲットは Qwen 3.5 hybrid (GatedDeltaNet + Full Attention) ですが、v1.0 ではモデルファミリー抽象層を介して Qwen 2.5 / Qwen 3.6 / Llama 3.x / Mistral / Gemma 2/3/4 にも対応します。

主要機能

  • OpenAI 互換 HTTP APImlx_lm.server のドロップイン代替、/v1/chat/completions を受信
  • N1 CTRSP — GatedDeltaNet 状態のクロスターン永続化
  • Think-Aware Budget + Loop-Escape Injection — Qwen の <think>…</think> 境界を正しく扱い、ユーザーに見える回答を切り詰めない
  • TPC (Token Prefix Compiler) — トークン列のバイト列ハッシュによる高速 prefix cache
  • N4 ALS (Adaptive Layer Skipping) — 低影響レイヤを token 難易度に応じて skip
  • N3 SSEE (Self-Speculative Early Exit) — モデル内部 speculative
  • N6 PES (Parallel Expert Scheduling) — Expert パスの並列実行実行
  • X5-R 圧縮 forwardmx.compile による Metal カーネル融合
  • Needle-retrieval heuristic — 長文コンテキスト + 短い検索質問のパターンで自動的に thinking モード ON にし、safety alignment の誤発火による拒否応答を回避
  • モデルファミリー抽象化 — Qwen / Llama / Mistral / Gemma を config で統一

技術革新 — m5-infer が他エンジンと違うこと

Ollama 比で最大 4.5× の decode 速度、Opus-judged 品質で +11% のリードは一発勝負ではなく、mlx-lm の上に積み上げた小さな training-free 最適化の集積です (倍率は long_gen decode でのピーク値、実際はワークロードで変動)。以下、計測上で最も効いたものを示します。全て v1.1.0 で実装済み、"将来計画"ではありません。

貢献度の表記:

  • 実測: bench で直接 A/B 比較した値 (同一マシン、同一モデル)
  • 📊 推定: 開発時の per-feature toggle 実験から推定 (参考値 — 実効は workload で変動)
  • ⚙️ 機能有効化: 速度/品質の倍率ではなく、機能の ON/OFF 自体が価値
# Innovation Decode 速度 品質 TTFT / latency
1 Hybrid speculative decoding 📊 +35% tps (29→40) ⚙️ greedy と output-equivalent
2 CTRSP (cross-turn state persist) 12K cold→warm total latency: 69s→11s (6×) · state survives restart
3 Think-aware budget + escape hint Opus score +36% (4.29→5.85)、extract 1.40→7.85
4 Needle-retrieval heuristic 長 context retrieval 0/6 → 6/6
5 ALS + SSEE + PES (decode tricks) 📊 +10-15% tps
6 X5-R compiled forward + wired mem 📊 +40% tps (17→24) 📊 cold 起動 +2-5s (1 回限り)
7 Hardware-aware auto-tune 📊 非-base チップで ±15%
8 モデルファミリー抽象化 ⚙️ Qwen/Llama/Mistral/Gemma で同一エンジン
全 stack 統合 Ollama 比で最大 4.5× (8.9→40.0 tps) Ollama 比 +11% (5.28→5.85) warm 総レイテンシ 最大 5.8× @ 12K

1. Hybrid 対応 speculative decoding (最も難しかった部分)

Qwen 3.5 は hybrid 構成 — GatedDeltaNet (GDN) 24 層 + Full-Attention 8 層。素の mlx-lm は hybrid 用 speculative path を持たない。通常の Transformer であれば draft token が reject されたとき KV cache を N entry 切り詰めて続行できる。しかし GDN ではそれが通用しない — recurrent state と convolutional buffer は draft window 全体を進んでしまっている。KV だけ truncate しても GDN state は壊れたまま残り、モデルは クラッシュせず静かに誤った出力を続ける。

実装: app/innovation/speculative/draft_speculative.py各 verify 呼び出し前に全 GDN 層の (recurrent_state, conv_buf) を事前に確保した tensor プールに snapshot。reject 時は O(1) で復元 — decode のホットパスで memory allocation が発生しない。GDN の内部状態は layer あたり数十 KB、24 層合計でも snapshot コストは 1 ms 以下。

結果:

  • 📊 Decode: Qwen 3.5 9B で +35% tps (baseline 29 tps → speculative 有効時 40 tps)
  • ⚙️ 品質: greedy decode と output-equivalent (mlx_lm.generate と バイト単位で比較検証、acceptance rate という曖昧な指標ではなく)
  • 📊 Hybrid モデルでの acceptance rate: ~70% (4 token draft)

2. CTRSP — Cross-Turn Recurrent State Persist

各生成の最後にモデルの完全状態 (quantized KV cache + GDN recurrent/conv buffer) を disk に serialize、prompt prefix のトークン列のバイト列ハッシュを key として保存。ハッシュはデコード後のテキストではなくトークン列のバイト列上で取るので、system prompt や tool schema が 1 ターン分の delta を追加されてもバイナリレベルの完全一致で検出できる。

結果:

  • ✅ 12 K token tool schema の cold → warm 総レイテンシ: 69 秒 → 11 秒 (2 回目呼び出しの CTRSP ヒットで 6×。"総レイテンシ" は 30 トークン応答を end-to-end で完了する時間であり、厳密な TTFT ではありません)。mlx_lm.server の in-process warm 総レイテンシは 2.3 秒で絶対値は速い。CTRSP 独自の価値は プロセス再起動を越えて state が残ることで、他エンジンは shutdown 時に prefix cache を失います。
  • ✅ 5 ターン継続 session の 5 ターン目 = 7.5 秒 (Ollama は同じ条件では完走しない)
  • ✅ Disk 使用量: 独立した system prompt ハッシュあたり ~50-100 MB (LRU デフォルト 32 entry = ~3 GB 上限)
  • 📊 典型的な agent ワークロード (system prompt は固定、user メッセージのみ変動) での hit rate: >90%

3. Think-aware budget + task-aware escape injection

Opus ベンチで最大の品質改善をもたらした機構。Qwen 3.5 の chain-of-thought は <think>...</think> で囲まれて出力される。素朴な実装では次の 2 つの失敗モードに陥る:

  • Budget 枯渇: ほとんどのエンジンが thinking token をユーザーの max_tokens にカウントする。推論タスクで Qwen は 800 トークン以上を思考で消費することがあり、max_tokens=1024 だと answer phase に入る前に切れる。
  • Think-loop 罠: Qwen は稀に Wait, let me re-check... の spiral に入り、</think> タグを閉じずに続行する。

実装:

  • Thinking 独立 budget (max_thinking_tokens、デフォルト 32 K) — max_tokens</think> 以降だけに適用。
  • N-gram 反復検出器 を think ブロック内に限定して動作 (QualityMonitor、ngram=6、3 回 repeat で発火)。
  • Task-aware escape hint: ループ検出器が発火したときに、素の </think>\n\n ではなく prompt から推定した typed transition を注入 (</think>\n\n**Final JSON:**\n\n\``json\n等)。これによりモデルを指定の出力形式へ強制的に遷移させる。実装はapp/backend/custom_generate.py::_build_escape_hint`。

結果:

  • extract_01 (構造化 JSON 抽出): 1.40 / 10 → 7.85 / 10 (+461%)
  • code_01 (関数 + tests): 3.10 → 6.55 (+111%)
  • design_01 (system design): 2.75 → 4.55 (+65%)
  • ✅ 10 task 総合: 4.29 → 5.85 (+36%)、同じモデルで Ollama の 5.28 を上回る
  • 📊 実行時オーバーヘッド: decode 1 ステップあたり 1 ms 未満 (n-gram hash + token ID lookup)

4. Needle-retrieval heuristic

Qwen 3.5 を thinking OFF で使うと safety alignment の副作用が表面化する: 12 K+ の長 context + 短い retrieval query で、ユーザー自身がコンテキストとして提供した内容について質問しただけでも "I cannot reveal authoritative information" と拒否することがある。標準的なエンジンはこれを「モデルの失敗」としてそのまま返してしまう。私たちは ルーティング層 (app/api/routes.py Rule 1b) で「長文コンテキスト + 短い質問」のパターンを自動検出し、thinking モードを強制的に ON にすることで false-positive な拒否応答を回避。

結果:

  • ✅ Long-context needle retrieval: 0/6 → 6/6 (標準ベンチ)
  • ⚙️ 速度コストはゼロ (ルーティング層での判定のみで、decode 処理には入らない)
  • ⚙️ 発動条件: system prompt が 3000 文字超 かつ user メッセージが 200 文字未満 かつ疑問文パターン

5. Speculative draft-tree + ALS + SSEE + PES

残りの decode 速度向上は小さな win の積層 (各 < 10% だが乗算で効く):

機能 Decode 貢献 (推定) 備考
Speculative draft tree 📊 +5-8% tps v1 は linear lookahead を出荷
N4 ALS (layer skip) 📊 +3-5% tps 易しい token で影響の小さい layer を skip
N3 SSEE (self-speculative) 📊 +3-5% tps モデル内部での early exit
N6 PES (parallel experts) 📊 +2-3% tps Expert パスの並列実行
合計 📊 ~+10-15% tps 相乗効果で効く (個別では <10%)

6. X5-R 圧縮 forward + wired memory

mx.compile で decode forward をラップして MLX に attention + MLP + GDN を Metal kernel 単位で融合させ、wired_limit でモデル重みを pin して memory pressure で swap されないようにする。地味だが、これを省くと speculative decoding の実効 gain が半減する。

結果:

  • 📊 Decode: baseline で +40% tps (他 innovation を積む前の 17 → 24 tps)
  • 📊 起動時の一度きりのコスト: +2-5 秒 (JIT compile。以降のすべてのリクエストで償却される)
  • ⚙️ Wired memory を使わないと、24 GB エントリーチップが thermal throttling 中に swap へ落ち、decode が約 8 tps まで低下する

7. Hardware-aware auto-tune

起動時に app/core/auto_tune.pysysctl でチップの階層 (M1 base 〜 M5 Max) を検出し、階層ごとのデフォルトを適用: GPU コア数に応じた並行実行上限、メモリ帯域に応じた RDMS draft サイズ、メモリ容量に応じた wired_limitengine.toml[engine] auto_tune = false にすれば手動設定がそのまま使われる。

結果:

  • 📊 M5 Pro (期待値): auto-tune 無効時との比較 +15-25% (GPU コアが増えることで並行 decode の幅が拡大)
  • 📊 M5 Max (期待値): +25-40% (base の 2 倍のメモリ帯域)
  • ⚙️ M5 base (実測環境): 基準となる階層。本 README の数値はこの階層で auto-tune 適用後の実測

8. モデルファミリー抽象化

app/core/model_family.py + app/backend/mask_adapter.py が Qwen 3.5、Qwen 3.6、Qwen 2.5、Llama 3.x、Mistral、Gemma 2/3/4 を統一インターフェースで扱う。通常の Transformer 系では GDN や think-aware など hybrid 専用の機構を自動的にスキップする。[model] family = "auto" とすれば main_path から自動判別される。

Innovation stack — illustrative decode contribution

クイックスタート

PyPI から (推奨、v1.1.1 以降):

pip install --upgrade m5-infer
m5-infer                         # フォアグラウンドで port 11436 起動 (Ctrl-C で停止)
# 同じことを: m5-infer start

初回起動時に Qwen 3.5 9B 4-bit (~5 GB) を HuggingFace からダウンロードします。2 回目以降は瞬時です。フラグ: --port--host--config

設定を編集したい場合はローカルに configs/engine.toml を作成してから起動:

m5-infer init                    # ./configs/engine.toml + models.toml を生成
$EDITOR configs/engine.toml
m5-infer                         # 編集内容を拾って起動

大モデルのダウンロード時は HF_TOKEN を環境変数に設定すると HuggingFace のレート制限を回避できます。

ソースから (開発 / ベンチスクリプト / 従来の start/stop/chat シェルスクリプトを使いたい場合):

git clone https://github.com/dualform-labs/m5-infer.git
cd m5-infer
pip install -e .

./start.sh --fg                  # フォアグラウンド、log は stdout
./start.sh                       # バックグラウンド、log は ./logs/
./stop.sh                        # バックグラウンドサーバ停止
./chat.sh                        # インタラクティブ chat (サーバ起動後に使用)

v1.1.1 以降、CTRSP スナップショット等の保存先は $M5_INFER_DATA_DIR / $XDG_DATA_HOME を尊重します。

起動 / 停止 / チャット スクリプト (ソース install 時)

./start.sh --fg        # フォアグラウンド起動 (port 11436)。Ctrl-C で停止。
./start.sh             # バックグラウンド起動、ログは logs/ に出力
./stop.sh              # バックグラウンド起動したサーバーの停止

./chat.sh              # 対話型 CLI (サーバー起動中にのみ動作)
./chat.sh mlx-community/Llama-3.2-3B-Instruct-4bit   # モデルを指定して起動

HTTP エンドポイント (OpenAI 互換)

サーバー起動後、http://127.0.0.1:11436 で以下が利用できます:

Method Path 用途
GET /health 稼働確認 + エンジン状態 (memory、MMRS、MRPB、MTAB の統計)
GET /v1/models 利用可能モデル一覧 (OpenAI 互換)
POST /v1/models/pull HuggingFace からモデルを download + load
POST /v1/chat/completions チャット生成 (OpenAI 互換、stream: true でストリーミング対応)

起動中のサーバーからエンドポイントを取得する方法:

# FastAPI の OpenAPI spec から一覧を取得
curl -s http://127.0.0.1:11436/openapi.json | python3 -c \
  "import json,sys; [print(f'{m.upper():6} {p}') for p,v in json.load(sys.stdin)['paths'].items() for m in v]"

# ブラウザから対話的にドキュメントを見る
open http://127.0.0.1:11436/docs        # Swagger UI
open http://127.0.0.1:11436/redoc       # ReDoc

チャット生成の動作確認

curl -s http://127.0.0.1:11436/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"こんにちは"}],"max_tokens":64}'

# ストリーミング版
curl -sN http://127.0.0.1:11436/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"こんにちは"}],"max_tokens":64,"stream":true}'

公式 OpenAI Python SDK のドロップイン代替として使う例:

from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:11436/v1", api_key="not-needed")
resp = client.chat.completions.create(
    model="main",
    messages=[{"role": "user", "content": "こんにちは"}],
    max_tokens=64,
)
print(resp.choices[0].message.content)

モデルのダウンロード

m5-infer は MLX 互換のモデルなら HuggingFace の repo ID を指定するだけでロードできます。weights は内部で利用している mlx-lm / huggingface_hub~/.cache/huggingface/ 以下に自動キャッシュするので、手動での download 操作は不要です。

ロード方法は 3 通り:

1. デフォルトモデルを設定ファイルで指定 — 初回起動前に configs/engine.toml を編集:

[model]
family = "auto"                                      # main_path から自動判別
main_path = "mlx-community/Qwen3.5-9B-MLX-4bit"      # 初回起動時に自動ダウンロード
draft_path = "mlx-community/Qwen3.5-0.8B-MLX-4bit"   # 任意 (speculative decoding 用)

初回の ./start.sh で両方をダウンロードします (main が数 GB、draft が ~0.6 GB)。2 回目以降はローカルキャッシュから読み込まれます。

2. 実行中に HTTP でモデルを pull — サーバーを再起動せずにホットスワップ:

curl -s -X POST http://127.0.0.1:11436/v1/models/pull \
  -H "Content-Type: application/json" \
  -d '{"model":"mlx-community/Llama-3.2-3B-Instruct-4bit"}'

swap 完了まで新規生成リクエストは待機されます。成功時に {"status":"loaded", ...} が返ります。

3. m5-infer 外で事前ダウンロード — オフライン環境や初回起動前に weights を用意しておきたい場合:

# huggingface CLI (huggingface_hub に同梱)
huggingface-cli download mlx-community/Qwen3.5-9B-MLX-4bit

# または git-lfs
git clone https://huggingface.co/mlx-community/Qwen3.5-9B-MLX-4bit

その後 engine.tomlmain_path に同じ repo ID を指定すれば、既存キャッシュを利用します。

MLX 互換モデルの探し方: HuggingFace mlx-community で探すのが最も早いです。Qwen 3.5 / 3.6 / 2.5、Llama 3.x、Mistral、Gemma 2/3/4 はそのまま動作します (詳細は configs/engine.tomldocs/MODEL_FAMILY_GUIDE.md)。

Gated (利用申請制) モデル: huggingface-cli login で一度ログインしておけば token が自動で使われます。

動作要件

項目 最低 推奨
チップ Apple Silicon M1 M5 シリーズ (Base / Pro / Max。Ultra は未発売)
統合メモリ 16 GB (未検証、小型モデル用) 24 GB 以上 (9B クラス 4bit モデル向け)
macOS 26.4 26.4 以降
Python 3.11 3.12 / 3.13 / 3.14

起動時にチップを検出し、そのチップ階層 (entry = M1 base 〜 ultra = M5 Ultra) に 最適な設定を自動適用します。階層ごとのポリシーは app/core/auto_tune.py を参照。engine.toml[engine] auto_tune = false にすれば手動設定が そのまま使われます。

設定

チューニングは全て configs/engine.toml で行います。モデルファミリー選択例:

[model]
family = "auto"                                 # main_path から自動判別
main_path = "mlx-community/Qwen3.5-9B-MLX-4bit"
draft_path = "mlx-community/Qwen3.5-0.8B-MLX-4bit"  # 任意 (speculative decoding 用)

対応 family: qwen35, qwen36, qwen25, llama, mistral, gemma, auto

新しいファミリーの追加手順は docs/MODEL_FAMILY_GUIDE.md を参照。

プロジェクト構成

app/
├── api/            OpenAI 互換 HTTP サーバ (FastAPI)
├── backend/        MLX 生成バックエンドとカスタム decode ループ
├── cli/            CLI チャットクライアント
├── core/           Config / logging / model_family 検出
├── engine/         Innovation executor、quality monitor、supervisor
├── innovation/     各 innovation モジュール (N1…N6 系、X2…X5R 系、RDMS、SSD、MTAB 等)
└── planner/        Request planner
configs/            engine.toml、models.toml
docs/               公開ドキュメント
tests/              Unit tests

ベンチマーク測定環境

⚠️ 測定条件に関する重要な注意

以下の数値は全て M5 MacBook Air (base、24 GB unified memory、GPU 10 コア、memory bandwidth 153 GB/s)、macOS 26.4 の 1 台で測定した値です。entry-level の M5 であり、9B-4bit モデルで available memory の約 60% を占有、持続負荷で thermal throttling が発生、ファンレス (能動冷却なし)。したがって数値は控えめ側に寄っており、以下の環境では大きく変わる可能性があります:

  • M5 Pro / Max (GPU 2-4×、memory bandwidth 2-4×) — decode 速度は memory bandwidth にほぼ比例して向上見込み
  • より大容量のメモリ (48 GB 以上) — 大型 draft model、より攻めた prefix cache、aggressive / extreme memory mode が解放
  • M5 Studio / Mac mini / MacBook Pro (能動冷却あり) — Air では ~30 秒の heavy decode 後に throttling で速度低下するが、これらでは持続

**「24 GB base-tier Mac でここまで出る」**という下限値として読んでください。ハードウェアの上限を示す数値ではありません。より強力な環境で同じベンチを回した場合の数値提供は歓迎します。

ベンチマーク — full_suite (同一手法、同一ハード、同一モデル)

3 エンジン全てを 同一の計測手法Qwen 3.5 9B (4bit 量子化)、上記の実測環境で測定。プロンプト・decoder 設定 (temperature = 0)・マシン・ウォームアップ手順は全て共通です。社内のベンチハーネスは公開リポには同梱していませんが、http://127.0.0.1:11436/v1/chat/completions に下記テーブルのプロンプトを投げる短い OpenAI 互換クライアントがあれば再現可能です。

指標 Ollama mlx_lm.server m5-infer
Decode tok/s (long_gen 512 tokens) 8.9 17.0 40.0
Ollama 比の速度倍率 1.0× 1.9× 最大 4.5 倍
Warm 総レイテンシ (12K tool スキーマ、2 回目、30 トークン応答) 64.9 秒 2.3 秒 11.1 秒
Session 5 ターン目 (連続対話の 5 ターン目) 失敗 1.6 秒 7.5 秒
短答精度 (3 問) 3/3 ✅ 3/3 ✅* 3/3 ✅

"Warm 総レイテンシ" は 30 トークン応答の end-to-end 完了時間であり、厳密な TTFT ではありません。vLLM 互換手法による厳密 TTFT の再計測は v1.2 で予定 (上の数値は v1.0.0 archive bench、v1.1.0 は strictly additive で品質後退なし、公開 bench 再測定は未実施 — 詳細は CHANGELOG v1.1.0)。

*mlx_lm.server は thinking モードが残ったまま回答を reasoning フィールドに出してしまい、bench の文字列照合はすり抜けますが、機能的には回答生成できているので pass 扱い。

m5-infer は同じモデル・同じプロンプト・同じ Mac で Ollama の最大 4.5 倍、mlx_lm.server の最大 2.4 倍の decode 速度 を達成 (数値は v1.0.0 archive bench、v1.1.0 は strictly additive で回帰なし)。

Decode speed Warm total latency Session 5 ターン目 多指標の総合比較

Thinking ON vs OFF — エンジン別比較

Qwen 3.5 の <think> 推論ブロックを ON / OFF の両方で、同じ bench (long_gen × 2、needle 4K × 3 位置、short_qa × 3) を 3 エンジンで実測。

エンジン Decode OFF Decode ON Needle OFF Needle ON Short QA OFF Short QA ON
Ollama 14.8 tps 11.2 tps 3/3 3/3 3/3 0/3
mlx_lm.server 18.2 tps 18.6 tps 3/3 3/3 3/3 0/3
m5-infer 38.9 tps 28.8 tps 3/3 3/3 3/3 3/3

thinking ON の短答 QA をパスできるのは m5-infer だけ。他の 2 エンジンは回答が reasoning / <think> 内に閉じ込められ、ユーザーに見える content が空のまま返ってくる。m5-infer の think-aware パーサーはモードに関係なく回答を正しく抽出する。

Decode: thinking ON のときでも m5-infer は mlx_lm.server の 最大 2.6 倍 (28.8 vs 18.6 tps、long_gen 512 トークン decode でのピーク値)。

Thinking decode 比較 Thinking 品質 matrix

Opus-4.7 品質ベンチマーク (2026-04-19)

10 タスク (数学推論、コード生成、コードレビュー、システム設計、構造化抽出、曖昧性解消、SQL 最適化、instruction following、技術翻訳、論理パズル) を各エンジンで同じ問題を解かせ、Claude Opus 4.7 が rubric (0〜10 点 × 複数 criterion、重み付き) で採点。Opus 4.7 自身が reference ceiling (満点基準) として参加。

エンジン 総合スコア vs Opus ceiling 得意タスク
Claude Opus 4.7 (reference) 10.00 / 10 100% (天井)
m5-infer 5.85 / 10 59% puzzle 9.65、ambig 8.55、extract 7.85
Ollama qwen3.5:9B 5.28 / 10 53% code 8.05、puzzle 9.60、design 6.85

Task-aware escape hint がこのベンチの決定打。m5-infer のループ脱出注入器が thinking ループを検出した際、素の \n\n ではなく プロンプトから推定したタスクタイプ別の遷移文字列 (**Final JSON:****Final Code:** など) を注入する。これだけで extract_01JSON 出力 0 件 (1.40) → 有効な 6 要素以上の JSON 配列 (7.85 / 10) まで回復。実装は app/backend/custom_generate.py::_build_escape_hint

m5-infer が勝つ領域: extract_01 (7.85 vs 2.10)、puzzle_01 (9.65 vs 9.60)、ambig_01 (8.55 vs 6.80)。構造化されたタスク (JSON 生成、演繹、pragmatic な回答) ほど typed escape の恩恵が大きい。

改善余地: sql_01 (2.70) は n-gram 検出器が見逃した thinking ループが原因 — 30 トークン程度のブロックが逐語的に 4 回繰り返されるが、現在の window サイズをわずかに超えて検出器が発火しない。次のパッチで修正予定。

注意: Ollama は GGUF q4_K_M 量子化、m5-infer は MLX 4-bit 量子化を使用。同じ base weights でも量子化方式が違うと出力が微妙に変わる。1 回の judging には タスクあたり ±0.5 程度のばらつきがあるため、見出しの数値は「このあたりのレンジ」として読んでください。

Opus quality — aggregate Opus quality — per task

ロードマップ

将来リリースで検討中の項目は docs/ROADMAP.md を参照。ロードマップ外の要望は Discussions でお聞かせください。

謝辞

m5-infer は優れた仕事の積み重ねの上に築かせていただきました。以下の各チームに、敬意と感謝を込めて。

  • Apple MLX チームMLXmlx-lm。m5-infer は mlx-lm の上に重ねる薄い層であり、私たちが提示する速度向上はすべて MLX という優れた基礎の上での相対値です。
  • ローカル LLM の開拓者たちOllamallama.cppvLLMSGLang。これらのプロジェクトがローカル LLM を何百万人もの開発者に届け、m5-infer がいま満たすべき UX 基準を引き上げてくれました。Ollama や mlx_lm.server との比較ベンチは、同条件での like-for-like 比較であり、優劣判定ではありません。エンジンごとに最適化の優先順位が異なるだけです。
  • Open-weight モデルの提供者 — Alibaba DAMO (Qwen)、Meta (Llama)、Mistral AI (Mistral)、Google DeepMind (Gemma)。最先端のモデルを open weights で公開すること自体が寛容な行為であり、ローカル推論エコシステム全体がそれに依存しています。私たちの Qwen 3.5 向け新技術は、Alibaba が hybrid 構成のモデルを公開してくれたからこそ実装できました。
  • 研究コミュニティ — Leviathan 他 (2023) の speculative decoding、Medusa 著者、GatedDeltaNet の設計者、配布基盤としての HuggingFace

比較に関する一言: 本 README のベンチマーク値は、M5 MacBook Air base 1 台での実測・再現可能な値であり、他エンジンの全体的な品質を判定するものではありません。目指すものが違えば、トレードオフも違います。各チームの選択に敬意を表します。

商標について: Apple、Apple Silicon、MLX、macOS は Apple Inc. の商標です。Ollama は Ollama Inc. の商標です。Qwen、Llama、Mistral、Gemma は各社の商標または登録商標です。本文書内でのこれら商標の使用は、いずれも純粋に記述的・事実指示的 (nominative use) な目的に限られます。m5-infer は独立したプロジェクトであり、上記いずれの企業・団体とも関連、提携、推奨、スポンサー関係を有しません。

ライセンス

Apache License 2.0 — LICENSENOTICE を参照。

コントリビューション

Issue や Pull Request を歓迎します。大きな変更は事前に Issue で議論してください。詳細は CONTRIBUTING.mddocs/ROADMAP.md を参照。

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

m5_infer-1.1.3.tar.gz (830.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

m5_infer-1.1.3-py3-none-any.whl (202.4 kB view details)

Uploaded Python 3

File details

Details for the file m5_infer-1.1.3.tar.gz.

File metadata

  • Download URL: m5_infer-1.1.3.tar.gz
  • Upload date:
  • Size: 830.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for m5_infer-1.1.3.tar.gz
Algorithm Hash digest
SHA256 7d9e7c295a33ba3455b9e121cd639ef023a3d0919d332601959c7a596d3b7ea0
MD5 bd3c0a74aa0961028efdd9dd37444627
BLAKE2b-256 35a07ea67ddb8b1b4f8951b71d73f5967486b113a4b7ad2f723cd21933b47f19

See more details on using hashes here.

Provenance

The following attestation bundles were made for m5_infer-1.1.3.tar.gz:

Publisher: publish.yml on dualform-labs/m5-infer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file m5_infer-1.1.3-py3-none-any.whl.

File metadata

  • Download URL: m5_infer-1.1.3-py3-none-any.whl
  • Upload date:
  • Size: 202.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for m5_infer-1.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 23b01e6545fe4b67a40489f4bcd7b46023d03e643ce75d699d2ad8758b9f435b
MD5 9f1cfe21181682531c380a426932e580
BLAKE2b-256 db8e25cb7e34c007269af1e4c7ed4a1c2db53d162da7780ce3393a11880f6992

See more details on using hashes here.

Provenance

The following attestation bundles were made for m5_infer-1.1.3-py3-none-any.whl:

Publisher: publish.yml on dualform-labs/m5-infer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page