xinfer-ai 0.14.4 β†’ 0.14.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -61,11 +61,34 @@ xinfer --m /home/Qwen3.6-35B-A3B --d 0,1 --ui-server
61
61
  python3 -m xinfer.server --m Qwen/Qwen3.6-27B-FP8 --kvcache-dtype turbo4 --ui-server
62
62
  ```
63
63
 
64
- **MTP**
64
+ **Speculative decoding (MTP & DFlash2)**
65
+
66
+ Built-in MTP (model must include MTP heads):
67
+
68
+ ```bash
69
+ xinfer --m Qwen/Qwen3.5-35B-A3B --d 0,1 --ui-server --num-speculative-tokens 3
70
+ ```
71
+
72
+ External DFlash2 draft model:
73
+
65
74
  ```bash
66
- xinfer --w /home/Qwen3.6-35B-A3B --d 0,1 --ui-server --mtp 2
75
+ xinfer --m Qwen/Qwen3.8-... --d 0,1 --ui-server \
76
+ --draft-model <dflash2-draft-id-or-path> --num-speculative-tokens 7
67
77
  ```
68
78
 
79
+ See [docs/speculative_decoding.md](docs/speculative_decoding.md) for details.
80
+
81
+ ### 🧩 MoE Expert Offloading
82
+
83
+ MoE expert offloading is supported on CUDA only. Run a large MoE model with expert weights offloaded to host RAM. The value is a fraction: `0.2` keeps 20% of each layer's experts on the GPU; `auto` selects the cache size automatically. Tensor parallelism requires NCCL.
84
+
85
+ ```bash
86
+ XINFER_MOE_OFFLOAD=0.2 xinfer \
87
+ --m Qwen/Qwen3.5-35B-A3B-FP8 --d 0,1 --ui-server
88
+ ```
89
+
90
+ See the [MoE expert offloading guide](docs/moe_offload.md) for configuration and TP details.
91
+
69
92
  > **Tip:** Open `http://IP:8001` for the built-in chat UI, or use `http://IP:8000/v1/` as your API `Base URL`.
70
93
 
71
94
  ---
@@ -82,8 +105,6 @@ Add `--kvcache-dtype` to compress KV cache and extend context length:
82
105
  | `turbo4` | **3.7Γ—** | Best balance | SM70+ / Apple M1|
83
106
  | `turbo3` | **4.7Γ—** | Max compression | SM70+ |
84
107
 
85
- ---
86
-
87
108
  ## πŸ“ˆ Performance
88
109
 
89
110
  > Tested on **V100-32G**, **A100-40G**, **Hopper-80G** and **RTX 5090**
@@ -134,6 +155,7 @@ Add `--kvcache-dtype` to compress KV cache and extend context length:
134
155
  * βœ… Qwen2/Qwen3 MoE
135
156
  * βœ… Qwen3 Next
136
157
  * βœ… Qwen3.5/3.6/3.8 Dense/MoE (27B, 35B, 122B, 397B, Multimodal model)
158
+ * βœ… Qwen3.8-Next/Qwen4
137
159
  * βœ… Mistral v1, v2
138
160
  * βœ… Mistral-3-VL Reasoning (3B, 8B, 14B, Multimodal model)
139
161
  * βœ… GLM4 (0414)
@@ -529,7 +551,8 @@ xinfer --m Qwen/Qwen3.6-27B-FP8 --ui-server --enable-tool-grammar
529
551
  | `--frequency-penalty` | Penalize frequent tokens (βˆ’2 to 2) |
530
552
  | `--mcp-config` | MCP servers JSON config |
531
553
  | `--mcp-command` / `--mcp-args` | Single MCP server command + args |
532
- | `--mtp`| Multi-token prediction, usage `--mtp 2` for two-token prediction per forward pass |
554
+ | `--num-speculative-tokens` | Speculative draft tokens per decode step. Enables **built-in MTP** on models with MTP heads (e.g. Qwen3.5). Example: `--num-speculative-tokens 3` |
555
+ | `--draft-model` | External **DFlash2** draft model (HuggingFace id or local directory). When set, uses DFlash2 instead of built-in MTP. |
533
556
 
534
557
  ### Environment Variables
535
558
 
@@ -591,7 +614,7 @@ SM90_LOWER_PRECISION_GDN_PREFILL=1 xinfer --m Qwen/Qwen3.5-35B-A3B-FP8 --ui-serv
591
614
  * [x] **MXFP4/NVFP4 Model Support**
592
615
  * [x] **Support Turboquant (4-bit, 3-bit) KvCache**
593
616
  * [ ] TentorRT-LLM
594
- * [x] **Multi-token Prediciton (MTP)**
617
+ * [x] **Multi-token Prediction (MTP) & DFlash2 speculative decoding**
595
618
 
596
619
  ---
597
620
 
@@ -67,10 +67,27 @@ cargo build --release --features metal
67
67
  target/release/xinfer --m Qwen/Qwen3-VL-8B-Instruct --ui-server
68
68
  ```
69
69
 
70
- Common runtime knobs: `--max-model-len`, `--max-num-seqs`, `--kv-fraction` (CUDA KV share), `--cpu-mem-fold` (CPU swap ratio), `--port`, `--kvcache-dtype` (fp8/turbo8/turbo4/turbo3), `--prefix-cache`, `--prefix-cache-max-tokens`, `--ui-server`, `--batch` (perf test).
70
+ Common runtime knobs: `--max-model-len`, `--max-num-seqs`, `--kv-fraction` (CUDA KV share), `--cpu-mem-fold` (CPU swap ratio), `--port`, `--kvcache-dtype` (fp8/turbo8/turbo4/turbo3), `--prefix-cache`, `--prefix-cache-max-tokens`, `--ui-server`, `--batch` (perf test), `--num-speculative-tokens` (built-in MTP), `--draft-model` (DFlash2).
71
71
 
72
72
  Reasoning defaults to enabled when a request omits `thinking` / `enable_thinking`. Use `--disable-reasoning` on the Rust CLI to make the default be disabled instead; explicit request values still override the server default.
73
73
 
74
+ ### Speculative decoding (MTP & DFlash2)
75
+
76
+ Speed up decode with speculative token drafting + target verification. See [speculative_decoding.md](./speculative_decoding.md) for full details.
77
+
78
+ - **Built-in MTP** (Qwen3.5 / Qwen3.6 with MTP weights): `--num-speculative-tokens N`
79
+ ```bash
80
+ target/release/xinfer --m Qwen/Qwen3.5-35B-A3B --d 0,1 \
81
+ --num-speculative-tokens 3 --ui-server
82
+ ```
83
+ - **DFlash2** (external draft checkpoint): `--draft-model <hf_id_or_local_path>` plus optional `--num-speculative-tokens`
84
+ ```bash
85
+ target/release/xinfer --m Qwen/Qwen3.8-... --d 0,1 \
86
+ --draft-model <dflash2-draft> --num-speculative-tokens 7 --ui-server
87
+ ```
88
+
89
+ If `--draft-model` is set, DFlash2 is used instead of built-in MTP. Build with `cuda,nccl,flashinfer,cutlass` for best performance.
90
+
74
91
  ## 4) Multi-rank (single node)
75
92
  - **NCCL multi-GPU**
76
93
  ```bash
@@ -0,0 +1,136 @@
1
+ # MoE expert offload
2
+
3
+ Run Mixture-of-Experts models whose weights do not fit in VRAM. The routed
4
+ experts stay in pinned host RAM; each MoE layer keeps a fraction of its experts
5
+ in GPU *slots*. Attention, dense layers, shared experts and the KV cache stay on
6
+ the GPU as usual.
7
+
8
+ This feature requires a CUDA build. Tensor parallelism (`--d 0,1`, etc.) also
9
+ requires NCCL; dense models do not enter the offload path and are unaffected
10
+ when the variable is unset.
11
+
12
+ ```bash
13
+ # Keep 20% of every layer's experts on GPU (at least one top-k decode step),
14
+ # and keep the remaining experts in host RAM. TP works rank-by-rank.
15
+ XINFER_MOE_OFFLOAD=0.2 xinfer \
16
+ --m Qwen/Qwen3.5-35B-A3B-FP8 --d 0,1 --max-model-len 8192 --ui-server
17
+
18
+ # Or size the per-layer GPU caches automatically from free VRAM.
19
+ XINFER_MOE_OFFLOAD=auto xinfer \
20
+ --m Qwen/Qwen3.5-35B-A3B-FP8 --d 0,1 --max-model-len 8192 --ui-server
21
+ ```
22
+
23
+ Placement never changes routing or precision: with greedy decoding the output
24
+ is byte-identical for every `XINFER_MOE_OFFLOAD` value.
25
+
26
+ ## How it works
27
+
28
+ - **Decode** β€” before each fused MoE GEMM, a CUDA kernel looks the routed experts
29
+ up in a per-layer LRU table kept on the GPU, assigns least-recently-used slots
30
+ to misses and rewrites expert ids to slot ids. A second kernel copies the
31
+ missing experts straight from device-mapped pinned host memory into their
32
+ slots. Both kernels have fixed launch shapes and read the miss count from
33
+ device memory, so decode stays inside CUDA graphs with no host sync.
34
+ - **Prefill** β€” when a call touches more distinct experts than the layer has
35
+ slots, the whole expert layer is copied (pinned async memcpy) into a staging
36
+ buffer, and the unmodified GEMM runs on it.
37
+ - The GEMM kernels are unchanged: they read `[num_slots, N, K]` slot tensors
38
+ instead of `[num_experts, N, K]`.
39
+
40
+ Design follows [FreeToken](https://github.com/FlashML-org/FreeToken) (Apache-2.0).
41
+
42
+ ## Options
43
+
44
+ | Variable | Meaning |
45
+ |---|---|
46
+ | `XINFER_MOE_OFFLOAD=auto` | After loading, grow every layer's slots evenly into the free VRAM, leaving the reserve below for the KV cache and workspaces (recommended) |
47
+ | `XINFER_MOE_OFFLOAD=<0..1>` | Fixed fraction of each layer's experts kept on the GPU; for example, `0.2` means 20% (at least `num_experts_per_tok`). Unset = no offload |
48
+ | `XINFER_MOE_OFFLOAD_RESERVE_GB` | VRAM left free by `auto` (default 2.5). Raise it for longer contexts |
49
+ | `XINFER_MOE_OFFLOAD_PROFILE=1` | Logs a per-MoE-call time split (launch gap, GPU wait, host, copy). Adds device syncs and disables CUDA graphs; diagnostics only |
50
+
51
+ More experts on the GPU means faster decode; more reserve means a longer
52
+ context. TurboQuant (`--kvcache-dtype turbo4|turbo3`) stretches the reserve
53
+ ~4x. Models with a large KV per token (e.g. Gemma 4) need `fp8` or `turbo*`.
54
+ MTP (`--num-speculative-tokens`) works but was slower in our tests.
55
+
56
+ For tensor-parallel inference, pass multiple devices with `--d 0,1` (or more
57
+ devices). Every rank must be able to read the model files locally. Each rank
58
+ loads and offloads its own expert shard, while NCCL continues to combine the
59
+ rank-local MoE results. The offload fraction applies independently to every
60
+ rank and every MoE layer.
61
+
62
+ ## Supported
63
+
64
+ - Formats:
65
+ - GGUF (`FusedMoeGGUF`, separate `ffn_gate_exps`/`ffn_up_exps`, quant types the
66
+ GGUF MoE kernel reads natively) and ISQ (`--isq`, `FusedMoeISQ`: experts are
67
+ quantized on the GPU at load, then moved to host RAM);
68
+ - NVFP4 ModelOpt safetensors (`FusedMoeNvfp4`);
69
+ - unquantized bf16/f16 (`FusedMoe`), block FP8 (`FusedMoeFp8`), MXFP4
70
+ (`FusedMoeMxfp4`) and GPTQ/AWQ/compressed-tensors WNA16 (`FusedMoeWNA16`): all
71
+ share one generic path that keeps every `[E, ...]` expert tensor (weights and
72
+ scales, any dtype) in host RAM.
73
+ - Not supported: W2-packed DeepSeek-V4 experts (fails with a clear error).
74
+ - CUDA single-GPU and tensor-parallel inference. NVFP4 on SM100+ (swizzled
75
+ scales) is not supported yet.
76
+ - Tested on real models: GGUF, NVFP4, bf16, ISQ, and FP8 MoE safetensors,
77
+ including Qwen3.5-35B-A3B-FP8 with two-rank tensor parallelism. MXFP4 and
78
+ WNA16 are covered by the generic path's bit-exact GEMM test.
79
+ - Host RAM must hold every expert (pinned).
80
+
81
+ ## Benchmarks
82
+
83
+ Reproduce with `bench/bench_moe_offload.py` (rate `0` = baseline without offload):
84
+
85
+ ```bash
86
+ python3 bench/bench_moe_offload.py --bin target/release/xinfer \
87
+ --model /models/Qwen3.6-35B-A3B-NVFP4 --rates 0,0.1,0.2,0.35,auto --out bench/qwen36
88
+ ```
89
+
90
+ It runs the OpenAI-compatible server, sends a warm-up and one streamed request
91
+ (greedy) and records time to first token, decode speed and inter-token latency
92
+ percentiles; `--prompt-tokens N` adds a long-context needle test. It writes a
93
+ Markdown table and the raw JSON next to `--out` (not committed).
94
+
95
+ RTX 3060 12 GB, PCIe 3.0 x16, Xeon E5-2650 v4, 64 GB DDR4-2400, Windows 11,
96
+ 200 decoded tokens, `--max-model-len 8192`. Without offload none of these
97
+ models runs on this card: the weights spill into the driver's sysmem fallback
98
+ and no VRAM is left for the KV cache.
99
+
100
+ **Qwen3.6-35B-A3B NVFP4** (23.4 GB of weights)
101
+
102
+ | GPU experts | KV dtype | KV tokens | TTFT | Decode tok/s | Inter-token p50 / p99 ms |
103
+ |---|---|---|---|---|---|
104
+ | none | β€” | does not run | | | |
105
+ | 10% | bf16 | 192k | 2.1 s | 21.5 | 46 / 70 |
106
+ | 20% | bf16 | 134k | 2.0 s | 26.4 | 38 / 51 |
107
+ | 35% | bf16 | 46k | 0.8 s | 28.6 | 35 / 44 |
108
+ | auto (31%) | bf16 | 58k | 1.3 s | 26.9 | 37 / 49 |
109
+ | 20% | turbo4 | 520k | 2.1 s | 23.3 | 41 / 68 |
110
+ | 35% | turbo3 | 201k | 1.0 s | 26.8 | 37 / 45 |
111
+
112
+ Long context: an 11.5k-token needle prompt at 20% prefills at 333 tok/s and the
113
+ needle is found. Greedy output is byte-identical for every GPU fraction.
114
+
115
+ **Gemma-4-26B-A4B NVFP4** (18.8 GB): 10% / 15% with `turbo4` β†’ 16.7 / 19.8 tok/s
116
+ (27k / 16k KV tokens); with `fp8` at 10% β†’ 18.9 tok/s.
117
+ **GLM-4.7-Flash NVFP4** (20.5 GB, no CUDA graphs in xinfer for this model):
118
+ 10% / 20% β†’ 10.8 / 11.2 tok/s. Outputs identical across fractions for both.
119
+
120
+ **Qwen1.5-MoE-A2.7B-Chat bf16** (28.6 GB, 60 experts of 17 MB each), `auto` with
121
+ `XINFER_MOE_OFFLOAD_RESERVE_GB=1.5`:
122
+
123
+ | Experts | KV dtype | GPU experts | KV tokens | TTFT | Decode tok/s | Inter-token p50 / p99 ms |
124
+ |---|---|---|---|---|---|---|
125
+ | bf16 | fp8 | auto (~15%) | 8.4k | 2.5 s | 7.9 | 131 / 168 |
126
+ | bf16 | turbo4 | auto (~15%) | 12.5k | 2.4 s | 8.1 | 126 / 158 |
127
+ | ISQ `q8_0` | fp8 | auto (40%) | 14.8k | 1.9 s | 18.0 | 55 / 86 |
128
+ | ISQ `q8_0` | turbo4 | auto (40%) | 27.1k | 1.9 s | 20.5 | 49 / 74 |
129
+
130
+ bf16 experts are PCIe-bound (~1.4 GB of misses per token over PCIe 3.0);
131
+ `--isq q8_0` halves the expert size and fits 2.7x more experts on the GPU, so
132
+ decode is 2.5x faster. Q4K needs `moe_intermediate_size % 256 == 0` (1408 here)
133
+ and the GGUF MoE kernel has no Q4_0, so 4-bit ISQ is not available for this
134
+ model. bf16 greedy output is identical at 10% and auto (~15%).
135
+
136
+ Run-to-run variation on this machine is about Β±10%.
@@ -0,0 +1,129 @@
1
+ # Speculative Decoding (MTP & DFlash2)
2
+
3
+ xInfer speeds up autoregressive decode with **speculative decoding**: a small set of candidate tokens is proposed each step, then verified by the target model in a single batched forward pass. Accepted drafts advance the sequence without extra per-token decode rounds.
4
+
5
+ Two modes are supported:
6
+
7
+ | Mode | When to use | CLI |
8
+ |------|-------------|-----|
9
+ | **Built-in MTP** | Target model ships MTP prediction heads (e.g. Qwen3.5 / Qwen3.6 with MTP weights) | `--num-speculative-tokens N` |
10
+ | **DFlash2** | External DFlash2 draft model (separate safetensors checkpoint) | `--draft-model <id_or_path>` |
11
+
12
+ **Rule:** If `--draft-model` is set, xInfer uses **DFlash2** (DFlash1 checkpoints are not supported). Otherwise, `--num-speculative-tokens` enables **built-in MTP** when the target model has MTP layers.
13
+
14
+ ---
15
+
16
+ ## Built-in MTP
17
+
18
+ MTP (Multi-Token Prediction) uses lightweight heads bundled in the target checkpoint. Each decode step:
19
+
20
+ 1. Target model produces one **anchor** token and a hidden state.
21
+ 2. MTP head drafts `N` tokens autoregressively (no extra KV growth).
22
+ 3. Target model verifies `[anchor, draftβ‚€, …, draftₙ₋₁]` in one forward pass.
23
+ 4. Matching prefix is accepted; GDN/Mamba state is rolled back on partial rejection (Qwen3.5 hybrid models).
24
+
25
+ ### CLI
26
+
27
+ ```bash
28
+ # Qwen3.5 35B with 3 speculative tokens per step
29
+ xinfer --m Qwen/Qwen3.5-35B-A3B --d 0,1 \
30
+ --num-speculative-tokens 3 --ui-server
31
+ ```
32
+
33
+ Typical values: **3–7** draft tokens. Higher values increase acceptance variance and KV verify width.
34
+
35
+ ### Requirements
36
+
37
+ - Target weights must include MTP layers (`mtp.*` or GGUF `nextn.*` tensors).
38
+ - Model types with MTP support today: **Qwen3.5**, **Qwen3.5 MoE**, **Qwen3-VL** (Qwen3.5 text backbone).
39
+ - Recommended build: `./build.sh --release --features cuda,nccl,flashinfer,cutlass`
40
+
41
+ ---
42
+
43
+ ## DFlash2
44
+
45
+ [DFlash2](https://github.com/z-lab/dflash) uses a **separate draft model** that consumes projected intermediate hidden states from selected target layers. Draft tokens are chosen with a top-k candidate lattice (`selector_top_k` in the draft config).
46
+
47
+ ### CLI
48
+
49
+ ```bash
50
+ # Qwen3.8 target + DFlash2 draft (HuggingFace id or local path)
51
+ xinfer --m Qwen/Qwen3.8-... --d 0,1 \
52
+ --draft-model <your-dflash2-draft-repo-or-path> \
53
+ --num-speculative-tokens 7 --ui-server
54
+ ```
55
+
56
+ `--draft-model` accepts either:
57
+
58
+ - A **HuggingFace model id** (downloads `config.json` + safetensors), or
59
+ - A **local directory** containing `config.json` and `model.safetensors` (or sharded index).
60
+
61
+ `--num-speculative-tokens` is the number of draft tokens proposed per step (excluding the anchor). If omitted, xInfer uses the draft model config (`block_size - 1`).
62
+
63
+ Draft weights must be **safetensors** (GGUF draft models are not supported). The checkpoint must be **DFlash2** (`architectures` contains `DFlash2` or `dflash_config.selector_top_k` is set).
64
+
65
+ ### Requirements
66
+
67
+ - Target: **Qwen3.5**, **Qwen3.5 MoE**, **Qwen3-VL**, or **Qwen3.8** (dense) today.
68
+ - Draft: matching-family **DFlash2** checkpoint.
69
+ - Recommended build: `cuda,nccl,flashinfer,cutlass` (CUDA graphs optional; disable with `--disable-cuda-graph` for debugging).
70
+
71
+ ---
72
+
73
+ ## Python API
74
+
75
+ ```python
76
+ from xinfer import Engine, EngineConfig
77
+
78
+ # Built-in MTP
79
+ cfg = EngineConfig(
80
+ model_id="Qwen/Qwen3.5-35B-A3B",
81
+ num_speculative_tokens=3,
82
+ )
83
+
84
+ # DFlash2 (draft_model enables DFlash2 instead of MTP)
85
+ cfg = EngineConfig(
86
+ model_id="Qwen/Qwen3.8-...",
87
+ draft_model="<dflash2-draft-id-or-path>",
88
+ num_speculative_tokens=7,
89
+ )
90
+
91
+ engine = Engine(cfg, "bf16")
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Flags reference
97
+
98
+ | Flag | Description |
99
+ |------|-------------|
100
+ | `--num-speculative-tokens N` | Draft tokens per decode step. Enables **MTP** when the target has MTP heads and `--draft-model` is unset. With `--draft-model`, sets DFlash2 draft width (optional; defaults from draft config). |
101
+ | `--draft-model <id_or_path>` | External **DFlash2** draft model. Enables DFlash2 and disables built-in MTP. |
102
+
103
+ **Removed:** `--mtp` (use `--num-speculative-tokens`), `--draft-model-id` / `--draft-model-path` (use `--draft-model`).
104
+
105
+ ---
106
+
107
+ ## Tips & limitations
108
+
109
+ - **Throughput:** Speedup depends on draft acceptance rate and model size. Monitor logs for `MTP Stats` / `DFlash2 Stats` acceptance summaries.
110
+ - **Memory:** Verify passes append up to `N+1` tokens per step; ensure KV budget via `--kv-fraction` and `--max-num-seqs`.
111
+ - **Hybrid models:** Qwen3.5 GDN layers require state rollback on rejected drafts; this is handled automatically.
112
+ - **Batching:** Speculative decode is optimized for `batch_size=1` decode steps; larger batches use a batched DFlash2 path.
113
+ - **PD disaggregation:** Do not combine speculative decode with PD client/server modes on hybrid Mamba models.
114
+
115
+ ---
116
+
117
+ ## Quick test
118
+
119
+ ```bash
120
+ # 1. Health check
121
+ curl -s http://localhost:8000/v1/models
122
+
123
+ # 2. Short completion (MTP or DFlash2 server)
124
+ curl -s http://localhost:8000/v1/chat/completions \
125
+ -H "Content-Type: application/json" \
126
+ -d '{"model":"Qwen/Qwen3.5-35B-A3B","messages":[{"role":"user","content":"Hello"}],"max_tokens":64}'
127
+ ```
128
+
129
+ See also [test_model.md](./test_model.md) for full model validation workflows.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "xinfer-ai",
3
- "version": "0.14.4",
4
- "assetVersion": "0.14.4",
3
+ "version": "0.14.6",
4
+ "assetVersion": "0.14.6",
5
5
  "description": "xInfer β€” a high-performance LLM inference engine in Rust with CUDA/Metal acceleration",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/guoqingbao/xinfer#readme",