How to Run Ternary Bonsai 2 27B Without Refusals Locally

A 9.7 MB adapter, not a checkpoint. The 5.95 GB ternary pack stays byte-identical and the refusal direction is removed inside the compute graph, at a strength you set per request. The measurements behind this card are public.

Atomic Chat Discord GitHub
  • Load it with --lora-scaled adapter.gguf:1.5. Scale 0 gives the published model back, token for token, so one process serves both.
  • It needs PrismML's llama.cpp fork - stock llama.cpp and Ollama cannot open the base pack at all.
  • Every number on this card was measured inside the running model, not inferred from the adapter file.

Ternary-Bonsai-2-27B-Abliterate-LoRA-GGUF

A rank-1 LoRA adapter that removes a refusal direction from prism-ml/Ternary-Bonsai-2-27B-gguf at inference time, built and measured by AtomicChat. The base pack stays byte-identical; nothing is requantized. The direction was estimated on the ternary pack itself, not on the bf16 model it was trained from.

Why an adapter and not a model

W' = W - r (r^T W) is a dense matrix, but these weights are ternary with an FP16 scale per 128. The edit is about 1.4% of ||W|| against a grid step of 1.7-2.4x a typical weight, so baking it back into the pack rounds it away. llama.cpp builds a LoRA into the graph as two extra matmuls and never merges it into the base weights, which is what makes a rank-1 adapter an exact carrier of the edit at 1.75 bits/weight.

You need PrismML's llama.cpp fork

Stock llama.cpp cannot open the base pack at all: PTQ1_0 (143) and PQ2_0 (142) are private ggml type ids, and the weights live in a Hadamard-rotated basis. Use PrismML-Eng/llama.cpp. Built and verified against tag prism-b10709-9a9394a.

Get the files

hf download prism-ml/Ternary-Bonsai-2-27B-gguf Ternary-Bonsai-2-27B-PTQ1_0.gguf --local-dir .   # or PQ2_0
hf download AtomicChat/Ternary-Bonsai-2-27B-Abliterate-LoRA-GGUF \
    bonsai2-27b-abliterate-own42-lora.gguf --local-dir .

Run it:

llama-cli -m Ternary-Bonsai-2-27B-PTQ1_0.gguf \
    --lora-scaled bonsai2-27b-abliterate-own42-lora.gguf:1.5

Or serve it, with the strength as a per-request knob:

llama-server -m Ternary-Bonsai-2-27B-PTQ1_0.gguf -ngl 99 --jinja \
    --lora bonsai2-27b-abliterate-own42-lora.gguf --lora-init-without-apply
curl localhost:8080/v1/chat/completions -H 'Content-Type: application/json' -d '{
  "messages": [{"role": "user", "content": "..."}],
  "lora": [{"id": 0, "scale": 1.5}]
}'

Use scale 1.5. At 1.0 the projection is exact but 14% of AdvBench-style prompts still refuse; at 2.0 the model starts emitting empty replies. See the table below. Scale 0 reproduces the published model token for token, so one process can serve both behaviours.

Files

file sites what
bonsai2-27b-abliterate-own42-lora.gguf 129 main adapter, direction from residual row 42
bonsai2-27b-abliterate-own38-lora.gguf 129 same, row 38
bonsai2-27b-abliterate-own42-layers15-63-lora.gguf 98 row 42, blocks 15-63 only, no embedding
directions/refusal_dir_own_projected.safetensors - [65, 5120] directions, the ones the adapters use
directions/refusal_dir_own_plain.safetensors - same before the harmless-mean correction (see caveat)
eval/*.json - raw eval summaries

129 sites = 64 ffn_down + 48 ssm_out + 16 attn_output + token_embd. Each adapter carries its provenance in its GGUF metadata (prism.abliterate.*), including the sha256 of the base pack it was derived from.

How the direction was estimated

Difference of means of the residual stream at the last prompt token (chat template applied, thinking off), 416 harmful prompts from mlabonne/harmful_behaviors against 416 harmless prompts from mlabonne/harmless_alpaca. The held-out test splits were never used for estimation. Row L of the direction file is the residual stream entering block L; row 0 is the embedding output.

The naive difference of means over-refuses. It carries a component along the mean harmless activation (cosine about -0.3), so zeroing the projection moves a harmless prompt roughly 3.5 sigma toward the harmful cluster. Measured: the uncorrected row-38 direction made the model answer "I cannot provide information about the fastest methods for illegal activities" to a question about the fastest land mammal. The shipped directions subtract that component, which puts an average harmless prompt back where it was (measured on the held-out split). The uncorrected file is included only for reproduction.

Data provenance

No prompt set is redistributed here - only the recipe, so anyone can rebuild the same splits from the sources.

source role derived from license
mlabonne/harmful_behaviors 416 train / 104 held-out test AdvBench (Zou et al. 2023, llm-attacks, MIT) not stated on the dataset
mlabonne/harmless_alpaca 416 train / 104 held-out test, sampled tatsu-lab/alpaca CC BY-NC 4.0 on the source
cais/mmlu 500 questions over 57 subjects original MIT

Splits are rebuilt by make_directions.py with sampling seed 0 and mmlu_run.py with sampling seed 0. numpy.random.Generator does not guarantee an identical stream across numpy releases, so verify you rebuilt the same subsets against these checksums (produced with numpy 2.5.3):

038bd43f018f66617129f7bf80c5514f...  test_harmful.jsonl
e640e1637beb3283985487412b684cea...  test_harmless.jsonl
9859cc69902a2b5707afe54346034e8f...  train_harmful.jsonl
087080f508babfd93553662fbaffca84...  train_harmless.jsonl

Model outputs on the harmful split are not published; eval/ carries aggregates only.

Evaluation

104 harmful + 104 harmless held-out prompts, greedy, 64-token budget, thinking off, same seed and system prompt everywhere. Refusal is a rule-based opening-phrase match, which is indicative, not a judge. An empty or degenerate reply is counted as damage, not as compliance.

config harmful: refused empty/degenerate harmless: refused
base 99% 0 0%
row 42, scale 1 14% 0 0%
row 42, scale 1.5 0% 0 0%
row 42, scale 2 0% 5 empty 0%
row 38, scale 1 34% 0 0%
row 38, scale 1.5 0% 0 0%
row 42, blocks 15-63, scale 1 14% 0 0%

Restricting the intervention to blocks 15-63 is consistently worse than covering every site, so the lower blocks and the embedding carry part of the behaviour.

MMLU, 500 questions stratified over 57 subjects, answer forced to a single letter by a root ::= [A-D] grammar, thinking off. Answer-only, so the absolute number sits below PrismML's published thinking-mode result; what matters here is the difference.

config accuracy flipped to wrong flipped to right
base 73.2% +- 2.0 - -
scale 1 72.6% 12 9
scale 1.5 73.0% 15 14
scale 2 72.6% 26 23

Flips are symmetric, so there is no systematic loss of knowledge even at scale 2. The damage at scale 2 shows up in open-ended generation on harmful prompts, not in knowledge.

Both packs

The same adapter file applies to PTQ1_0 and PQ2_0 unchanged, and was run on both. The two packs carry identical tensor names, shapes and prism.hadamard.* metadata; both decoders match the F16 pack bit for bit; an adapter rebuilt from PQ2_0 is bit-identical to the shipped one, tensor for tensor. On PQ2_0 the leak along r falls to 3.6-4.0e-6 at every writer and stays below 1.6e-5 in the residual stream, and all 416 greedy replies of the refusal evaluation - base and scale 1.5 - are byte-identical to the PTQ1_0 run. The packing changes how the trits are stored, not what they are.

A note on head order in ssm_out

The fork rotates the activation only for the base matmul and hands the LoRA branch the raw activation, so A belongs in the unfolded input basis:

writers:     A = P^T (s * H(r^T W_fold)),   B = -r
token_embd:  A = W_fold @ H(s * r),         B = -r

P is the tiled to grouped V-head permutation that the fork applies to ssm_out when prism.hadamard.gdn_v_grouped is set. An adapter whose ssm_out factors are left in the checkpoint's (grouped) head order does not ablate those 48 sites. Verified by tapping the residual writes inside the fork: with this adapter the component along r falls from about 1e-2 to 3.8e-6 at every site, and the residual stream leak stays below 1.5e-5 across all 64 blocks.

Limitations

  • The F16 pack was not run. It carries the same tensor names, values and rotation metadata, so the adapter should apply, but only PTQ1_0 and PQ2_0 were exercised.
  • The refusal metric matches opening phrases only. A reply that answers with a disclaimer counts as compliance.
  • Capability was checked with MMLU alone. No math, code or agentic benchmark.
  • The direction comes from one prompt-pair source at one token position. Transfer to other phrasings and languages is unmeasured.

Responsible use

This adapter removes a learned refusal direction, so the model will answer requests the published model declines. It is an inference-control and research artifact. Nothing about it makes the resulting output safe, correct or lawful, and any deployment needs its own access controls and policy enforcement.

Credits

Direction estimation, adapter build and evaluation by nik.bogatyrev.

Base model Ternary Bonsai 2 27B by prism-ml, Apache-2.0, redistributed here in no form - this repository ships only the adapter.

The refusal-direction method follows Arditi et al., Refusal in Language Models Is Mediated by a Single Direction (2024). The rank-1 LoRA formulation for this pack, and the residual-writer inventory it targets, follow OrcaRouter's OrcaBonsai-27B-Uncensored, Apache-2.0. The directions shipped here are estimated independently on the ternary pack.

Downloads last month
-
GGUF
Model size
2.42M params
Architecture
qwen35
Hardware compatibility
Log In to add your hardware

We're not able to determine the quantization variants.

Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for AtomicChat/Ternary-Bonsai-2-27B-Abliterate-LoRA-GGUF

Base model

Qwen/Qwen3.8-27B
Adapter
(2)
this model

Collection including AtomicChat/Ternary-Bonsai-2-27B-Abliterate-LoRA-GGUF