LLM inference engine implementing continuous batching, PagedAttention, prefix caching, prefill-decode disaggregation, and KV-cache-aware routing.
Python
0
161 commits
updated Oct 18, 2026
A from-scratch LLM inference engine for newly-published architectures, bit-parity validated against the upstream reference implementation. The niche neither vLLM nor SGLang fills: a research-paper-readable engine that ports new architectures fast and matches the reference behaviour exactly. Ten model families register today, from Qwen2 through complete from-scratch ports of DeepSeek-V4 and GLM-5.2.
Positioning, non-goals, and roadmap: docs/plans/roadmap-2026.md.
model.forward per step (FlashAttention varlen on CUDA, PyTorch reference elsewhere), PagedAttention with per-layer named KV streams, prefix caching, OpenAI-compatible HTTP API with SSE streaming.world_size=1), prefill/decode disaggregation, and two-model speculative decoding.Every non-trivial choice is an ADR in docs/decisions/ (ADR-001 through ADR-021). Paper-section to code walkthroughs live in docs/architectures/.
Continuous batching, Qwen2.5-0.5B-Instruct, A10, bf16:
| Concurrency | Tokens/sec | vs C=1 |
|---|---|---|
| 1 | 45.7 | 1.00x |
| 2 | 79.5 | 1.74x |
| 4 | 127.7 | 2.79x |
| 8 | 187.5 | 4.10x |
Other validated results (full reports in docs/benchmarks/):
# Install (Python 3.11+, uv as the package manager)
uv sync
# Unit tests (CPU only, no model download)
uv run pytest tests/unit/ -v
# API server (downloads Qwen2.5-0.5B-Instruct on first start)
uv run python -m mini_infer.api.server
# In another shell, hit the API
curl -N http://localhost:8000/v1/completions \
-H 'Content-Type: application/json' \
-d '{"model":"qwen","prompt":"The capital of France is","max_tokens":8,"stream":true}'
# Demo: DeepSeek-V4 hybrid CSA/HCA backbone (synthetic weights, no download)
uv run python scripts/demo_deepseek_v4_hybrid.py
Default model is Qwen/Qwen2.5-0.5B-Instruct (override with MINI_INFER_MODEL). Device is automatic: CUDA, else MPS, else CPU. The Triton paged kernel runs on CUDA; other devices use a numerically-equivalent PyTorch reference.
ModelRegistry maps an HF config.architectures[0] string to an owned nn.Module. HF safetensors load directly (load_state_dict, or load_state_dict_with_tp for multi-GPU); no runtime monkey-patching.
| HF architecture | Examples | Family-specific primitives |
|---|---|---|
Qwen2ForCausalLM | Qwen2.5-0.5B / 7B / 32B | Biased Q/K/V projections |
Qwen3ForCausalLM | Qwen3 0.6B-32B | Per-head Q/K norm, tied embeddings (doc) |
LlamaForCausalLM | Llama 2/3/4, SmolLM2, TinyLlama, Nemotron | Llama-shape baseline (doc) |
MistralForCausalLM | Mistral 7B / Small / Large | Llama-shape (separate HF key) (doc) |
Gemma3ForCausalLM | Gemma 3 1B / 4B | Sliding + global attention, dual RoPE, sandwich norms, GeGLU (doc) |
Gemma4ForConditionalGeneration | Gemma 4 31B-it (text-only) | Heterogeneous per-layer KV, k_eq_v, dual RoPE with different head_dim, logit softcap, SDPA backend override (doc) |
MixtralForCausalLM | Mixtral 8x7B / 8x22B | Top-k sparse MoE (8 experts, top-2) (doc) |
DeepseekV2ForCausalLM | V2-Lite / V2 / V3 / Kimi-K2 | Multi-head Latent Attention (~7x smaller cache), low-rank Q, interleaved RoPE, dense + MoE FFN (doc) |
DeepseekV4ForCausalLM | V4-Pro / V4-Flash | Every published V4 primitive: three-mode attention (SWA/CSA/HCA), Lightning Indexer, token compressor, attention sink, grouped output, hash-routed MoE, Hyper-Connections, YaRN (doc, ADR-014) |
GlmMoeDsaForCausalLM | GLM-5.2 (753B FP8) | DeepSeek-V3.2-style MLA + DeepSeek Sparse Attention (Lightning Indexer top-k) + GLM IndexShare + noaux_tc sigmoid MoE; block-FP8 per-expert + FP8-resident loader (ADR-021) |
Adding a family is one file in src/mini_infer/models/<family>.py: declare a config + a class composing the shared blocks (RMSNorm, RoPE, GQA, SwiGLU, MoE FFN, ...), decorate with @register_model, add one import. Copy the closest example, from mistral.py (Llama-shape, 30 lines) up to deepseek_v4.py (hybrid attention + Hyper-Connections, ~1.6k lines across the family).
Validation. Each loadable family has a CPU/MPS smoke that loads an ungated checkpoint and emits "Paris" for "The capital of France is". Models past M1's memory budget (Mixtral, Gemma 4, DeepSeek-V2-Lite, V4, GLM-5.2) are validated on Modal and/or bit-parity-checked against the HF block on synthetic input. DeepSeek-V4: every primitive is bit-parity vs the V4-Pro reference (cosine 1.0000, max abs diff < 1e-9); the V4-Flash loader matches all 34,223 params of the real safetensors index, with a full multi-GPU forward as the remaining gate. GLM-5.2: bit-parity vs the HF glm_moe_dsa reference on CPU, a single L4 (fp32-exact + bf16), and 2x L4 under NCCL TP; the FP8-resident loader fits the 753B checkpoint on a single 8xH200 node, with the funded full-weight run pending.
from mini_infer.engine.model_runner import ModelRunner
from mini_infer.engine.sampler import SamplingParams
from mini_infer.scheduler import ContinuousScheduler, Request
runner = ModelRunner.from_pretrained(
"Qwen/Qwen2.5-0.5B-Instruct",
quant=None, # "int8" enables W8A16 weight-only quant
kv_quant=None, # "turbo4" | "turbo3" | "fp8" | "nvfp4"
attention_backend="flash_attn", # "flashinfer" required for fp8/nvfp4
)
scheduler = ContinuousScheduler(runner)
scheduler.start()
try:
result = scheduler.run(Request(
prompt="The capital of France is",
sampling_params=SamplingParams(temperature=0.0), # greedy
max_tokens=32,
))
print(result.text)
finally:
scheduler.stop()
kv_quant | attention_backend | KV vs bf16 | Best on | Notes |
|---|---|---|---|---|
None (default) | flash_attn | 100% | All CUDA | Token-for-token parity; the reference path |
None | flashinfer | 100% | All CUDA | bf16 KV via FlashInfer; same parity, different kernel |
turbo4 | flash_attn | 38% | A10/A100 | Argmax parity at 0.5B; 7B drifts |
turbo3 | flash_attn | 26% | A10/A100 | Coherent at 0.5B; fused kernel keeps decode at 0.31-0.41x of bf16 |
fp8 | flashinfer | 50% | H100/H200 | cos sim 0.999985, 0.93x throughput; the 8-bit KV answer on Hopper |
nvfp4 | flashinfer | 28% | B200 | 4-bit KV; 0.91x throughput; needs outlier-aware calibration (infra only) |
/v1/completions mirrors the OpenAI Completions API (subset), non-streaming or SSE. Env vars: MINI_INFER_MODEL, MINI_INFER_USE_PD=1 (back the API with the PD pipeline), MINI_INFER_PD_MODE=serial|parallel. For non-default quant/backends, build the runner programmatically and pass it to make_app(...).
uv run python -m mini_infer.api.server
MINI_INFER_MODEL="Qwen/Qwen2.5-7B-Instruct" uv run python -m mini_infer.api.server
SpeculativeRunner(target=..., draft=..., K=4).run_greedy(...) for a target + draft pair (same vocab family).modal run scripts/modal_packed_bench.py --config <throughput|prefix|flashinfer_fp8|flashinfer_nvfp4|...> reproduces the published numbers. See --help for the full list.HTTP request
│
▼
FastAPI (api/server.py) ──► ContinuousScheduler.submit(Request)
│
engine thread
│
┌───────────────────┼─────────────────────┐
▼ ▼ ▼
admit waiting prefill (B=1) batched decode
│ │
└────► PagedKVCache ◄─┘
│
▼
BlockPool (shared)
│
▼
Backend kernels / PyTorch reference
(CUDA/Triton, TPU/Pallas, Trainium/NKI) (CPU/MPS)
engine/ loads the model and owns prefill and decode.models/ is the registry: per-family nn.Modules + a shared block library (RMSNorm, RoPE, GQA, SwiGLU, MoE FFN, ...).scheduler/ owns the engine thread, admission control, the running batch, and the shared PagedKVCache.cache/ holds the block pool, the batch-aware cache, and the paged attention kernel + reference. Per-layer named StreamSpecs handle heterogeneous KV (Gemma 4) and MLA's kv_latent + k_rope; V4 adds a per-request StateCache.api/ is FastAPI with OpenAI-compatible endpoints and SSE streaming.tests/unit/): CPU-only, fast, on every CI push. Block pool, cache, attention reference, scheduler, sampler, API schemas.tests/golden/): CPU/fp32 token-for-token vs Hugging Face, catches numerical drift.scripts/): real CUDA validation, on demand.A separate weekly workflow (.github/workflows/bitparity.yml) runs the requires_model tests against pinned HF revisions (tests/_pinned_models.toml) so upstream changes don't silently affect us.
uv run pytest tests/unit/ -q # CI-equivalent (no model loads)
uv run pytest tests/golden/ -q # token-for-token vs HF
uv run pytest -m requires_model -v # bit-parity subset (downloads models)
By design:
Planned work (DeepSeek-V3/Kimi-K2 large-checkpoint runs, V4-Flash multi-GPU forward, the funded GLM-5.2 753B run) is tracked in docs/plans/roadmap-2026.md.
(Pending.)
LLM inference engine implementing continuous batching, PagedAttention, prefix caching, prefill-decode disaggregation, and KV-cache-aware routing.
Python
0
161 commits
updated Oct 18, 2026
A from-scratch LLM inference engine for newly-published architectures, bit-parity validated against the upstream reference implementation. The niche neither vLLM nor SGLang fills: a research-paper-readable engine that ports new architectures fast and matches the reference behaviour exactly. Ten model families register today, from Qwen2 through complete from-scratch ports of DeepSeek-V4 and GLM-5.2.
Positioning, non-goals, and roadmap: docs/plans/roadmap-2026.md.
model.forward per step (FlashAttention varlen on CUDA, PyTorch reference elsewhere), PagedAttention with per-layer named KV streams, prefix caching, OpenAI-compatible HTTP API with SSE streaming.world_size=1), prefill/decode disaggregation, and two-model speculative decoding.Every non-trivial choice is an ADR in docs/decisions/ (ADR-001 through ADR-021). Paper-section to code walkthroughs live in docs/architectures/.
Continuous batching, Qwen2.5-0.5B-Instruct, A10, bf16:
| Concurrency | Tokens/sec | vs C=1 |
|---|---|---|
| 1 | 45.7 | 1.00x |
| 2 | 79.5 | 1.74x |
| 4 | 127.7 | 2.79x |
| 8 | 187.5 | 4.10x |
Other validated results (full reports in docs/benchmarks/):
# Install (Python 3.11+, uv as the package manager)
uv sync
# Unit tests (CPU only, no model download)
uv run pytest tests/unit/ -v
# API server (downloads Qwen2.5-0.5B-Instruct on first start)
uv run python -m mini_infer.api.server
# In another shell, hit the API
curl -N http://localhost:8000/v1/completions \
-H 'Content-Type: application/json' \
-d '{"model":"qwen","prompt":"The capital of France is","max_tokens":8,"stream":true}'
# Demo: DeepSeek-V4 hybrid CSA/HCA backbone (synthetic weights, no download)
uv run python scripts/demo_deepseek_v4_hybrid.py
Default model is Qwen/Qwen2.5-0.5B-Instruct (override with MINI_INFER_MODEL). Device is automatic: CUDA, else MPS, else CPU. The Triton paged kernel runs on CUDA; other devices use a numerically-equivalent PyTorch reference.
ModelRegistry maps an HF config.architectures[0] string to an owned nn.Module. HF safetensors load directly (load_state_dict, or load_state_dict_with_tp for multi-GPU); no runtime monkey-patching.
| HF architecture | Examples | Family-specific primitives |
|---|---|---|
Qwen2ForCausalLM | Qwen2.5-0.5B / 7B / 32B | Biased Q/K/V projections |
Qwen3ForCausalLM | Qwen3 0.6B-32B | Per-head Q/K norm, tied embeddings (doc) |
LlamaForCausalLM | Llama 2/3/4, SmolLM2, TinyLlama, Nemotron | Llama-shape baseline (doc) |
MistralForCausalLM | Mistral 7B / Small / Large | Llama-shape (separate HF key) (doc) |
Gemma3ForCausalLM | Gemma 3 1B / 4B | Sliding + global attention, dual RoPE, sandwich norms, GeGLU (doc) |
Gemma4ForConditionalGeneration | Gemma 4 31B-it (text-only) | Heterogeneous per-layer KV, k_eq_v, dual RoPE with different head_dim, logit softcap, SDPA backend override (doc) |
MixtralForCausalLM | Mixtral 8x7B / 8x22B | Top-k sparse MoE (8 experts, top-2) (doc) |
DeepseekV2ForCausalLM | V2-Lite / V2 / V3 / Kimi-K2 | Multi-head Latent Attention (~7x smaller cache), low-rank Q, interleaved RoPE, dense + MoE FFN (doc) |
DeepseekV4ForCausalLM | V4-Pro / V4-Flash | Every published V4 primitive: three-mode attention (SWA/CSA/HCA), Lightning Indexer, token compressor, attention sink, grouped output, hash-routed MoE, Hyper-Connections, YaRN (doc, ADR-014) |
GlmMoeDsaForCausalLM | GLM-5.2 (753B FP8) | DeepSeek-V3.2-style MLA + DeepSeek Sparse Attention (Lightning Indexer top-k) + GLM IndexShare + noaux_tc sigmoid MoE; block-FP8 per-expert + FP8-resident loader (ADR-021) |
Adding a family is one file in src/mini_infer/models/<family>.py: declare a config + a class composing the shared blocks (RMSNorm, RoPE, GQA, SwiGLU, MoE FFN, ...), decorate with @register_model, add one import. Copy the closest example, from mistral.py (Llama-shape, 30 lines) up to deepseek_v4.py (hybrid attention + Hyper-Connections, ~1.6k lines across the family).
Validation. Each loadable family has a CPU/MPS smoke that loads an ungated checkpoint and emits "Paris" for "The capital of France is". Models past M1's memory budget (Mixtral, Gemma 4, DeepSeek-V2-Lite, V4, GLM-5.2) are validated on Modal and/or bit-parity-checked against the HF block on synthetic input. DeepSeek-V4: every primitive is bit-parity vs the V4-Pro reference (cosine 1.0000, max abs diff < 1e-9); the V4-Flash loader matches all 34,223 params of the real safetensors index, with a full multi-GPU forward as the remaining gate. GLM-5.2: bit-parity vs the HF glm_moe_dsa reference on CPU, a single L4 (fp32-exact + bf16), and 2x L4 under NCCL TP; the FP8-resident loader fits the 753B checkpoint on a single 8xH200 node, with the funded full-weight run pending.
from mini_infer.engine.model_runner import ModelRunner
from mini_infer.engine.sampler import SamplingParams
from mini_infer.scheduler import ContinuousScheduler, Request
runner = ModelRunner.from_pretrained(
"Qwen/Qwen2.5-0.5B-Instruct",
quant=None, # "int8" enables W8A16 weight-only quant
kv_quant=None, # "turbo4" | "turbo3" | "fp8" | "nvfp4"
attention_backend="flash_attn", # "flashinfer" required for fp8/nvfp4
)
scheduler = ContinuousScheduler(runner)
scheduler.start()
try:
result = scheduler.run(Request(
prompt="The capital of France is",
sampling_params=SamplingParams(temperature=0.0), # greedy
max_tokens=32,
))
print(result.text)
finally:
scheduler.stop()
kv_quant | attention_backend | KV vs bf16 | Best on | Notes |
|---|---|---|---|---|
None (default) | flash_attn | 100% | All CUDA | Token-for-token parity; the reference path |
None | flashinfer | 100% | All CUDA | bf16 KV via FlashInfer; same parity, different kernel |
turbo4 | flash_attn | 38% | A10/A100 | Argmax parity at 0.5B; 7B drifts |
turbo3 | flash_attn | 26% | A10/A100 | Coherent at 0.5B; fused kernel keeps decode at 0.31-0.41x of bf16 |
fp8 | flashinfer | 50% | H100/H200 | cos sim 0.999985, 0.93x throughput; the 8-bit KV answer on Hopper |
nvfp4 | flashinfer | 28% | B200 | 4-bit KV; 0.91x throughput; needs outlier-aware calibration (infra only) |
/v1/completions mirrors the OpenAI Completions API (subset), non-streaming or SSE. Env vars: MINI_INFER_MODEL, MINI_INFER_USE_PD=1 (back the API with the PD pipeline), MINI_INFER_PD_MODE=serial|parallel. For non-default quant/backends, build the runner programmatically and pass it to make_app(...).
uv run python -m mini_infer.api.server
MINI_INFER_MODEL="Qwen/Qwen2.5-7B-Instruct" uv run python -m mini_infer.api.server
SpeculativeRunner(target=..., draft=..., K=4).run_greedy(...) for a target + draft pair (same vocab family).modal run scripts/modal_packed_bench.py --config <throughput|prefix|flashinfer_fp8|flashinfer_nvfp4|...> reproduces the published numbers. See --help for the full list.HTTP request
│
▼
FastAPI (api/server.py) ──► ContinuousScheduler.submit(Request)
│
engine thread
│
┌───────────────────┼─────────────────────┐
▼ ▼ ▼
admit waiting prefill (B=1) batched decode
│ │
└────► PagedKVCache ◄─┘
│
▼
BlockPool (shared)
│
▼
Backend kernels / PyTorch reference
(CUDA/Triton, TPU/Pallas, Trainium/NKI) (CPU/MPS)
engine/ loads the model and owns prefill and decode.models/ is the registry: per-family nn.Modules + a shared block library (RMSNorm, RoPE, GQA, SwiGLU, MoE FFN, ...).scheduler/ owns the engine thread, admission control, the running batch, and the shared PagedKVCache.cache/ holds the block pool, the batch-aware cache, and the paged attention kernel + reference. Per-layer named StreamSpecs handle heterogeneous KV (Gemma 4) and MLA's kv_latent + k_rope; V4 adds a per-request StateCache.api/ is FastAPI with OpenAI-compatible endpoints and SSE streaming.tests/unit/): CPU-only, fast, on every CI push. Block pool, cache, attention reference, scheduler, sampler, API schemas.tests/golden/): CPU/fp32 token-for-token vs Hugging Face, catches numerical drift.scripts/): real CUDA validation, on demand.A separate weekly workflow (.github/workflows/bitparity.yml) runs the requires_model tests against pinned HF revisions (tests/_pinned_models.toml) so upstream changes don't silently affect us.
uv run pytest tests/unit/ -q # CI-equivalent (no model loads)
uv run pytest tests/golden/ -q # token-for-token vs HF
uv run pytest -m requires_model -v # bit-parity subset (downloads models)
By design:
Planned work (DeepSeek-V3/Kimi-K2 large-checkpoint runs, V4-Flash multi-GPU forward, the funded GLM-5.2 753B run) is tracked in docs/plans/roadmap-2026.md.
(Pending.)