Skip to main content
Ternary Bonsai 2 2B is the smallest model in the family, for wearables, always-on background tasks, and the tightest memory budgets. It is derived from Qwen3.5-2B, a hybrid-attention vision-language model, with the architecture unchanged. Every language-model weight is ternary: {−1, 0, +1} in a fixed rotated basis, with an FP16 scale per group of 128 weights. It takes images as well as text, carries the base model’s 262K-token context, and thinks before it answers by default.
Before you run it: the recommended sampling settings differ between thinking and non-thinking mode, and the 2B wants a presence penalty that the 27B does not. See Best practices.

Specifications

Artifacts

Both repositories are in the Bonsai 2 collection on Hugging Face. GGUF: prism-ml/Ternary-Bonsai-2-2B-gguf The three language-model files hold the same ternary weights and differ only in storage layout and kernel speed. Bits/weight is file size over the ~1.88B language-model parameters, so it includes the F32 tensors and metadata. The same model is ~3.8 GB at FP16. MLX: prism-ml/Ternary-Bonsai-2-2B-mlx-2bit, 1.34 GB on disk. model.safetensors carries the language model in MLX’s 2-bit grouped format (group 128) plus the vision tower in FP16. MLX stores a scale and a bias per group, so the container costs a little more than the GGUF packings; the weights decode to exactly the same ternary values.
Which runtime runs which file. Upstream llama.cpp (build b11555 or newer) runs the Q2_0 file. PQ2_0 and PTQ1_0 need the PrismML llama.cpp binaries, which the demo’s setup.sh installs. The MLX pack loads natively in mlx-vlm 0.7.8 and newer. See Formats & runtime support.

How to run it

Through the demo repo, which applies the right sampling profile, loads the vision projector, and enables tool calling:
Or directly with llama.cpp / MLX:
-n 16384 (or max_tokens) leaves room for the thinking trace; a small cap ends generation mid-thought, before any answer. The demo repo is the source of truth for running these models; tested setups and current implementation details live there.

Best practices

These apply to every GGUF packing and to the MLX pack.

Generation parameters

Use the set that matches the mode you’re running in. Thinking and non-thinking mode want different sampling, and changing the thinking mode alone does not change the sampling for you.
Apply the penalties over the last 64 tokens: --repeat-last-n 64 in llama.cpp (its default window), presence_context_size=64 in MLX (whose default is 20). The presence penalty has no GGUF metadata key, so pass it on the command line or in each request. Avoid greedy decoding (temperature=0) with thinking on; it can get stuck reasoning on some prompts. The demo’s launchers apply the thinking-mode profile, and BONSAI_THINKING=0 switches both the sampling and the chat template to non-thinking.

Output length

Give thinking mode room to finish. Even simple prompts can use 3,000+ thinking tokens, so allow at least 8,192 output tokens with thinking on. For short answers, turn thinking off.

System prompt

A plain system message works well:

MLX sampler

mlx-vlm’s Python generate() decodes greedily unless you pass the sampler, and mlx_vlm.server reads temperature, top_p, and top_k from generation_config.json but not min_p or the presence penalty. Send the full set with every request. Matching these values does not make output identical across runtimes: sampler ordering, kernels, and RNG differ.

Choosing a packing

  • PQ2_0: the default. Cheaper to unpack, faster prompt processing, and the pack measured on Apple Silicon.
  • PTQ1_0: the smallest file. Use it where memory is tightest.
  • Q2_0: the one packing upstream llama.cpp runs. Pick it for tools that bundle a stock llama.cpp.

Vision and tool calling

Send an image as an OpenAI-style image_url content part, and tools as a tools array; both come back through the standard API. Use the Q8_0 projector: quality is nearly identical to BF16, and the BF16 file can crash llama-server under concurrent image requests on current builds. For small or dense text in images, raise the image token budget with --image-min-tokens 1024.
See Tool calling & MCP in the demo repo for MCP server setup and the agentic walkthrough.

Thinking mode

The 2B reasons by default. Over the API, turn it off per request with "chat_template_kwargs": {"enable_thinking": false} on llama-server, or a top-level "enable_thinking": false on the mlx-vlm server, and switch to the non-thinking sampling at the same time. Cap it server-wide with --reasoning-budget N, or start the demo’s launchers with BONSAI_THINKING=0. The built-in chat UI has a Reasoning effort picker that overrides the server default per conversation.

Speculative decoding

The 2B ships without a draft model. At this size a decode step is already cheap, and in our tests speculative decoding with a draft head was slower than plain decoding on everyday prompts, so plain decoding is the recommended setup.