Why wait for someone to optimize an LLM for your GPU when an AI agent can build a specialized inference engine in a few hours?
Summary • Quick start • What crit solves • Case studies •
How crit works • API • CLI • crit-hub • Limitations • Documentation
Summary
crit is a toolkit for optimizing or building inference engines tailored to your hardware and desired model.
Quick start
Give one of these prompts to your coding agent and specify your target LLM or target engine:
Use github.com/crit-labs/crit to build an optimized inference engine for <huggingface_url>.
Use github.com/crit-labs/crit to optimize my own inference engine in <engine_reference>.
What crit solves
The boring stuff has already been built ⏱️
Requests, memory management and cleanup eat up development time before optimization even starts.
critsupplies a clean foundation so you can get to the work that makes your engine fast.
Curated high-impact inference strategies 📊
Don't rebuild inference strategies from scratch. We studied established inference engines and distilled their core speedup strategies into reusable code, so you can apply what works to your model and hardware quickly.
Physics-based evaluation suite 🔬
A benchmark score alone doesn't tell you what's still left on the table.
critmeasures your hardware's limits and shows how close your engine gets, with quality and correctness checks along the way.
Case Studies
We gave Claude Opus 5.5 a target LLM along with the crit repository and let it cook autonomously.
| What it shows | Evidence |
|---|---|
| Fast engines | Qwen3.8-27B on one RTX 5090: 189 tok/s sampled decode, 4.4x its own first working candidate, and 8,295 tok/s prefill, with the full 262,144-token context |
| Quality is your call | The same model built once per quality mode: as_quantized prefills 2.3x faster, preserve stays within 0.4% of the exact reference's perplexity, and both decode at the same speed |
| Hours, not weeks | 3h 55m and five candidates from the run's first command to the selected engine, $51 at API list prices |
| Every step is evidence | Each candidate is sealed with its exact source and its Eval result, so the tables below are a record, not a recollection |
| Honest about what is left | Decode runs at 53% of the ceiling Eval measures for it, prefill at 46%; Eval reports each gap next to the score |
| Next | Gemma 4 31B (dense), and Qwen3.8-Flash-Next and Nemotron 3.5 Super 120B, mixture-of-experts models larger than VRAM |
Dev box details
| Part | Spec |
|---|---|
| GPU | NVIDIA GeForce RTX 5090, 32 GB, 450 W power limit |
| PCIe | Gen 4 x16 (the card supports Gen 5; the board's slot is Gen 4) |
| CPU | AMD Ryzen 5 9600, 6 cores, 12 threads |
| RAM | 96 GB DDR5 (2x 48 GB) at 4,800 MT/s |
| OS | Windows 11 Home (build 26200), engines run in WSL 2 on Ubuntu 24.04 |
| Driver and CUDA | NVIDIA driver 596.49 (CUDA 13.2), CUDA toolkit 13.1 |
📋 Dense LLM, Qwen architecture → Inspect engine
Goal: Get to a fast, working engine for a dense model as quickly as possible. Limit: A fixed candidate budget.
|
3h 55m Development time |
189.0 tok/s Sampled decode |
8,295 tok/s Prefill |
154 ms TTFT |
+3.6% Perplexity vs reference |
0.134 Mean KL divergence |
mtp4-nvfp4-mma
runs Qwen3.8-27B in its original NVFP4 weights on one RTX 5090, with the model's full 262,144
tokens of context, in the as_quantized quality mode: Eval judges it against the checkpoint run the
way it was quantized. One coding agent built it on crit's current main as a Python engine on Core:
cuBLASLt for the prompt's 4-bit and 8-bit matrix multiplies, flash-linear-attention for the
gated-delta layers, and its own Triton and CUDA kernels for attention and decode. Each round drafts 4
tokens with the model's own built-in draft layer and verifies them with rejection sampling on the
GPU, so sampled requests draft too. Its own tensor-core NVFP4 GEMV reads the 4-bit weights at
1.2–1.4 TB/s, against 0.9–1.0 TB/s for cuBLASLt at the same shapes.
| Candidate | What changed | Sampled decode | Prefill | TTFT | Dev time, cumulative | Agent tokens, cumulative | API equivalent, cumulative |
|---|---|---|---|---|---|---|---|
| 1 | Working engine: reads the checkpoint as stored, 8-bit KV cache, one CUDA graph per token, no drafting | 43 tok/s | 6,027 tok/s | 290 ms | 0h 55m | 39.2M | $13.96 |
| 2 | Drafts 3 tokens ahead with the model's draft layer, 8-bit decode GEMVs, fused prompt kernels (missed a gate: short prompts reached their first token 12% later at 65,536 tokens of context than at 262,144) | 134 tok/s | 7,771 tok/s | 219 ms | 1h 49m | 69.6M | $23.26 |
| 3 | Drafts 4 tokens ahead; short prompts run as captured CUDA graphs | 155 tok/s | 7,660 tok/s | 169 ms | 2h 44m | 97.7M | $31.51 |
| 4 | Its own tensor-core NVFP4 GEMV for decode and verify, using the GPU's FP4 conversion instruction | 181 tok/s | 8,218 tok/s | 156 ms | 3h 27m | 135.3M | $47.15 |
| 5 | Drafts from NVFP4 copies of the draft layer and output head; the verify pass keeps the stored weights | 189 tok/s | 8,295 tok/s | 154 ms | 3h 55m | 149.3M | $50.75 |
The last three columns are running totals from the run's first command (06:28 UTC) to each
candidate's seal, and for candidate 5 to its selection, so its row is the whole run. The run spent
its budget of three candidates on 1 to 3, then resumed for two more; development time leaves out the
3h 26m in between. Candidates 1 to 3 were built from crit and crit-eval alone; before planning 4 and
5 the agent had read the notes of earlier runs on this model. Agent tokens and API equivalent count
the agent's messages in that span, from its Claude Code session transcript: Claude Opus 5.5 for
every message, priced at Anthropic's API list prices ($4/MTok input, $20/MTok output, $8/MTok 1-hour
cache writes, $0.20/MTok cache reads; 99% of the tokens are cache reads).
Measured with crit-eval (crit-eval-v0.2.1) at the full 262,144-token context. Decode is sampled at the
model's own settings (temperature 1.0, top-p 0.95, top-k 20), the geometric mean over six prompts,
from a chat question to a 14K-token agent session; Eval decodes each engine's own output.
- Long context: a prompt that fills all 262,144 tokens gets its first token after 144 s, then decodes at 110 tok/s. On prompts outside Eval's speed suite, in 8 languages and code, it decodes at 131 tok/s.
- Quality: the worst path uses 70% of the
as_quantizeddrift budget. Greedy decoding reaches 194 tok/s, and drafting never changes the output: it is token for token what plain decoding produces. - What is left: a verify pass takes 15.1 ms against the 12.5 ms its weight reads need, and a draft step 1.24 ms against 0.62 ms. Eval projects 251 tok/s if both reached their floors.
📋 Quality modes comparison → as_quantized engine • preserve engine
Goal: Show what each quality mode buys, on the same model, GPU and agent. Limit: Five candidates per run.
crit judges an engine's quality in one of two modes, chosen per run. preserve, the default, holds it
to the checkpoint's own arithmetic with exact activations, within a 5% drift budget: the weights are
used as stored, but the prompt's activations stay in 16 bits unless quality shows they can go lower.
as_quantized holds it to the checkpoint run the way it was quantized, with 4-bit and 8-bit
activations where the checkpoint declares them, within a 10% budget. We built Qwen3.8-27B on one RTX
5090 once in each mode: mtp4-nvfp4-mma
from the case study above, and
mtp-adaptive-width,
which moves between 2 and 8 drafts per round: wider after a round that kept every draft, narrower
after one that kept fewer.
as_quantized |
preserve |
|
|---|---|---|
| Sampled decode | 189.0 tok/s | 185.3 tok/s |
| Greedy decode | 194.3 tok/s | 195.5 tok/s |
| Prefill | 8,295 tok/s | 3,580 tok/s |
| TTFT | 154 ms | 337 ms |
| Decode at 262,144 tokens of context | 110 tok/s | 79 tok/s |
| Perplexity vs its mode's reference | +3.6% | +0.4% |
| Mean KL divergence, worst path | 0.134 | 0.022 |
| Share of its mode's drift budget | 70% | 57% |
| Development time, five candidates | 3h 55m | 3h 20m |
| API equivalent | $50.75 | $36.81 |
- Decode is a tie. A decode pass reads every weight once, and both engines read the same stored 4-bit and 8-bit weights, so the quality mode barely moves it.
- Prefill is where the modes split. A long prompt is compute-bound.
as_quantizedmay feed 4-bit activations to the GPU's 4-bit tensor cores;preservekeeps the 4-bit layers' activations in 16 bits and widens their weights to match. Eval prices each mode's ceiling accordingly:preservereaches 77% of its 4,676 tok/s,as_quantized46% of its 17,841 tok/s. - Quality follows the reference.
preservestays within 0.4% of the exact model's perplexity.as_quantizedis measured against the quantized checkpoint, whose own run-to-run spread on long prompts is wide, so Eval judges each of its texts against twice that spread. - Both runs were honest about failures. Two of
preserve's five candidates failed before Eval: one read log-probabilities at the wrong stride, and one kept its first engine in memory when Eval reopened it at the full context. Each fix became the next candidate.
Pick preserve when answers must match the original model; pick as_quantized when prompt
throughput matters more, as with agents that send long prompts.
preserve candidate |
What changed | Sampled decode | Prefill | TTFT | Dev time, cumulative | Agent tokens, cumulative | API equivalent, cumulative |
|---|---|---|---|---|---|---|---|
| 1 | Working engine: its own GEMVs on the stored 8-bit and 4-bit weights in a CUDA-graphed step, 16-bit prompt matrix multiplies, 8-bit KV cache | 52 tok/s | 2,763 tok/s | 488 ms | 0h 45m | 31.1M | $11.41 |
| 2 | Drafts with the model's draft layer, each round in one CUDA graph; the GPU's FP4 conversion instruction in the NVFP4 GEMV (failed the serving check: log-probabilities read at the wrong stride) | – | – | – | 1h 33m | 61.4M | $19.78 |
| 3 | Candidate 2 with the stride fixed (failed Eval: the first engine stayed in memory when Eval reopened it at 262,144 tokens) | – | – | – | 1h 54m | 79.9M | $24.40 |
| 4 | Memory released on close, an 8-bit draft layer, 8-bit prompt activations where quality allows | 160 tok/s | 3,269 tok/s | 361 ms | 2h 26m | 90.4M | $27.26 |
| 5 | Draft width moves between 2 and 8 with the previous round's acceptance, drafting up to the context's end, a faster prompt convolution | 185 tok/s | 3,580 tok/s | 337 ms | 3h 20m | 127.2M | $36.81 |
Measured like the case study above. The preserve run was built from crit and crit-eval alone, from
its first candidate to its last.
How crit works
An agent, or you, builds the engine as a series of candidates. Each candidate is a complete, servable engine, measured the same way, and each next one is chosen from the last one's numbers.
flowchart LR
recon["<b>Reconnaissance</b><br/><small>read model spec, hardware config, workload, crit guides and docs</small>"] --> limits["<b>Evaluation</b><br/><small>measure GPU<br/>limits per KPI</small>"]
limits --> build
subgraph loop ["Candidate loop"]
direction LR
build["<b>Build candidate</b><br/><small>Core foundation<br/>plus Inference strategies</small>"] --> measure["<b>Evaluation</b><br/><small>scores vs limits,<br/>quality gates</small>"]
measure --> decide{"<b>Decision<br/>algorithm</b><br/><small>prioritize biggest<br/>gap first</small>"}
decide -- "decision:<br/>new candidate needed" --> build
end
decide -- "decision:<br/>exit condition met" --> final["<b>Final candidate</b><br/><small>sealed and served<br/>by crit serve</small>"]
📗 Core → Full guide
Core is the plumbing every engine needs, distilled by studying multiple inference engines.
We took apart how each engine handles requests, memory, text and model files, kept the base features they all rebuild, and turned them into one native SDK. Your engine keeps what makes it fast: the model, its kernels, its layouts and its execution strategy.
| Part | What it does |
|---|---|
| Runtime | Admits, schedules, cancels and retires requests, and publishes their output |
| Engine libraries | A C ABI that turns an engine into a shared library, with stock Eval and serve adapters |
| Memory | Resource budgets, reservations and host-memory admission |
| Text | Tokenizer and chat template loading, stop strings and streamed reply text |
| Sampling | Sampling rules, penalties, log-probabilities and greedy draft acceptance |
| State cache | KV pages, recurrent state slots and prefix-cache snapshots |
| Model files | Safetensors and GGUF checkpoints, config.json, generation defaults and stop tokens |
| Files | Strict JSON, read-only, chunked and mapped file reads, SHA-256 |
| Structured output | Token masks for choices, JSON schemas and grammars |
| Device graphs | CUDA graph capture and replay to cut launch overhead |
| CUDA backend | Allocations, streams, shared device memory and device time per category |
| Tracing | A bounded lifecycle trace with operator ranges and NVTX |
| Contracts | Who owns what between Core and your engine |
Try the Core + Inference walkthrough.
📕 Inference → Full guide
Inference is a curated collection of high-performance strategies.
We studied bespoke inference engines and what makes them fast and turned every strategy that holds beyond one model or GPU into an entry. Your agent picks the entries that match its engine's bottleneck and builds them into the engine on Core.
Examples:
| Bottleneck | Example entries |
|---|---|
| One token per pass | Speculative decoding, draft trees, device-side rounds, EAGLE-3 and DFlash drafters |
| Weight bytes per pass | Quantized projections, with the GEMM and GEMV kernels from Kernels' providers |
| Long context | Attention state, prefix checkpoints, session scheduling |
| Model larger than VRAM | Recon names the placement scenario. Sparse models: a device expert cache with expert residency and the checkpoint's drafter from the first candidate, misses computed on the CPU or copied by measured dispatch; dense models: dense layer offload |
| Embedding and n-gram tables | Lookup table rows: rows read on demand from disk, no read-ahead |
| Hybrid recurrent layers | Recurrent state recovery |
All entries are in the full guide, or run crit inference search <query>.
Every entry has the same layout:
| Part | What it holds |
|---|---|
README.md |
When to use it, how it works, a worked example, how to adapt it, its costs and how to validate it |
code/ |
Real implementation code, C++, CUDA or Python, with runnable examples |
entry.json |
When to use and avoid it, what the code provides, what the engine must supply and what it must preserve |
What gets in: contribution criteria
| Criterion | Means |
|---|---|
| Shown value | A measured gain, a reproduced and fixed bug, or a capability engines had to rebuild themselves |
| No overfitting | Nothing in it is true only for the model or GPU where it was found |
| Broad use | Dense, MoE, hybrid and multimodal models and other CUDA GPUs use it unchanged or are unaffected |
📘 Kernels → Full guide
Kernels is a register of kernel collections.
crit hosts no compute kernels. Your agent takes each operation's kernel from an established
provider, or writes its own when none fits, then measures it against its floor in Eval's kernel
profile and against the best other provider for the same operation.
| Provider | Covers |
|---|---|
| Hugging Face Kernel Hub | Community kernels for attention, GEMMs, activations, normalization, MoE and more, built per framework version and backend (CUDA, ROCm, Metal, XPU, CPU) |
| cuBLASLt | GEMMs in BF16/FP16, FP8 (per-tensor and per-row scales), block-scaled NVFP4 and MXFP8 on the GPUs that have them; epilogues |
| CUTLASS | GEMM and convolution templates for layouts and fusions cuBLASLt does not take, grouped GEMMs, mixed-input GEMMs (4- or 8-bit weights widened to 16 bits inside the main loop) |
| cuDNN | Fused attention (SDPA), normalization |
| FlashAttention | Prompt and decode attention, paged KV, sliding windows, softcap |
| FlashInfer | Paged and ragged attention, FP8 KV, MLA, cascade attention, top-k / top-p / min-p sampling, some GEMMs |
| flash-linear-attention | Chunked prefill and recurrent decode for gated delta, Mamba-style, RWKV and other linear attention |
| Triton | Writing a missing kernel in Python, compiled per device; ahead-of-time compilation for native engines |
| vLLM and SGLang kernels | Fused MoE, quantized GEMMs (GPTQ, AWQ, Marlin's 4-bit weights against 16-bit activations, FP8), attention backends, custom all-reduce |
| ggml | GGUF block formats (Q4_0 .. Q8_0, K-quants, IQ) on CUDA, Metal, Vulkan, ROCm and CPU |
| OpenBLAS, oneDNN | CPU GEMMs and fused CPU operators |
| Composable Kernel, hipBLASLt | GEMMs and attention on AMD GPUs |
| MLX, Metal Performance Shaders | GEMMs, attention and quantized matmuls on Apple GPUs |
Kernel workbench
When the agent writes a kernel, or adapts a provider's, crit kernels new gives it a workbench folder with
the checkpoint's own shapes, an FP64 reference and a yardstick such as cuBLAS. crit eval workbench check then
compares the kernel with the reference on every shape and requires the same bits on a repeat, and bench times
it against the yardstick and the GPU's floor. See the workbench.
📚 Eval → Full guide
crit-eval is a speed and quality measurement tool to compare inference engines honestly.
There is no shared way to measure a bespoke inference engine.
Different bespoke inference engines report vastly different types of numbers. An honest comparison requires downloading and running the engines yourself.
crit-evalfinally makes apples-to-apples comparison possible.
Physics-based evaluation lets you see what performance is left on the table.
crit-evalmeasures your GPU's memory bandwidth and matrix-multiply rates and turns them into a limit for each score. Its evaluation report can tell your coding agent how much performance can still be squeezed out of your inference engine.
Speed that can't hide a quality loss.
Many inference engines push decode and prefill speed with no regard for quality.
crit-evaltreats quality as a first-class KPI. It measures not only inference speed but also quality regressions.
A hardware report rounds out the evaluation.
The same model on the same inference engine but on two different machines can also lead to different inference speeds, even if the GPU is the same. We make this transparent by also reporting the runner's hardware configuration.
🔌 API → Overview · Full guide
crit serve loads a sealed candidate, keeps it resident and answers OpenAI-compatible
chat requests, including streaming, tool calls, stop strings and structured output. The same engine
also speaks the Anthropic Messages, OpenAI Responses and Ollama APIs,
so Claude Code, Codex and local chat UIs need only a base URL, and GET /metrics serves
Prometheus metrics. Eval measures every candidate through this same
server, so the numbers you read are the numbers you get. Engines implement the
adapter contract.
📓 CLI → Overview
One crit command drives everything: crit run keeps the run's records (plans, candidates,
measurements, selection, cost), crit eval runs the common suite, crit serve serves an engine,
crit pull installs a crit-hub engine, and crit kernels, crit models and crit inference reach the other parts. Records are written
only through it, so each plan, candidate, Eval and seal is checked as it is recorded.
Miscellaneous
🔭 crit-hub → Repository
crit-hub collects bespoke inference engines in one repository
to make discovery super easy. Each engine pins its runnable source, the exact checkpoint it was built on, what
was measured, its known limits and a link to the repository of whoever contributed it.
Use them as jump-off point for further optimization.
crit pull crit-labs/nvfp4-gemv-int8kv installs one: it checks the card's requirements against your GPU and CUDA,
fetches the source at its pinned commit, builds it, fetches and checks the checkpoint, and then
crit serve --engine crit-labs/nvfp4-gemv-int8kv serves it (Installed engines).
🚫 Limitations
| Area | Current status |
|---|---|
| Multi-GPU setups | Expected to work with engine-specific partitioning and communication; not yet tested end to end. |
| macOS | Not yet tested. Current CI covers Windows and Linux. |
| AMD and Apple GPUs | No bundled ROCm or Metal backend. The current GPU backend uses CUDA. |
| Model formats | Hugging Face safetensors, PyTorch state dicts (.bin, .pt) and Optimum ONNX exports, unquantized or quantized as FP8, NVFP4, compressed-tensors int, AWQ, GPTQ, MLX, bitsandbytes or MatMulNBits, and GGUF, a lone .gguf included: its config, tokenizer and chat template are derived from its metadata for the llama, qwen2, qwen3, qwen3moe, qwen35 and qwen35moe architectures and byte-level BPE tokenizers (others need the base model's files beside it), and Eval's reference reads those architectures. A new format is one ModelFormat in crit_core.model. Core's C++ model library reads safetensors only; engines read a GGUF's tensors through crit_core.model's offsets. |
Documentation
| To… | Read |
|---|---|
| Build and compare engines | Building engines · Agent instructions · Run records |
| Integrate the native runtime | Core · Contracts · Core + Inference walkthrough |
| Choose or adapt a technique | Inference |
| Find and adapt kernels | Kernels |
| Quantize weights and record the transform | Models |
| Compare speed, quality and speculative output | Eval |
| Serve an engine through the OpenAI, Anthropic or Ollama API | API · Serving · Adapter contract |
Find a crit command |
CLI |
Change crit or run its tests |
What belongs in crit · Contributing · Licensing |
| See release history | Changelog |
Metadata
Release files for crit 0.13.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| crit-0.13.0.tar.gz | 640.9 kB | With provenance | Details for crit-0.13.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| crit-0.13.0-py3-none-any.whl | Python 3 | none | any | Details for crit-0.13.0-py3-none-any.whl |
Total release size: 954.9 kB
Release files / crit-0.13.0.tar.gz
| Download URL | crit-0.13.0.tar.gz |
|---|---|
| Size | 640.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f6ee7c0b4e867ae2d6fbfb57e434b7a6fa7df1b7a41115a99a80fae522d25fa2
|
|
BLAKE2b-256 checksum How to use checksums |
738fa01379280a240c070dc7b4d55a2ff6bf75f41f97f4a4a51a982f15cc2a52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logRelease files / crit-0.13.0-py3-none-any.whl
| Download URL | crit-0.13.0-py3-none-any.whl |
|---|---|
| Size | 314.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
12e810bc82a9e8fd4ec01dd8138ecd05fa47d8528f953c1f1f3cd18b2fa34f3d
|
|
BLAKE2b-256 checksum How to use checksums |
a6ae65669b6928153b3ccd90cafb1a3f3399c8c27b512f4f90886370f19a2911
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logSecurity for crit 0.13.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for crit-0.13.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.