<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>open-source on Saurabh Kumar</title><link>https://saurabh-kumar.com/tags/open-source/</link><description>Recent content in open-source on Saurabh Kumar</description><generator>Hugo</generator><language>en-US</language><copyright>Copyright © 2025, Saurabh Kumar.</copyright><lastBuildDate>Sun, 14 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://saurabh-kumar.com/tags/open-source/index.xml" rel="self" type="application/rss+xml"/><item><title>My Local LLM On A 32 GB M1 Pro</title><link>https://saurabh-kumar.com/articles/2026/06/my-local-llm-on-a-32-gb-m1-pro/</link><pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/articles/2026/06/my-local-llm-on-a-32-gb-m1-pro/</guid><description>&lt;p&gt;My pi setup has a &lt;code&gt;fast&lt;/code&gt; mode: a slot I wired into my own config for the small, high-volume questions, pointed at Bedrock&amp;rsquo;s Haiku 4.5. It is a good model. Cheap, responsive, and it answers the kind of small, contained question I throw at it twenty times a day without complaining. It runs on third-party hardware, and those questions send code to a third party. I wanted to try running that slot locally.&lt;/p&gt;&#10;&lt;p&gt;None of that is a crisis. Bedrock is fast and it works. But the machine that could do the same job is sitting on my desk, and when Kyle Howells &lt;a href="https://ikyle.me/blog/2026/how-to-setup-a-local-coding-agent-on-macos" rel="noopener external"&gt;got Gemma 4 26B-A4B running locally on an M1 Max at 72 tokens per second&lt;/a&gt; and the &lt;a href="https://news.ycombinator.com/item?id=48507020" rel="noopener external"&gt;Hacker News thread&lt;/a&gt; filled up with people on smaller Macs reporting the same setup was workable, I wanted to know whether my own laptop could host that slot.&lt;/p&gt;&#10;&lt;p&gt;The catch is that my laptop is an M1 Pro with 32 GB of unified memory: half the RAM and roughly half the memory bandwidth of the machine in Kyle&amp;rsquo;s post. Copying his commands verbatim was never going to work. I needed to know what to change, in what order, and what to give up. So I did not copy the commands. I gave pi the article and asked it to build me a plan for my machine, and to interrogate me before it wrote a line.&lt;/p&gt;&#10;&lt;h2 id="i-handed-the-guide-to-an-llm-and-made-it-grill-me"&gt;I Handed The Guide To An LLM And Made It Grill Me&lt;/h2&gt;&#10;&lt;p&gt;The whole thing started with one message. Typos and all, this is what I actually sent:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Act local AI setup expert working on M1 silicon with 32GB ram. Read though [the article] and also pull all the comments on this article on news.ycombinator.com article id 48507020. I&amp;rsquo;ve ollama and muse already installed there are models installed as well. Prep and action plan to set this up in the most performant and maintaibale way. Not sure quite sure if i need to uninstall previous ollama app and it&amp;rsquo;s modetls. Create and write a plan after analysing things. &lt;code&gt;/grill-me&lt;/code&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;The &lt;code&gt;/grill-me&lt;/code&gt; at the end is a small skill in my pi setup. It is two paragraphs of instructions that tell the model to walk down the decision tree one node at a time, recommend an answer at each step, and wait for my reply before moving on. It is not magic. It is a way of forcing the conversation I would otherwise have with myself in a notebook, except the model will not let me skip a step.&lt;/p&gt;&#10;&lt;p&gt;The interview was mostly the model proposing and me confirming in monosyllables. My replies, verbatim, were things like:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;A&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;i would prefer ~/code/localLLM&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;A, also note i&amp;rsquo;ve &amp;ldquo;llm&amp;rdquo; cli already installed, and alias would be nice.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;let&amp;rsquo;s go with recommendation, write the plan.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;That is the entire texture of it. A simple opening prompt that points at an existing guide and names my constraints, then a handful of one-word answers steering the branches. The model did the reading (the article and the full HN thread) and the arithmetic. I made the calls.&lt;/p&gt;&#10;&lt;p&gt;And it pushed back, which is the point. When I leaned toward the 65 K context window from the article, it did the memory math and showed me I would be swapping inside the first generation on 32 GB, so we settled on 32 K. When I waffled on keeping Ollama &amp;ldquo;just in case,&amp;rdquo; it pointed out I was at 89 % disk with a thirteen-gigabyte Ollama models directory I was not using. When I asked for an alias, it caught that &lt;code&gt;llm&lt;/code&gt; was already taken by &lt;a href="https://llm.datasette.io" rel="noopener external"&gt;Simon Willison&amp;rsquo;s tool&lt;/a&gt; and proposed &lt;code&gt;lcl&lt;/code&gt; instead. None of these are insights I lacked. They are checks I would have skipped because I was already mentally on the next step.&lt;/p&gt;&#10;&lt;p&gt;By the end there was a written record of every decision and its reason, in the repo, not in my head. That is where it should be.&lt;/p&gt;&#10;&lt;h2 id="the-decisions-that-fell-out"&gt;The Decisions That Fell Out&lt;/h2&gt;&#10;&lt;p&gt;The interview converged on a short list, and the reasoning behind each one is worth more than the choice itself.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Model: Gemma 4 26B-A4B, the MoE, not a dense model.&lt;/strong&gt; The single most important fact about local inference on a Mac is not how much RAM you have, it is how fast it can be read. Generation is bottlenecked on memory bandwidth. The M1 Max in Kyle&amp;rsquo;s article runs at 400 GB/s; my M1 Pro at roughly 200. Everything else being equal, I should expect about half his throughput. His 72 tokens per second becomes my 36. That is still faster than I read. It is also why a mixture-of-experts model is the right answer here: Gemma 4 26B-A4B holds 26 billion parameters but activates only four billion per token. The full weights sit in RAM, but each forward pass streams a fraction of them. The dense 27B sibling has the same disk footprint and meaningfully worse generation rate. It is probably smarter. It does not matter, because it is too slow for the slot I am filling.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Speculative decoding, on.&lt;/strong&gt; Gemma 4 ships an MTP draft head, a tiny network that predicts the next several tokens cheaply, which the main model accepts or rejects in one pass. When acceptance is high, you get most of those tokens almost for free. Kyle measured +24 % on his machine. The HN thread is full of caveats about acceptance rates and MoE models benefiting less. None of it changes the decision: turn it on, measure, keep what you get.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Context: 32 K, not 65 K.&lt;/strong&gt; The arithmetic the model made me do. A 65 K KV cache plus 16 GB of weights plus a draft model does not leave enough headroom on 32 GB once Chrome and an IDE are open.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;mise, not a shell script.&lt;/strong&gt; The article uses &lt;code&gt;huggingface-cli&lt;/code&gt; to fetch the GGUFs and a hand-written tmux script to launch the server. Both work. Both are also infrastructure you now own and will forget you configured. llama.cpp&amp;rsquo;s &lt;code&gt;-hf&lt;/code&gt; flag does the download without a Python virtualenv. mise&amp;rsquo;s &lt;code&gt;github:&lt;/code&gt; backend installs the prebuilt Metal binary and rolls it forward with &lt;code&gt;mise upgrade&lt;/code&gt;. The tmux script becomes a few named tasks in a &lt;code&gt;.mise.toml&lt;/code&gt;. The choice is not about elegance, it is about which moving piece you are willing to own. I already own mise.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Ollama goes.&lt;/strong&gt; I had it from earlier experiments, with &lt;code&gt;gemma4:latest&lt;/code&gt; and a couple of other models taking 13 GB, plus the app idling in the background on port 11434. Ollama is a wrapper around llama.cpp. Once you run &lt;code&gt;llama-server&lt;/code&gt; directly, the wrapper is in the way. So the app, the models directory, and the port all go, and the 13 GB comes back on a disk that was at 89 %.&lt;/p&gt;&#10;&lt;h2 id="the-setup-start-to-finish"&gt;The Setup, Start To Finish&lt;/h2&gt;&#10;&lt;p&gt;This is the reproducible part. You can follow it by hand, or point an LLM at this section and let it do the work. That is roughly how it got built in the first place. The numbers below are the actual results on my M1 Pro, not estimates.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;1. Decommission Ollama&lt;/strong&gt; (skip if you never had it):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;osascript -e &lt;span class="s1"&gt;&amp;#39;tell application &amp;#34;Ollama&amp;#34; to quit&amp;#39;&lt;/span&gt; 2&amp;gt;/dev/null&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pkill -f Ollama&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf ~/.ollama /Applications/Ollama.app &lt;span class="c1"&gt;# reclaimed 13 GB&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. Install llama.cpp via mise.&lt;/strong&gt; The prebuilt arm64 binary has Metal and Accelerate baked in:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mise use --global &lt;span class="s1"&gt;&amp;#39;github:ggml-org/llama.cpp@latest&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;llama-server --help &lt;span class="p"&gt;|&lt;/span&gt; grep -E -- &lt;span class="s1"&gt;&amp;#39;--spec-type|-hf&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# confirm MTP + HF download support&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The one thing worth verifying is that &lt;code&gt;--spec-type draft-mtp&lt;/code&gt; is present, since MTP support landed only recently. On my install (build &lt;code&gt;b9631&lt;/code&gt;) it was there. If it ever is not, a source build with &lt;code&gt;-DGGML_METAL=ON&lt;/code&gt; is the five-minute fallback.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;3. A project with mise tasks&lt;/strong&gt; at &lt;code&gt;~/code/localLLM/.mise.toml&lt;/code&gt;. The &lt;code&gt;[env]&lt;/code&gt; block makes the model cache live with the project; the tasks replace the shell script:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;LLAMA_CACHE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{{ config_root }}/models&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;github:ggml-org/llama.cpp&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;latest&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Start llama-server (Gemma 4 26B-A4B + MTP) in tmux&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;tmux has-session -t llama 2&amp;gt;/dev/null &amp;amp;&amp;amp; { echo running; exit 0; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;tmux new-session -d -s llama -c &amp;#34;&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;PWD&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; \\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&lt;/span&gt;&lt;span class="nx"&gt;llama-server&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;-hf&lt;/span&gt; &lt;span class="nx"&gt;unsloth&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;gemma-4-26B-A4B-it-GGUF&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;UD-Q4_K_XL&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;-hfd&lt;/span&gt; &lt;span class="nx"&gt;unsloth&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;gemma-4-26B-A4B-it-GGUF&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;Q8_0-MTP&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--spec-type&lt;/span&gt; &lt;span class="nx"&gt;draft-mtp&lt;/span&gt; &lt;span class="nx"&gt;--spec-draft-n-max&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;-ngl&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt; &lt;span class="nx"&gt;-fa&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;-c&lt;/span&gt; &lt;span class="mi"&gt;32768&lt;/span&gt; &lt;span class="nx"&gt;--parallel&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--reasoning&lt;/span&gt; &lt;span class="nx"&gt;off&lt;/span&gt; &lt;span class="nx"&gt;--reasoning-budget&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--temp&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="nx"&gt;--repeat-penalty&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt; &lt;span class="nx"&gt;--repeat-last-n&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--host&lt;/span&gt; &lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="nx"&gt;--port&lt;/span&gt; &lt;span class="mi"&gt;8082&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;tee&lt;/span&gt; &lt;span class="nx"&gt;-a&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;llama-server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tmux kill-session -t llama 2&amp;gt;/dev/null &amp;amp;&amp;amp; echo stopped || echo &amp;#39;not running&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;curl -fsS http://127.0.0.1:8082/v1/models &amp;gt;/dev/null &amp;amp;&amp;amp; echo up || echo down&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A note on the port. The article uses 8080. On my machine 8080 and 8081 were already taken by Google &lt;code&gt;cloud-run&lt;/code&gt; processes, so the server silently answered on a port that was not mine until I noticed the responses carried a &lt;code&gt;Server: Google Frontend&lt;/code&gt; header. I moved everything to &lt;strong&gt;8082&lt;/strong&gt;. Check &lt;code&gt;lsof -i :8080&lt;/code&gt; before you assume a port is free.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;4. A one-key alias&lt;/strong&gt; in &lt;code&gt;~/.zshrc&lt;/code&gt;. &lt;code&gt;llm&lt;/code&gt; was taken, so &lt;code&gt;lcl&lt;/code&gt; for &amp;ldquo;local&amp;rdquo;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; mise -C &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/code/localLLM&amp;#34;&lt;/span&gt; run &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl start &lt;span class="c1"&gt;# boot the server (first run downloads the model)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl status &lt;span class="c1"&gt;# is it up?&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl stop &lt;span class="c1"&gt;# free the ~22 GB of RAM&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;5. First boot and download.&lt;/strong&gt; &lt;code&gt;lcl start&lt;/code&gt; triggers the download through llama.cpp&amp;rsquo;s &lt;code&gt;-hf&lt;/code&gt;. The real footprint came in smaller than the article&amp;rsquo;s &amp;ldquo;~21 GB&amp;rdquo; estimate. The total was &lt;strong&gt;18 GB&lt;/strong&gt;: 16 GB for the Q4_K_XL main model, &lt;strong&gt;1.2 GB&lt;/strong&gt; for the MTP head (an auxiliary head, not a full second model), and a 441 MB multimodal projector that gets pulled automatically even though I do not need images. Add &lt;code&gt;--no-mmproj&lt;/code&gt; if you want to skip it.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;6. Point pi at it.&lt;/strong&gt; A &lt;code&gt;local&lt;/code&gt; provider in &lt;code&gt;~/.pi/agent/models.json&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;providers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;local&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Local llama.cpp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;baseUrl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://127.0.0.1:8082/v1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;openai-completions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;apiKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;local&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;models&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Gemma 4 26B-A4B Q4 + MTP&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;reasoning&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;contextWindow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;maxTokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cost&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;cacheRead&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;cacheWrite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And a &lt;code&gt;local&lt;/code&gt; mode in &lt;code&gt;modes.json&lt;/code&gt; pointing at it. The benchmark answers came back clean, so I gave it real work, and that is where it broke.&lt;/p&gt;&#10;&lt;h2 id="the-thinking-spiral"&gt;The Thinking Spiral&lt;/h2&gt;&#10;&lt;p&gt;The first real task I handed Gemma was trivial: rewrite a Hugo template so a list of tags renders as &amp;ldquo;tagged A, B and C&amp;rdquo; instead of &amp;ldquo;A B C&amp;rdquo;. A ten-line edit. Haiku does it in one tool call. Gemma read the file, started thinking, and never stopped. The session export shows a single reasoning block of &lt;strong&gt;32,000 characters&lt;/strong&gt;: it derived the correct off-by-one logic, then re-derived it, then second-guessed the whitespace, then re-read its own conclusion and started over. It never emitted the edit. I aborted it. I told it &amp;ldquo;you are going in loop.&amp;rdquo; It thought about that, too, and aborted again.&lt;/p&gt;&#10;&lt;p&gt;This is the failure mode the chat-window benchmarks hide. Gemma 4 IT ships with thinking on, and llama.cpp&amp;rsquo;s default reasoning budget is unlimited (&lt;code&gt;-1&lt;/code&gt;). In a chat that is fine; the model thinks, then answers. Inside an agent loop, where the next move is supposed to be a tool call, an unbounded thinking budget on a small model is a trap. There is nothing forcing the transition from reasoning to acting, and a 4B-active MoE is not big enough to find the exit on its own. The default sampling makes it worse: &lt;code&gt;--repeat-penalty&lt;/code&gt; defaults to &lt;code&gt;1.0&lt;/code&gt;, which is to say off, so nothing discourages the model from looping on its own reasoning.&lt;/p&gt;&#10;&lt;p&gt;My first instinct was the biggest hammer: turn thinking off entirely. It worked. But &amp;ldquo;it worked&amp;rdquo; is where most debugging stops and most of the understanding gets lost, so I made the model prove which flag was actually doing the work. I had pi reconstruct the exact failing scenario from the session export, same system prompt, same tools, same file, same prompt, and replay it against two server configs:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;--reasoning off --reasoning-budget 0 # A: no thinking&#10;--reasoning auto --reasoning-budget 1024 --repeat-penalty 1.1 # B: bounded thinking&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Same prompt, same machine, here is what came back:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Config&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Reasoning emitted&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Outcome&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Original (unbounded, no repeat penalty)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;32,000 chars&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;spiral, aborted, no edit&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A — reasoning off&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0 chars&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;clean &lt;code&gt;read&lt;/code&gt; → &lt;code&gt;edit&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;B — bounded to 1024&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;518 chars&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;clean &lt;code&gt;read&lt;/code&gt; → &lt;code&gt;edit&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;The lesson is in the third row. Capping the budget alone stopped the spiral: the model thought for 518 characters, decided, and acted. So the bug was never &amp;ldquo;thinking is bad.&amp;rdquo; It was &amp;ldquo;&lt;em&gt;unbounded&lt;/em&gt; thinking is bad,&amp;rdquo; and &lt;code&gt;--reasoning off&lt;/code&gt; is just the most aggressive way to bound it to zero. Both configs produced the same edit, the same slightly-clumsy Hugo logic. The extra reasoning in config B bought nothing on this task; that is a 4B-active model hitting its ceiling, not a budget I can tune my way around.&lt;/p&gt;&#10;&lt;p&gt;Two more things I only learned by testing. &lt;code&gt;--repeat-penalty&lt;/code&gt; is an orthogonal fix and worth keeping regardless: at &lt;code&gt;1.0&lt;/code&gt; it is off, and it guards the &lt;em&gt;content&lt;/em&gt; against loops, not just the thinking. And &lt;code&gt;--reasoning off&lt;/code&gt; does not lobotomise the model the way the name suggests. I gave it a reasoning trap with thinking off, and it still worked the problem out, step by step, &lt;em&gt;in its visible answer&lt;/em&gt;, and got it right. What the flag removes is the separate, hidden, budget-less thinking phase, not the model&amp;rsquo;s ability to reason. For an agent loop that is the right trade: when reasoning happens it is visible in the content and bounded by the task, instead of a hidden phase that can run away.&lt;/p&gt;&#10;&lt;p&gt;So for the &lt;em&gt;fast&lt;/em&gt; slot I keep it off. The whole point of the slot is a quick, direct tool call; the test showed bounded thinking adds latency with no quality gain here, and if I want real reasoning I switch to a cloud mode. That is also why &lt;code&gt;&amp;quot;reasoning&amp;quot;&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; in the model config above, with thinking disabled at the server there is nothing for pi to render. The middle ground is real and now proven, though: if I ever stand up a &lt;code&gt;local-deep&lt;/code&gt; mode for harder offline work, &lt;code&gt;--reasoning auto --reasoning-budget 1024&lt;/code&gt; is a configuration I have watched stay out of the ditch. It just wants its own server on its own port, because &lt;code&gt;--reasoning off&lt;/code&gt; is a hard gate you cannot toggle back on per request.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;What it actually does&lt;/strong&gt;, measured on the M1 Pro:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Metric&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Result&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Generation&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;34–36 tok/s&lt;/strong&gt; (predicted half of Kyle&amp;rsquo;s 72, got it)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Prompt ingestion&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;77 tok/s&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Time to first token&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;~376 ms&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MTP acceptance&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;~65 %&lt;/strong&gt; (e.g. 1350 of 2090 drafted tokens accepted)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Half the bandwidth, half the tokens, just as predicted. The MTP acceptance beat what I feared. The article saw +24 % on a dense workload; here, two thirds of the drafted tokens landed.&lt;/p&gt;&#10;&lt;h2 id="how-i-actually-use-a-fast-model"&gt;How I Actually Use A Fast Model&lt;/h2&gt;&#10;&lt;p&gt;A &lt;code&gt;fast&lt;/code&gt; model in pi is not the thing I have a conversation with. It is the model that does the small, high-volume, latency-sensitive jobs around the edges of the real work, where Opus is overkill and the round-trip cost is what matters. Those are the jobs I would rather keep on my own machine, and the place where 36 tokens per second is plenty.&lt;/p&gt;&#10;&lt;p&gt;In my setup, that means:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;/answer&lt;/code&gt; and &lt;code&gt;Ctrl+.&lt;/code&gt;&lt;/strong&gt; — a &lt;a href="https://github.com/theskumar/agent-stuff/blob/main/extensions/answer.ts" rel="noopener external"&gt;small pi extension I run&lt;/a&gt; that grabs the last assistant message when it ends on a batch of clarifying questions, extracts them into structured JSON via a side LLM call, and opens a TUI so I can answer them one at a time. The extraction is a contained, schema-shaped task, the sort of thing a fast model handles well. It defaults to &lt;code&gt;claude-bridge/claude-haiku-4-5&lt;/code&gt; and is configurable at &lt;code&gt;~/.pi/agent/extensions/answer.config.json&lt;/code&gt;, the line I now point at &lt;code&gt;local&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Branch summaries and compaction.&lt;/strong&gt; When I navigate away from a branch on &lt;code&gt;/tree&lt;/code&gt;, or a session outgrows its context window, pi summarizes the work to carry forward. By default those calls run on the active session model, which means a &lt;code&gt;/tree&lt;/code&gt; hop can cost an Opus summary. A &lt;a href="https://github.com/theskumar/agent-stuff/blob/main/extensions/tree-fast-summary.ts" rel="noopener external"&gt;small extension I run&lt;/a&gt; reroutes both to whatever the &lt;code&gt;fast&lt;/code&gt; mode points at, so the throwaway housekeeping never touches the expensive model.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;/mode fast&lt;/code&gt;&lt;/strong&gt; for the genuinely small questions, the &amp;ldquo;what&amp;rsquo;s the flag for X&amp;rdquo; and &amp;ldquo;rename this&amp;rdquo; and quick grep-and-explain ones, where I switch the whole session to the fast slot and switch back.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;None of these need a frontier model. They need something that responds now and gets the structure right. That is the bar Gemma 4 has to clear to take the slot from Haiku.&lt;/p&gt;&#10;&lt;p&gt;So I gave it the slot. I went back and forth here. The cautious move was to add a separate &lt;code&gt;local&lt;/code&gt; mode, keep &lt;code&gt;fast&lt;/code&gt; on Haiku, and switch into the local one when I felt like testing it. I know how that goes: I would never remember to switch, and a week later I would have no data. So &lt;code&gt;fast&lt;/code&gt; now points at Gemma 4 and the Haiku mode moved to &lt;code&gt;lcl&lt;/code&gt;, there if I want to compare. Local models can be brilliant in a chat window and brittle inside an agent loop, as the thinking spiral already showed, and the only way I learn which one Gemma 4 is here is by living with it in the slot that fires twenty times a day. If it holds up, on tool calling, on diff quality, on the ten-line edits that fill my day, it stays. If it does not, the rollback is one line in &lt;code&gt;modes.json&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="what-i-am-not-doing-yet"&gt;What I Am Not Doing Yet&lt;/h2&gt;&#10;&lt;p&gt;I am not putting the server behind launchd. At ~22 GB resident on a 32 GB machine, leaving it running alongside Chrome and an IDE is asking for swap. On-demand &lt;code&gt;lcl start&lt;/code&gt; / &lt;code&gt;lcl stop&lt;/code&gt; is honest about the cost in a way always-on is not.&lt;/p&gt;&#10;&lt;p&gt;I am not running a second model on a second port. Maybe Qwen 3.6 35B-A3B earns a slot for the cases where Gemma is too weak. Maybe one model is enough. I will know in a week.&lt;/p&gt;&#10;&lt;h2 id="what-the-planning-helped-with"&gt;What the Planning Helped With&lt;/h2&gt;&#10;&lt;p&gt;The model researched the options; I chose the setup. The conversation made me write down why I chose each part, so I can refer back to it later.&lt;/p&gt;&#10;&lt;p&gt;I still need to see how the local model handles everyday work. If it does not hold up, I can switch the slot back.&lt;/p&gt;&#10;</description></item><item><title>fabric</title><link>https://saurabh-kumar.com/til/1780920001/</link><pubDate>Mon, 08 Jun 2026 12:00:01 +0000</pubDate><guid>https://saurabh-kumar.com/til/1780920001/</guid><description>&lt;p&gt;&lt;a href="https://github.com/danielmiessler/fabric" rel="noopener external"&gt;faberic&lt;/a&gt; is a crowd-sourced collection of prompts bundeled with a cli to use them via piped interfaces.&lt;/p&gt;&#10;</description></item><item><title>thaw</title><link>https://saurabh-kumar.com/til/1775995200/</link><pubDate>Sun, 12 Apr 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1775995200/</guid><description>&lt;p&gt;&lt;a href="https://github.com/stonerl/Thaw" rel="noopener external"&gt;thaw&lt;/a&gt; a fork of Ice. I&amp;rsquo;m switching to it, as Ice seems to the inactive now.&lt;/p&gt;&#10;</description></item><item><title>Opus 4.6 launch</title><link>https://saurabh-kumar.com/til/1770379200/</link><pubDate>Fri, 06 Feb 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1770379200/</guid><description>&lt;p&gt;Opus 4.6 launch.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;context compaction (beta) and 1M context window, enables longer agentic tasks without loosing context.&lt;/li&gt;&#10;&lt;li&gt;they claim it has found 500 Zero-Day Flaws in open-source projects (yet to see the proofs though)&lt;/li&gt;&#10;&lt;li&gt;agent teams, multiple agent coordinates with a leader agent. &lt;a href="https://code.claude.com/docs/en/agent-teams" rel="noopener external"&gt;https://code.claude.com/docs/en/agent-teams&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Redis’s License Change and the Valkey Fork</title><link>https://saurabh-kumar.com/articles/2026/01/rediss-license-change-and-the-valkey-fork/</link><pubDate>Mon, 12 Jan 2026 00:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/articles/2026/01/rediss-license-change-and-the-valkey-fork/</guid><description>&lt;h2 id="the-license-change"&gt;The License Change&lt;/h2&gt;&#10;&lt;p&gt;On March 20, 2024, Redis Inc. announced a switch from the BSD 3-clause license to dual source-available licensing: RSALv2 and SSPLv1. The new licensing takes effect with Redis 7.4 and restricts how the code can be offered as a service.&lt;/p&gt;&#10;&lt;p&gt;I see this as a loss for the open-source community, even if Redis Inc. considers it a business necessity.&lt;/p&gt;&#10;&lt;h2 id="the-thing-about-open-source"&gt;The Thing About Open Source&lt;/h2&gt;&#10;&lt;p&gt;Redis was created by Salvatore Sanfilippo in 2009, known as &amp;ldquo;antirez&amp;rdquo; in the community. He built Redis to solve a simple problem: web applications were slow because databases couldn&amp;rsquo;t keep up. What if, instead of storing data on disk, you kept everything in memory? What if you made it blazingly fast?&lt;/p&gt;&#10;&lt;p&gt;Redis became legendary. By 2024, it was everywhere—caching layers for social media platforms, session stores for e-commerce sites, real-time analytics for financial systems. Developers loved it. Not just because it was fast, but because it was &lt;em&gt;theirs&lt;/em&gt;. They could use it, modify it, deploy it however they wanted.&lt;/p&gt;&#10;&lt;p&gt;Then Redis Inc. changed the rules.&lt;/p&gt;&#10;&lt;h2 id="the-rebellion"&gt;The Rebellion&lt;/h2&gt;&#10;&lt;p&gt;Kyle Davis, a longtime Redis contributor, said something interesting after the split: &amp;ldquo;From this point forward, Redis and Valkey are two different pieces of software.&amp;rdquo; Not just different licenses—different software.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Just eight days later, on March 28, 2024&lt;/strong&gt;, something remarkable happened. The Linux Foundation announced Valkey, a fork starting from Redis 7.2.4, the last truly open version. But this wasn&amp;rsquo;t some ragtag group of angry developers. This was AWS, Google Cloud, Oracle, Ericsson, and Snap Inc.—companies with deep pockets and deeper technical expertise.&lt;/p&gt;&#10;&lt;p&gt;They weren&amp;rsquo;t just copying Redis and giving it a new name. They were going to &lt;em&gt;improve&lt;/em&gt; it.&lt;/p&gt;&#10;&lt;h2 id="the-performance-revolution"&gt;The Performance Revolution&lt;/h2&gt;&#10;&lt;p&gt;Here&amp;rsquo;s where the story gets interesting, because it stops being about philosophy and starts being about engineering.&lt;/p&gt;&#10;&lt;p&gt;Redis, for all its speed, had a design constraint: it was single-threaded. Salvatore made this choice deliberately. Single-threading avoids all the complexity of concurrent programming—no locks, no race conditions, no debugging nightmares. For years, this was fine. Processors were getting faster, and Redis was fast enough.&lt;/p&gt;&#10;&lt;p&gt;But by 2024, we&amp;rsquo;re not making processors faster anymore—we&amp;rsquo;re making them wider. Your laptop has eight cores, maybe sixteen. Your cloud server has dozens. And Redis, with its elegant single-threaded design, was using&amp;hellip; one.&lt;/p&gt;&#10;&lt;p&gt;The Valkey team saw their opportunity.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Valkey 8.0, released on September 15, 2024&lt;/strong&gt;, introduced enhanced multi-threaded I/O. Not for the core data operations—those stayed single-threaded to preserve Redis&amp;rsquo;s simplicity and safety—but for network operations, for handling connections, for moving data in and out.&lt;/p&gt;&#10;&lt;p&gt;The results were dramatic. In benchmarks on AWS hardware, Valkey 8.0 hit 1.19 million requests per second. That&amp;rsquo;s 230% more than Valkey 7.2. That&amp;rsquo;s the kind of improvement that makes CTOs take notice.&lt;/p&gt;&#10;&lt;p&gt;But the engineers didn&amp;rsquo;t stop there.&lt;/p&gt;&#10;&lt;h2 id="the-memory-game"&gt;The Memory Game&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;Valkey 8.1, released on March 31, 2025&lt;/strong&gt;, arrived with something even more impressive: a complete redesign of how data is stored in memory.&lt;/p&gt;&#10;&lt;p&gt;The team rebuilt the hash table—the fundamental data structure at the heart of any key-value store—using modern algorithms inspired by Google&amp;rsquo;s &amp;ldquo;Swiss Tables&amp;rdquo; design. Every key-value pair now uses about 20-30 bytes less memory. That might not sound like much, but scale it up.&lt;/p&gt;&#10;&lt;p&gt;When researchers tested Valkey 8.1 against Redis 8.2 with 50 million entries, Valkey consumed 3.77 GB while Redis used 4.83 GB. That&amp;rsquo;s a 28% reduction. For a company running terabytes of cached data across hundreds of servers, that&amp;rsquo;s not just impressive—it&amp;rsquo;s millions of dollars in annual savings.&lt;/p&gt;&#10;&lt;h2 id="the-feature-race"&gt;The Feature Race&lt;/h2&gt;&#10;&lt;p&gt;Of course, efficiency isn&amp;rsquo;t everything. Redis Inc. didn&amp;rsquo;t just sit back and watch. &lt;strong&gt;In May 2025, Redis released version 8.0&lt;/strong&gt;, bundling their enterprise features—JSON support, vector search for AI applications, time series databases—directly into the core product. These are sophisticated capabilities that took years to develop.&lt;/p&gt;&#10;&lt;p&gt;Valkey&amp;rsquo;s roadmap is playing catch-up here. Version 8.1 added JSON and Bloom filters. Vector search support became available through the valkey-search module. Time series support is planned. But there&amp;rsquo;s still a gap.&lt;/p&gt;&#10;&lt;p&gt;This is the classic innovator&amp;rsquo;s dilemma: Redis has more features &lt;em&gt;today&lt;/em&gt;. Valkey has better fundamentals and a trajectory that suggests they&amp;rsquo;ll have those features &lt;em&gt;tomorrow&lt;/em&gt;—and they&amp;rsquo;ll run faster when they do.&lt;/p&gt;&#10;&lt;h2 id="the-cloud-complication"&gt;The Cloud Complication&lt;/h2&gt;&#10;&lt;p&gt;Here&amp;rsquo;s where it gets messy. When you go to AWS and spin up what was formerly called &amp;ldquo;Redis,&amp;rdquo; you might now get Valkey. Same with Google Cloud&amp;rsquo;s Memorystore.&lt;/p&gt;&#10;&lt;p&gt;These managed services have their own quirks and limitations, shaped by what the cloud providers choose to support. The result? More fragmentation.&lt;/p&gt;&#10;&lt;p&gt;Managed Redis-compatible services now include both projects, with different feature sets and provider support.&lt;/p&gt;&#10;&lt;h2 id="the-choice"&gt;The Choice&lt;/h2&gt;&#10;&lt;p&gt;So where does this leave developers in 2026?&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;re starting a new project, Valkey offers true open-source freedom, better performance on multi-core systems, and superior memory efficiency. You sacrifice some advanced features, but its BSD license permits use, modification, and redistribution under its terms.&lt;/p&gt;&#10;&lt;p&gt;If you need those advanced features &lt;em&gt;right now&lt;/em&gt;—vector search for AI applications, sophisticated time series analysis, enterprise support—Redis has them. But you&amp;rsquo;re accepting the terms Redis Inc. sets, and those terms have already changed once.&lt;/p&gt;&#10;&lt;p&gt;For existing Redis deployments, the calculation is harder. Migration costs are real. But so is the risk of depending on a vendor that rewrote the rules mid-game.&lt;/p&gt;&#10;&lt;h2 id="my-take"&gt;My Take&lt;/h2&gt;&#10;&lt;p&gt;Valkey started with an established codebase and backing from major companies, including AWS, Google Cloud, Oracle, Ericsson, and Snap Inc. Its performance and memory work give developers reasons to evaluate it beyond the license.&lt;/p&gt;&#10;&lt;p&gt;I prefer Valkey’s permissive licensing and foundation governance. To me, the fork shows why the ability to keep developing an existing open-source codebase matters when a vendor changes terms. Both projects continue to evolve, and the choice still depends on features, migration costs, and workload measurements.&lt;/p&gt;&#10;</description></item></channel></rss>