← Blog · Models · Docs

Ternary Bonsai: a 27B reasoning model in 8 GB of RAM, on your CPU

October 2026 · Inference Server team · updated October 2026 (v1.6.0)

Update — v1.7 (October 2026): the Bonsai family grew. Ternary-Bonsai-2 small ships 2B/4B/9B in the same PQ2_0 format — a thinking model in 0.52 GB, a 9B in 2.42 GB — and unlike the 27B they answer image questions (F16 vision projectors, v1.7.1). The 27B itself remains text-only. Fetch group: bonsai-small.

Update — v1.6.0 (October 2026): the PQ2_0 kernel got a second pass — decode is now 10.8 tok/s on the same M4 (93 ms/token, 1.2× the launch figure below) and prompt processing is 2.24× faster, with batched decode verified token-exact against the launch build. Figures in the body are the v1.5.0 launch measurements unless marked otherwise.

new in v1.5.027B thinking model · 7.2 GB file 10.8 tok/s M4 (v1.6.0) · 5.2 tok/s Graviton token-exact parityApache 2.0

v1.5.0 adds ternary-bonsai-27b — prism-ml's Ternary-Bonsai-2-27B, a 27-billion-parameter thinking model whose weights are stored at an average of 1.72 bits each. The entire model is a 7.2 GB file that runs in about 8 GB of resident memory, which means a 27B-class reasoning model — with a visible reasoning_content channel and a reasoning_effort dial — now fits a 16 GB CPU host. No GPU, no quality-destroying self-quantization: this is the format the model was trained for, served with a CPU kernel we wrote for it.

The numbers in this post are measured: 8.9 tok/s decode on an Apple M4 at short context at launch (10.8 tok/s as of v1.6.0), 5.2 tok/s on an AWS Graviton c9g.8xlarge installed from the public channel, and token-for-token identical output to the upstream reference implementation on greedy decoding.

What "1.72-bit ternary" actually means

Most quantized LLMs squeeze 16-bit weights down to 4 or 5 bits after training. Ternary models go much further: every weight in the big matrices is one of just three values — −1, 0, or +1 — scaled by one shared fp16 multiplier per block of 128 weights. Packed, that lands at the advertised ~1.72 bits per weight across the model, which is how 27B parameters fit in 7.2 GB.

You can't get away with that by rounding an ordinary model — the information loss would be catastrophic. Two things make it work here:

  • It's trained ternary. The released weights were optimized in this format. (The upstream card reports 98.2% of the FP16 model's scores across their thinking benchmarks — their measurement, not ours.)
  • A Hadamard transform at runtime. Before every ternary matrix multiply, the activations pass through a fast Walsh–Hadamard transform (a 1024-point "mixing" rotation, plus per-channel sign flips). The rotation spreads each value's information across many channels, so outlier activations — the thing that normally kills extreme quantization — get smeared out before they ever meet the ternary weights. The transform is mandatory: the weights only make sense in the rotated space.

So serving this model isn't "load gguf, go" — the engine has to apply the rotation at the right seven points of the forward pass, invert it at the embedding, and decode the 2-bit weight stream. That's the port.

The CPU kernel: 1.3 → 8.9 tok/s on the same laptop

The upstream release ships a llama.cpp fork whose Metal (Apple GPU) lane is quick — ~36 tok/s on an M4 — but whose CPU lane decodes at ~1.3 tok/s on that same machine. For a format whose whole promise is "big model, small cheap host", that left most of the opportunity on the table: at 7.2 GB of weights, a laptop-class memory system should be able to stream the model many times per second.

Our first attempt — unpacking the ternary weights into our usual int8 decode packs — worked but needed 26 GB resident, defeating the point of a 1.72-bit model. The shipped kernel instead decodes the 2-bit weight stream inline, straight out of the memory-mapped file, multiplying against int8-quantized activations with exact integer math. Nothing is unpacked ahead of time:

  • 8.9 tok/s decode on an Apple M4 at short (~100-token) context at launch — 10.8 tok/s as of v1.6.0,
  • 5.2 tok/s on an AWS Graviton c9g.8xlarge, measured on a fresh install from the published channel during the v1.5.0 release gate,
  • ~8 GB resident, model load in under 10 seconds.

That's roughly 7× the upstream CPU lane on identical hardware — and it changes what the model is for. At 1.3 tok/s a 27B thinker is a curiosity; at 5–11 tok/s it's a usable private reasoning tier on a 16 GB box.

How we know the outputs are right

Aggressive quantization earns extra skepticism, so the port gate was token-exact parity: we captured greedy (temperature 0) completions from the upstream fork's own server on the same gguf file, then required our engine to reproduce the exact token sequence — not "similar text", the same tokens. The shipped kernel passes 4/4 parity fixtures token-for-token, plus 8/8 chat-template goldens (including thinking-off and reasoning_effort variants), and the full multi-model regression gate ran with zero failures before release.

In other words: whatever you think of 1.72-bit weights, our serving of them is bit-faithful to the reference implementation. The quantization trade-off is the model's published one, not something our kernel adds.

Run it

On a 16 GB (or larger) Linux host:

curl -fsSL https://inference-server.searchblox.com/install | sudo bash
sudo ./fetch-models.sh ternary-27b     # 7.2 GB download
# already installed? just fetch the group and restart the service

Then ask it something — and note enable_thinking, because thinking defaults off for this model:

curl -s http://127.0.0.1:8081/v1/chat/completions \
  -H "Authorization: Bearer $SEARCHAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"ternary-bonsai-27b",
       "messages":[{"role":"user","content":"Why is the sky blue?"}],
       "enable_thinking":true, "max_tokens":768}'

The response carries the model's deliberation in reasoning_content and the answer in content, like our other thinking models. You can pass reasoning_effort (low / medium) to trim deliberation — but budget max_tokens generously either way: even at low effort this model routinely spends ~360 tokens thinking before it answers. A too-small max_tokens gets you an empty content with the budget consumed by reasoning.

Honest caveats

  • Decode slows as context grows. The short-context figures above are the best case; across a 414-token generation the M4 averaged ~5.3 tok/s. Long documents plus long reasoning chains will sit at the lower end. (This is a known optimization target, not a format limit.)
  • Text-only in this release. Upstream publishes a vision projector for the model and we mirror it alongside, but our loader doesn't support it yet — image requests return a clean error. The catalog lists the model as text-only.
  • It's a thinker, not a chatbot. Per-answer latency is dominated by reasoning tokens; at 5–9 tok/s that's tens of seconds per considered answer. Use it where you'd use our other reasoning tiers: analysis, review queues, agent planning — with the fast small models handling interactive traffic on the same server.
  • Never self-requantize other models to this format. The ternary types only work for checkpoints trained in them; converting an arbitrary gguf produces silent garbage. Use the published file.

Model: prism-ml Ternary-Bonsai-2-27B (Apache 2.0), served as ternary-bonsai-27b from the ternary-27b fetch group. Measurements: M4 numbers on Apple M4 (release build); Graviton number from the v1.5.0 fleet gate on c9g.8xlarge installed from the public channel; upstream CPU / Metal comparison numbers measured with the upstream fork on the same M4 and gguf file.