Running Pi Coding Agent on Local Models A developer ran the pi coding agent entirely on a 32 GB M5 MacBook using local models served by llama.cpp's llama-server in router mode, eliminating API bills and rate limits. The setup uses Qwen3.8-27B at IQ3_S (about 12 GB) and Muse Glimmer 30B at Q4_K_M (about 17 GB), with --models-max set to 1 so the router evicts one model before loading the other, since the combined 29 GB of weights exceeds the roughly 20 GB of unified memory available to the GPU. The writeup describes configuring --models-preset with an ini file so models can be selected from pi's /model menu without restarting the server manually. Running Pi Coding Agent on Local Models https://darshanmakwana412.github.io/2026/10/pi-coding-agent-with-local-llama-server/ For the past month I have been running a coding agent entirely on my macbook. No API bill, no rate limits, and it works without internet. The harness I use is pi https://github.com/earendil-works/pi , a very minimal coding agent with just four tools read, bash, edit, write and a typescript extension system, and the models are served with llama.cpp https://github.com/ggml-org/llama.cpp ’s llama-server . The first week of this was honestly painful. Every time I wanted to try a different model I had to kill the server, find the right gguf path in my huggingface cache, restart it with a different set of flags, then go and edit pi’s config so it knows the model exists. Then I would quit pi and forget the server was still running with 17 GB sitting in memory The goal of this writeup is to get to a setup where I open pi, pick a model from the /model menu, and that model just becomes available. No terminal juggling. If I switch to a different model the old one gets evicted and the new one gets loaded, and when I quit pi the server goes away with it Table of Contents: The Machine and the Models I am on an M5 macbook with 32 GB of unified memory. Unified memory means the CPU and GPU share the same pool, so there is no separate VRAM, a model that fits in RAM can be run on the GPU directly. The catch is that macOS does not let the GPU take all of it, by default it only lets the GPU wire somewhere around 70-75% of the total. So on 32 GB I realistically have a little over 20 GB for weights plus the KV cache I keep two models around for coding: - Qwen3.8-27B quantized to IQ3 S about 12 GB on disk with the vision projector . It ships with an MTP https://arxiv.org/abs/2404.19737 head, which is an extra little head trained to predict the token after the next one, so it can do speculative decoding without a separate draft model - Muse Glimmer 30B at Q4 K M about 17 GB with a separate small DFlash draft model for speculative decoding If you want to know what IQ3 S and Q4 K M actually mean I wrote about quantization and floating point formats https://darshanmakwana412.github.io/2026/03/quantization-float-points-turboquant/ a while back. The short version is that both are ways of storing each weight in 3-4 bits instead of 16 Now some napkin math. 12 + 17 = 29 GB just for weights, which is way over my ~20 GB budget, so both models can never be resident at the same time. This one fact drives most of the setup below llama-server in Router Mode Normally you start llama-server with a single model: llama-server -m some-model.gguf --port 8080 This works but it ties one server process to one model, which is exactly the thing I wanted to get rid of. Recent llama.cpp builds have a router mode . If you start llama-server without -m , it does not load anything. Instead it becomes a small router that knows about a set of models, and when a request comes in for a model that is not loaded yet, it spawns a child llama-server for that model on a random port and forwards the request to it. You can see this in the logs: srv ensure model: model name=muse-glimmer-30B is not loaded, loading... srv load: spawning server instance with name=muse-glimmer-30B on port 63040 The model name here comes from the model field of the request, the same field you would send to the openai api. So from the client’s point of view it is just one openai compatible endpoint at http://127.0.0.1:8080/v1 that happens to serve many models Two flags matter here. --models-preset points the router to an ini file where I describe each model, and --models-max sets how many models can be loaded at once. The default is 4, which would happily try to load both my models and run out of memory, so I set it to 1. Now when I ask for qwen while muse is loaded, the router stops muse first and then loads qwen llama-server --models-preset ~/.pi/llama-presets.ini --models-max 1 \ --host 127.0.0.1 --port 8080 The router also picks up anything I have ever pulled with llama-server -hf