Run a 27B‑class LLM locally with one command.
| Variant | Footprint | Quality | Hardware |
|---|---|---|---|
| Bonsai 2 (PTQ1_0) | 6.0 GB | 98.2% of FP16, thinking model | CPU, or any GPU with ≥8 GB VRAM |
| Bonsai 2 (PQ2_0) | 7.2 GB | 98.2% of FP16, thinking model | CPU, or any GPU with ≥10 GB VRAM |
| 1-bit (Q1_0) | 3.9 GB | 89.5% of FP16 | CPU, or any GPU with ≥6 GB VRAM |
| Ternary (Q2_g64) | 7.2 GB | 94.6% of FP16 | CPU, or any GPU with ≥10 GB VRAM |
| 1-bit + DSpark | 5.7 GB | Lossless speedup (legacy drafter) | CUDA GPU (speculative decoding) |
| Ternary + DSpark | 9.1 GB | Lossless speedup (legacy drafter) | CUDA GPU (speculative decoding) |
Bonsai 2 is the new generation (Qwen3.8-27B ternary). It needs the updated PrismML llama.cpp fork (
prism-b10658+) —start.shupdates/re-clones it automatically. The oldternaryvariant now uses theQ2_g64file (the legacyQ2_0file is refused by post-rebase fork binaries).⚠ Known upstream bug (PrismML-Eng/llama.cpp#180): repacking the F32 Hadamard helper tensors segfaults the loader for PQ2_0 files (PTQ1_0 is unaffected).
start.shtherefore auto-applies the verified--no-repackworkaround for thebonsai2-pq2variant only (slightly slower). SetBONSAI2_REPACK=1to re-enable repacking once upstream lands the fix.
No cloud API. No subscription. Your data stays on your machine.
git clone https://github.com/Mikehutu/bonsai-runner
cd bonsai-runner
# Default: 1-bit (3.9 GB, works on most GPUs)
bash start.sh
# Bonsai 2 — the latest generation (thinking model, 262K context):
bash start.sh bonsai2 # 6.0 GB PTQ1_0 (default pick)
bash start.sh bonsai2-pq2 # 7.2 GB PQ2_0 (fastest on modern NVIDIA GPUs)
# Or pick a variant:
bash start.sh ternary # 7.2 GB, higher quality
bash start.sh 1bit+dspark # with speculative decoding (legacy drafter)
bash start.sh ternary+dspark # quality + speedTip: Run bash start.sh with no arguments to auto-detect your hardware
and get a model recommendation. On interactive terminals, you'll be prompted
to choose a variant and a context size. On SSH/CI, it defaults to 1bit
with the model's default context (~32K).
The script will:
- ✅ Check prerequisites (cmake, make, python3)
- ✅ Install
huggingface-hubfor model downloads - ✅ Clone and build the PrismML llama.cpp fork with CUDA/Metal/CPU support
- ✅ Download your chosen model from HuggingFace (one‑time)
- ✅ Start an OpenAI‑compatible server on
http://0.0.0.0:8080
To stop the server: press Ctrl+C or run ./stop.sh in another terminal.
Requirements: cmake, make, python3, and a C++ compiler (build-essential on Linux, Xcode CLI on macOS).
# Debian/Ubuntu
sudo apt install cmake build-essential python3 python3-pip
# macOS (Xcode CLI)
xcode-select --install
brew install cmakeSee .env.example for all available environment variables. Copy it to .env and adjust:
cp .env.example .env| Env var | Default | Description |
|---|---|---|
PORT |
8080 |
Server port (auto‑fallbacks to 8081, 8082 if busy) |
HOST |
0.0.0.0 |
Bind address |
NGL |
99 |
GPU layers (Metal/CUDA — ignored on CPU) |
CONTEXT_SIZE |
0 |
Context length in tokens. 0=model default (~32K). See Context Sizing below. |
PARALLEL |
0 |
Server parallelism: 0=auto, 1=single-request (benchmark-safe) |
HF_HUB_OFFLINE |
(unset) | Set to 1 for airgapped machines with pre-cached models |
Example:
PORT=18080 NGL=60 bash start.sh ternaryIf you cannot reach HuggingFace (airgapped network, no internet), pre-download the models on a connected machine:
pip install huggingface-hub
hf download prism-ml/Bonsai-27B-gguf --local-dir ~/.bonsai/models/1bit
hf download prism-ml/Ternary-Bonsai-27B-gguf --local-dir ~/.bonsai/models/ternaryThen set HF_HUB_OFFLINE=1 in your shell profile. The start.sh script
automatically unsets this variable during download attempts, so it works
whether you're online or offline — if the model is already cached, no
download is attempted.
Bonsai-27B supports up to 262K tokens of context — enough to process entire codebases, long documents, or multi-hour conversations. The default is ~32K (model default), but you can increase it if your machine has enough RAM.
The KV cache grows linearly with context length. Each additional token of context costs roughly:
| Variant | KV cache per 1K tokens |
|---|---|
| 1-bit (Q1_0) | ~6 MB |
| Ternary (Q2_0) | ~8 MB |
Formula: total RAM needed ≈ model_size + (context_k × kv_per_1k) + 1 GB overhead
| Context | 1-bit (3.9 GB model) | Ternary (7.2 GB model) |
|---|---|---|
| 32K (default) | ~5.1 GB | ~8.5 GB |
| 64K | ~5.5 GB | ~8.9 GB |
| 128K | ~6.3 GB | ~9.7 GB |
| 262K (max) | ~6.5 GB | ~10.3 GB |
# 262K context (tested on CPU + 48 GB DDR5)
CONTEXT_SIZE=262144 bash start.sh 1bit
# 128K context (works on 32 GB machines)
CONTEXT_SIZE=131072 bash start.sh 1bit
# 64K context (works on 16 GB machines)
CONTEXT_SIZE=65536 bash start.sh 1bit
# Model default (~32K, lowest RAM)
bash start.sh 1bitWhen you run bash start.sh with no arguments, the script estimates the
maximum context your machine can handle based on available RAM and shows
it in the hardware detection output:
── Model Recommendations ──
Context estimate: ~262K tokens (based on 48000 MB RAM)
Set CONTEXT_SIZE=268288 for max context, or
CONTEXT_SIZE=0 for model default (~32K)
If you set CONTEXT_SIZE higher than your machine can handle:
- llama-server will crash on startup with an out-of-memory error
- The error message will mention
llama_kv_cache_initorbad_alloc - Fix: Reduce
CONTEXT_SIZEto a lower value (see table above)
The script does not pre-validate context size against available RAM because llama.cpp's actual memory usage depends on many factors (batch size, number of layers offloaded to GPU, concurrent requests). The formula above is a conservative estimate — start with a lower value and increase until you find your machine's sweet spot.
After start.sh finishes, open http://localhost:8080 in your browser.
llama.cpp includes a built-in chat interface — no Docker, no extra setup.
Features:
- Chat with the model
- Upload files (PDF, images, text, code)
- Multi-turn conversation
- Copy/paste responses
Bonsai-27B is a vision-language model — it accepts images alongside text.
The start.sh script automatically downloads the multimodal projector
(mmproj) file and passes it to llama-server via the -mm flag.
Web UI: Click the + icon in the message box to upload a photo or screenshot.
OpenAI-compatible API: Send image_url content parts to /v1/chat/completions:
curl http://localhost:8080/v1/chat/completions \
-d '{"model":"bonsai","messages":[{"role":"user","content":[{"type":"text","text":"What is in this image?"},{"type":"image_url","image_url":{"url":"data:image/png;base64,<BASE64_DATA>"}}]}],"stream":true}'Vision settings:
| Setting | Default | Description |
|---|---|---|
| mmproj file | *mmproj-Q8_0.gguf |
Multimodal projector (vision tower), ~0.6 GB |
--image-max-tokens |
1024 |
Downscales images to 1024 vision tokens for speed |
--image-min-tokens |
model default | Minimum vision tokens (typically 256) |
Image token pricing:
- Each image is encoded into vision tokens (one token per ~32×32 pixel patch)
- Bonsai-27B accepts up to ~4096 vision tokens (about a 4.2 MP image)
--image-max-tokens 1024is the sweet spot for everyday photos and screenshots- For OCR-style tasks (small text, serial numbers), use
IMAGE_MAX_TOKENS=0to disable capping
Performance impact:
- The mmproj file adds ~0.6 GB to the model footprint (Q8_0) or ~0.9 GB (BF16)
- Vision tokens are prefill — a large photo adds thousands of tokens before the first word
- The prompt cache means follow-up questions about the same image are near-instant
- On CPU, expect ~16 t/s prompt processing and ~9 t/s generation with images
Concurrency: llama-server auto-detects n_parallel based on CPU cores.
On a 24-thread machine, it defaults to 4. To override, set the PARALLEL
env var:
# Single-request mode (benchmark-safe, lowest memory)
PARALLEL=1 bash start.sh 1bit
# 4 concurrent requests
PARALLEL=4 bash start.sh ternaryEach slot gets its own KV cache, so memory scales linearly with PARALLEL.
For CPU-only, PARALLEL=1 minimizes memory pressure.
The same endpoint also serves an OpenAI‑compatible API at
http://localhost:8080/v1/chat/completions. Works with any tool or library
that speaks the OpenAI API:
curl http://localhost:8080/v1/chat/completions \
-d '{"model":"bonsai","messages":[{"role":"user","content":"Hello"}],"stream":true}'| Model | HF repo | Size | Notes |
|---|---|---|---|
| Ternary-Bonsai-2-27B-PTQ1_0 | prism-ml/Ternary-Bonsai-2-27B-gguf |
6.0 GB | Bonsai 2, ternary g128 — 98.2% of FP16, thinking model (default) |
| Ternary-Bonsai-2-27B-PQ2_0 | same repo | 7.2 GB | Bonsai 2, ternary g128 — 98.2% of FP16, fastest on modern NVIDIA |
| Bonsai-27B-Q1_0 | prism-ml/Bonsai-27B-gguf |
3.9 GB | 1-bit, 89.5% of FP16 |
| Ternary-Bonsai-27B-Q2_g64 | prism-ml/Ternary-Bonsai-27B-gguf |
7.2 GB | Ternary, 94.6% of FP16 (official group-64 format) |
No DSpark drafter is published for Bonsai 2 — the +dspark variants apply only
to the v1 line, and their published *dspark-Q4_1.gguf drafters are legacy
packings that must be converted (gguf-dspark-to-dflash) for post-rebase fork
binaries; start.sh warns when they are used.
| Variant | Full Suite (79 scenarios) | Finnish (10 FI) | Speed | |---|---|---|---|---|| | Ternary Q2_0 | 83/100 ⭐ | 90/100 | 2.5s/turn, ~30 t/s | | Q1_0 | 81/100 | 85 Quality | 5.1s/turn, 44 t/s | | Ternary + DSpark | 83 | 85 Quality | 4.9s/turn (corrected) — DSpark hurts on DGX Spark | | Ternary + DSpark (old) | 77 | 85 Quality | 16.2s/turn (contaminated — vllm backend) |
Hardware: NVIDIA GB10 (128 GB unified VRAM). Full report at Bonsai 27B Benchmarks.
The 1‑bit (Q1_0) variant runs on CPU‑only hardware with no GPU at all:
| Metric | Value |
|---|---|
| Hardware | Minisforum AI X1 Pro (WSL2, 48 GB DDR5, AMD Ryzen AI 9 HX 370, 24 cores) |
| Model | Bonsai‑27B‑Q1_0 (3.6 GB GGUF) |
| Backend | llama.cpp CPU (no GPU — -ngl ignored) |
| Prompt ingest | ~12.7 tok/s |
| Generation | ~9.0 tok/s |
| Context | 262K tokens |
| RAM use | ~4 GB model + ~1 GB runtime overhead |
| Startup | Build ~5 min (first time), then instant |
CPU inference at ~9 tok/s is usable for testing, light chat, and quick code queries. For interactive use, any GPU (even an iGPU) or the DGX Spark machines will give dramatically better throughput.
Bonsai 27B is a Qwen3.6‑27B derivative with hybrid attention (~75% linear / ~25% full) and aggressive low‑bit quantization designed from the ground up for local deployment:
- 262K token context — full‑repo code analysis, long documents
- 3.9–7.2 GB footprints — runs on laptops and single GPUs
- 2–3× faster than equivalently quantized models — linear‑attention backbone
- Apache 2.0 — free to use, modify, distribute
bonsai-runner/
├── start.sh # One‑click runner (Linux/macOS/WSL)
├── start.ps1 # Windows runner (native PowerShell)
├── stop.sh # Stop the server (Linux/macOS/WSL)
├── stop.ps1 # Stop the server (Windows)
├── AGENTS.md # Instructions for AI agents
├── README.md # You are here
└── test-start.sh # Validation script (not shipped to end users)
Models and the llama.cpp build are cached under ~/.bonsai/ — re‑running is instant after the first download.
A PowerShell wrapper mirrors start.sh — no WSL or Git Bash needed:
powershell -ExecutionPolicy Bypass -File start.ps1 # bonsai2 (PTQ1_0, default)
powershell -ExecutionPolicy Bypass -File start.ps1 -Variant bonsai2-pq2
powershell -ExecutionPolicy Bypass -File stop.ps1 # stop the serverIt downloads the official PrismML fork prebuilt binary for Windows (auto-detects your
GPU: NVIDIA → CUDA, AMD/Intel GPU → Vulkan, none → CPU), then the model + mmproj, then
starts the server on :8080.
- AMD/Intel iGPU → Vulkan — this is the path for the Minisforum AI X1 Pro (Radeon 890M, RDNA 3.5). Expect single-digit tok/s; fine for testing, not for production.
- NVIDIA → CUDA — best speed (needs the CUDA runtime; the fork ships
cudart-*DLL zips separately). - AMD GPU → HIP/Radeon also exists in the releases; use
-Backendoverride if needed.
No NPU support. AMD Ryzen AI (XDNA) has no llama.cpp backend, and the ternary g128 format only has kernels in the PrismML fork (llama.cpp) and MLX (Apple). Windows NPU inference (ONNX Runtime / WinML) would require re-quantizing to a standard format and loses Bonsai's 5.9 GB design. The closest browser option is PrismML's WebGPU demo.
Stock llama.cpp cannot load
PTQ1_0/PQ2_0at all — only the fork binaries work.
start.sh auto-detects your hardware with a single script across all platforms:
| Platform | Detection | Backend | RAM source |
|---|---|---|---|
| Linux (any distro) | uname → "Linux" |
CPU (or CUDA if GPU found) | /proc/meminfo |
| macOS Apple Silicon (M1–M5) | uname -m → "arm64" |
Metal (GPU accelerated) | sysctl hw.memsize |
| macOS Intel | uname -m → "x86_64" |
CPU | sysctl hw.memsize |
| WSL2 (Windows) | uname → "Linux" |
CPU (sees Windows CUDA drivers but ignores them) | /proc/meminfo |
| Windows native (no WSL) | start.ps1 (PowerShell) |
CUDA / Vulkan / CPU prebuilt fork binary | Get-CimInstance Win32_VideoController |
Notes:
- CUDA on Linux: detected via
nvidia-smi. If the tool exists but reports no GPU (WSL host drivers), falls back to CPU. - Metal on Mac: uses the PrismML llama.cpp fork with
-DGGML_METAL=ON. Only Apple Silicon (arm64) gets Metal; Intel Macs use CPU. - No Mac to test? The detection logic uses standard POSIX commands (
uname,sysctl) that behave identically across macOS versions. The build flags (-DGGML_METAL=ON) come from the upstream PrismML fork. If you hit issues on macOS, open an issue — theuname/sysctlpaths are well-tested, but Metal compile issues are upstream.