KLD: mean / median KL divergence of the next-token distribution against the BF16 pi model, measured with exllamav3's qbench. The first figures use an
internal sysadmin/agent evaluation set (not published; separate from the calibration data); pub. uses turboderp's public qbench_prompts_gen.json
trace (from turboderp/Qwen3.8-27B-exl3), so anyone can reproduce it. Lower is better.
Context: largest --max-model-len validated with MTP 3, 2 sequences, fp8 KV cache and ≥100 MiB of VRAM really free at peak.
Needle: 8 facts spread through one prompt of that length, all 8 returned. Tools: the real request of the opencode agent (system prompt + 11 tools),
3 cases (fresh request, after a long story, full multi-turn session) × 10 tries × 3 configurations (xhigh without temperature, xhigh at T=0.3,
thinking off at T=0.3). Agent: 4 sysadmin tasks × 2 with opencode run and real tool execution.
Reasoning: total reasoning tokens over 6 checkable problems × 8 tries at xhigh (temperature 0.6). : forced tokens, temperature 0.
Variants / revisions
Table with columns: Revision, Linear layers, lm_head, Vision, MTP head, Embeddings, Size, KLD (ours / pub.)
Revision
Linear layers
lm_head
Vision
MTP head
Embeddings
Size
KLD (ours / pub.)
main
3.85 bpw, SC allocation
5 bits
6 bits
4 bits
INT8 per row
14.6 GB
0.0074 / 0.0070
Quantization recipe
Table with columns: Step, What we did
Step
What we did
Calibration trace
Generated by Qwen3.8-27B-pi itself (reasoning_effort: xhigh, MTP enabled) with exllamav3's sc_trace on 196 prompts: 157 from exllamav3's own sc_trace set (logic, maths, code, science; English and Spanish) and 39 of our own generic sysadmin/agent conversations (invented examples, no real system data; not published); 196 sequences × 2048 tokens used for conversion
Reference and sensitivity
sc_rfn_probe against a 2.00 bpw reference, sc_measure (per-tensor sensitivity on the trace)
Bit allocation
sc_optimize at the target rate with a 5-bit lm_head → per-tensor recipe (quantization_config.json)
Tokenizer, chat template and generation config unchanged from bytkim/Qwen3.8-27B-pi (XML tool calls, qwen3_coder). The dflash2/ companion of the source repo is not included.
EXL3 is not a native vLLM format. Two plugins are needed, both loaded through vLLM's vllm.general_plugins entry point:
Continuum-AI-Corp/OrcaSAQ2-kernel (commit 7ddffb12c00c24b8bc6b1164723c04ac490a91bc): EXL3 linear
layers in vLLM through exllamav3's kernels, and INT8 / packed embed_tokens and lm_head for qwen3_5 models (no vLLM patch needed). Its README describes it
as a "correctness path".
qwen38_exl3_vision (ours, version 0.2.0, in plugin/qwen38_exl3_vision/; 0.2.0 also adds EXL3 support for MoE experts, unused by this dense model): drops the separate attn.{q,k,v}_proj tensors of the EXL3 vision tower (vLLM uses the fused
attn.qkv) and pads input dimensions to multiples of 128 for layers that need it (4304 → 4352).
Python 3.12 is required (the exllamav3 wheels are cp312), plus a CUDA 13.2 toolkit (nvcc) for flashinfer's JIT kernels on first start:
3.70bpw branch, 226k context: the same command with --revision 3.70bpw --max-model-len 226000 --kv-cache-memory-bytes 8850000000.
Important notes
Memory. The driver reserves 453 MiB of the 24,576 MiB that vLLM cannot use: read the real headroom with
nvidia-smi --query-gpu=memory.used,memory.free --format=csv, not as 24,576 − used. Size the KV cache with --kv-cache-memory-bytes, never with
--gpu-memory-utilization (the profiling of this hybrid GatedDeltaNet model under-estimates the activations). --max-num-batched-tokens 1024 and
expandable_segments lower the peak by ≈350 MiB at no measurable cost. With MTP, KV capacity grows in block-sized steps: check the GPU KV cache size
line (8,420,000,000 B → 216,433 tokens; 8,850,000,000 B → 227,439) and keep --max-model-len below it.
Measured peaks (2 sessions, vision): 3.85 bpw 311 MiB really free (719 MiB at rest); 3.70 bpw see the benchmark table. Leave the GPU for the LLM: a desktop session on the same GPU would eat this margin.
Hugging Face page warnings: the "Model size" shown by Hugging Face (≈8B params) counts the packed storage elements of the safetensors files
(EXL3 packs ≈4 weights into each int16), not parameters: this is the 27B model at ≈3.85 bits per weight. The Configuration Parsing Warning
"quantization_config.bits must be an integer" is also expected: for EXL3 is the average bits per weight of a per-tensor allocation
(the per-tensor values are in ); exllamav3 and the OrcaSAQ2 plugin read it as a float.
Using it with opencode (how we tested)
This checkpoint is meant to be used as a coding / sysadmin agent from opencode (tested with opencode 1.18.33). Tool calling was
measured with the real opencode request (its long system prompt and 11 tools, captured and replayed against vLLM) and with opencode run agent tasks;
context, speed, memory and image tests were run directly against the vLLM API.
limit.context = --max-model-len (226000 for the 3.70bpw branch); with output: 22000 opencode compacts the history at ≈context − 22k tokens of input.
Declare "temperature": true in the model, otherwise opencode sends no temperature to a custom model (checked by capturing the requests).
The agent tasks ran at temperature 0.3; the tool-call test at both T=1.0 (model default) and T=0.3.
Without attachment / modalities opencode will not send images; drop them if you serve with --language-model-only.
Start opencode inside a project directory (it takes a git snapshot before each step); for opencode run from scripts, close stdin (</dev/null).
Benchmarks (single RTX 3090 24 GB, 300 W)
Table with columns: Test, main 3.85 bpw (215,000), 3.70bpw (226,000)
Test
main 3.85 bpw (215,000)
3.70bpw (226,000)
KV pool
216,433 tokens (KV 8.42e9 B)
227,439 tokens (KV 8.85e9 B)
8 facts in one long prompt (greedy)
8/8 at 61,675, 123,142 and 195,210 tokens (524 tok/s at 195k)
not measured
2 simultaneous sessions
2 × ≈94k, 8/8 in both, 698 tok/s aggregate
not measured
Decode, 1 request, t=0 / 0.6
76.8 / 75.5 tok/s (MTP: 70 % of drafts accepted)
not measured
Decode, 2 concurrent, t=0 / 0.6
For comparison, the base Qwen3.8-27B EXL3 3.85 reasoned 23,221 tokens at xhigh on the same problems (pi: 17,070, −26 %), with the same context and memory.
Limitations
Quality was not evaluated with standard benchmarks; the checks are KL divergence against BF16, long-context retrieval, tool calling, vision on synthetic images,
short reasoning problems and agent tasks.
The full 262,144 context does not fit at this rate on 24 GB with MTP and vision.
Decode is ≈20 % slower than the W4A16 sibling on Ampere; the OrcaSAQ2 plugin is young and describes itself as a correctness path.
The fp8 KV cache uses the default scale 1.0. All numbers come from one machine and one software stack.
plugin/qwen38_exl3_vision/: our vLLM plugin (vision tensors and padding; 0.2.0). scripts/embed_int8.py: re-encodes embed_tokens to INT8 per row.
LICENSE (Apache-2.0) and NOTICE (the pi model's notice, plus this quantization).
Feedback welcome
Reports of installations, both working and failed ones, are very welcome in the Community tab: GPU(s), driver,
vLLM / exllamav3 / plugin versions, the full command line and the result (speed, context, or the error message).
exllamav3 and the EXL3 format by turboderp (MIT), including the SC pipeline, qbench and the public
evaluation trace; EXL3 is based on QTIP (Tseng et al.).
Apache-2.0, same as the pi model and Qwen3.8-27B; the pi model's NOTICE is kept in NOTICE. The OrcaSAQ2 plugin and exllamav3 are not
redistributed here; install them from their repositories under their own licenses.
Measured 2026-10-09 (EXL3 3.85 and W4A16) and 2026-10-10 (3.70 branch, installed from this repo) at a 300 W power limit.
3.70bpw
3.70 bpw, SC allocation
5 bits
6 bits
4 bits
INT8 per row
14.2 GB
0.0093 / 0.0085
Conversion
exllamav3 1.5.1 convert.py with the recipe and the calibration trace, vision 6 bits, MTP 4 bits; codebook mul1, output scales always
Embeddings
embed_tokens re-encoded to INT8 per row (absmax / 127, BF16 scales) in the OrcaSAQ2 plugin's format; script included
bits
quantization_config.json
VLLM_USE_FLASHINFER_SAMPLER=0 is recommended by the OrcaSAQ2 plugin (flashinfer's JIT sampler is fragile; torch's is equivalent).
Use --tool-call-parser qwen3_coder (Qwen3.8 XML tool calls) and --reasoning-parser qwen3. reasoning_effort accepts only xhigh, medium and low
(high returns HTTP 400); they are prompt instructions, not limits. For a hard cap, thinking_token_budget was validated with vLLM 0.29 on the W4A16 version
(not re-checked with this stack).
On Ampere (sm86) the fp8 KV cache works with the FlashInfer attention backend (the default).
Why it is slower than W4A16 on Ampere (per-layer micro-benchmarks on the 3090): with 1 row the EXL3 kernel matches Marlin, but with the 4–8 rows of an
MTP verification step decoding the trellis becomes compute-bound and costs ≈6 ms more per step; the plugin path (BF16↔FP16 conversions, fused layers run
piecewise) adds ≈2.6 ms, and a higher-bit lm_head ≈1–2 ms. Ada / Blackwell GPUs should lose less. Prefill is ≈8 % slower.
Stability (our machine): our RTX 3090 hung twice at its factory 370 W limit with very long prompts (once inside an exllamav3 kernel, once with an
unrelated model); after setting a 350 W (now 300 W) power limit and a more aggressive fan curve, none of the tests here (up to 245k-token prompts) hung again. If you
see Xid errors in dmesg, try a lower power limit first.
135.2 / 125.5 tok/s
not measured
Images (4 synthetic tasks, 18 checks)
4/4 tasks, 18/18; follow-up turn on the same image (prefix cache) 2.7 → 1.4 s