VibeThinker on Apple Silicon
A 1.5B and 3B reasoning model that punches well above its weight
class — long chain-of-thought emitted as a structured
reasoning_content field, suitable for distillation
pipelines or low-RAM agents. rapid-mlx ships two MLX quants and
the custom vibethinker reasoning parser.
pip install rapid-mlx && rapid-mlx serve vibethinker-1.5b-4bit
runs the 1.5B reasoner at 4-bit on a 4 GB-unified-memory
Mac. The /v1/responses endpoint exposes the
reasoning trace as a structured field — no string-parsing
required. Headline tok/s: TODO — bench pending on M3 Ultra.
Why VibeThinker matters
The "reasoning at the low end" niche has been the gap between DeepSeek R1 (huge, server-only) and Qwen2.5-7B (small, but not reasoning-tuned). VibeThinker fills it — a 1.5B / 3B distilled reasoner that can show its work on math, code, and logic, while fitting comfortably in the RAM of an entry-level Apple Silicon Mac. If you need a chain-of-thought trace and you don't have 4 GB to spare for it, this is the model.
Engineering notes — what we shipped
The VibeThinker integration landed as a four-PR sequence, each addressing a different streaming-edge case in the reasoning parser:
-
PR #708 (233696fb) — initial
feat(aliases): add VibeThinker-1.5B + 3B aliases. Wires the alias config and points atmlx-community/VibeThinker-1.5B-mlx-4bitandmlx-community/VibeThinker-3B-8bit. -
PR #715 (6f12b313) —
fix(integration): VibeThinker + ≤5B alias bundle (parser + alias config). Wires the customvibethinkerreasoning parser and thehermestool-call parser, plus the recommended sampling defaults (temperature=1.0,top_p=0.95) baked into the alias. -
PR #718 (088f8811) —
fix(reasoning): VibeThinker follow-up (Case-4 rescue + 3 SSE-boundary codex P2 fixes). Bug: the streaming parser split the<think>token across SSE chunks, eating part of the preamble. Root cause: the base reasoning-parser used a fixed-width window, so a chunk boundary at exactly the wrong byte would skip the partition. Fix: partition<think>so the preamble survives — see commit 705b835a — and rescue Case-4 (mixed-tag streaming) by widening the lookahead. -
PR #754 (d9f0e877) — redo of the F-042
<function>XML matcher with wire-order + streaming-mixed fix. Resolves a regression where the parser would emit a partial function call when the model output a<think>block mid-tool.
The reasoning parser source is at
vllm_mlx/reasoning/;
the alias config at
vllm_mlx/aliases.json.
Upstream status
Upstream mlx-lm can load VibeThinker weights but does
not have a reasoning-parser surface — the
<think>...</think> trace appears inline in
the chat content. rapid-mlx splits the trace into a structured
reasoning_content field on
/v1/responses and on the OpenAI-style streaming
delta, so clients don't have to string-match.
Pick your quant
| Alias | Bits | Approx. disk | Recommended for | HF repo |
|---|---|---|---|---|
vibethinker-1.5b-4bit |
4 | ~1.0 GB | Default — fits 4 GB unified memory, including M1/M2 baseline | VibeThinker-1.5B-mlx-4bit |
vibethinker-3b-8bit |
8 | ~3.0 GB | Quality bump — 8-bit 3B for distillation / long-CoT runs | VibeThinker-3B-8bit |
Position vs the big reasoners
DeepSeek R1, V4-Flash, GLM-Z1 — these are stronger reasoners, but they need a high-end workstation or a cloud GPU. Qwen3-Thinking and GLM-4.5-Air are middle-ground, requiring 24-32 GB of unified memory. VibeThinker is the only reasoning option in the ≤5 GB-RAM tier on Apple Silicon. If you need reasoning at all and your hardware budget is a 16 GB MacBook Air, this is the model. If you have 64 GB+, use DeepSeek V4-Flash instead — it is meaningfully stronger.
Recommended settings
The alias bakes in temperature=1.0,
top_p=0.95 from the model card — VibeThinker was
trained with high-temperature sampling for diversity of reasoning
paths, and lower temperatures collapse the trace into shallow
restatements. Tool calling uses the
hermes parser.
rapid-mlx serve vibethinker-1.5b-4bit \
--port 8000 \
--host 127.0.0.1
Tutorial — reasoning chain via /v1/responses
The structured reasoning trace shows up as
response.reasoning — the message content is just the
final answer, so you can render the trace separately or strip it
for downstream evals.
curl http://127.0.0.1:8000/v1/responses \
-H 'Content-Type: application/json' \
-d '{
"model": "vibethinker-1.5b-4bit",
"input": "If a train leaves Chicago at 60 mph and another leaves NYC at 80 mph, when do they meet (790 miles apart)?",
"reasoning": {"effort": "medium"}
}'
Python with the OpenAI client:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="none")
resp = client.responses.create(
model="vibethinker-1.5b-4bit",
input="Prove that the sum of the first n odd numbers is n^2.",
reasoning={"effort": "high"},
)
# Final answer
print(resp.output_text)
# Structured reasoning trace
for item in resp.output:
if item.type == "reasoning":
for chunk in item.summary:
print("[reasoning]", chunk.text)
Performance
TODO — M-series headline tok/s pending. We'll publish 1.5B-4bit decode tok/s on M1 (8 GB), M2 Pro, and M3 Ultra in the next bench release.
Known limitations
-
At
temperature=0.0the model collapses to short non-reasoning answers — the alias defaults to 1.0 for a reason. Don't override unless you understand what you're losing. - Reasoning traces can run to several thousand tokens on hard problems. If you're streaming to a UI, throttle the render or the user will see a wall of "thinking" before the answer.
-
Tool calling works (via the
hermesparser) but is meaningfully weaker than on Qwen3-Thinking; pick the model for reasoning, not for agentic tool use.