Runs models on this machine through one shared llama.cpp server, so nothing leaves the host and no provider usage window is spent.
- llama.cpp.
brew install llama.cppputsllama-serveron the PATH. The module needs the router mode of the server (--models-preset,--models-max), which a current release has; it was built and measured on build 9960. Another binary can be named withLLAMA_SERVER_BIN. - Disk and memory. The three models below take about 49 GB on disk and the 70B needs about 40 GiB of GPU memory when loaded (figures measured on an M5 Max with 64 GB, where Metal allows 51.8 GiB). On a smaller machine, remove the 70B from
LOCAL_MODELS(see Adding or removing a model) and keep the 8B and the embedding model, which together fit in about 14 GiB. - The models. Nothing is downloaded at install time. The first call that names a model makes the server download it from Hugging Face into
~/.local/share/cookbook/local-llm/models/, which can take minutes (the 70B is 37.9 GB); the tool reports progress while it waits. To fetch a model ahead of time, callllm_generate(orllm_rankfor the embedding model) once with a one-word prompt and let it finish;llm_statusshows the model as loaded or sleeping afterwards.
The models come from these Hugging Face repositories, each an ordinary GGUF repository that needs no account or token:
| Model | Repository (file chosen by the quantization after the colon) |
|---|---|
llama-3.3-70b |
bartowski/Llama-3.3-70B-Instruct-GGUF :IQ4_XS |
llama-3.1-8b |
bartowski/Meta-Llama-3.1-8B-Instruct-GGUF :Q8_0 |
qwen3-embedding-0.6b |
Qwen/Qwen3-Embedding-0.6B-GGUF :Q8_0 |
llm_status- each model's state (loaded, sleeping, loading, not downloaded), purpose, download size and context window, without loading anything.llm_generate- answer one prompt; givenjsonSchema, the answer is constrained to it, so it always parses.llm_rank- order texts by meaning against a query with the embedding model.
The server runs in router mode on 127.0.0.1:8791: one process serves every model in src/modules/local-llm/config.ts, loads a model on the first request that names it (downloading it from Hugging Face the first time), and unloads a model after ten idle minutes. One chat model is loaded at a time, next to the embedding model: the 70B and the 8B together need more GPU memory than Metal allows (measured below), and a model that runs out is left broken, so loading a chat model first unloads the other, and a request still running on that one fails. A request whose GPU work fails anyway unloads its model, so the next call loads it fresh.
The first call that finds no server starts one, detached, so it outlives the caller and every session shares the models in memory; two callers starting at once both spawn, the port lets one bind, and both find it. Its files live in ~/.local/share/cookbook/local-llm/ (the model list it reads, server.log, and the downloaded models).
| Model | Download | GPU memory loaded | For |
|---|---|---|---|
llama-3.3-70b |
37.9 GB | 40.5 GiB | the most capable, and the slowest |
llama-3.1-8b |
8.5 GB | 12.1 GiB | fast classification and extraction |
qwen3-embedding-0.6b |
0.6 GB | 1.8 GiB | embeddings, for llm_rank and similarity |
GPU memory is llama.cpp's own load-time breakdown (weights, the KV cache for the model's whole context, compute buffers) on an M5 Max with 64 GB, where Metal allows 51.8 GiB.
The server reads a JSON schema from OpenAI's response_format.json_schema.schema; its README's { type, schema } example is silently read as no schema. A first load of a large model takes minutes and reports progress; while it downloads, loading two other models would make the server unload it and cancel the download, so let a large first download finish alone.
None. The models above download without a Hugging Face token (HF_TOKEN is only needed for a gated or private repository you add yourself), and the server binary comes from brew install llama.cpp.
| Variable | Default | Purpose |
|---|---|---|
LOCAL_LLM_PORT |
8791 |
The server's port |
LLAMA_SERVER_BIN |
llama-server |
The llama.cpp server binary |
XDG_DATA_HOME |
~/.local/share |
Base of cookbook/local-llm/ |
The models are the entries of LOCAL_MODELS in src/modules/local-llm/config.ts; the tools' model argument and llm_status follow that list. To add one:
- Find a GGUF repository on Hugging Face and pick a quantization. Repositories list one file per quantization (
Q8_0is near-lossless,Q4_K_MandIQ4_XSare smaller and faster); a repository that is gated or private needsHF_TOKENin the environment of the process that starts the server. - Add an entry with
kind(chatorembedding),hfRepoas<user>/<repository>:<quantization>,sizeGb(the download),contextTokens,parallel(concurrent requests the model serves),memoryMiband adescription.memoryMibis only reported byllm_status; take it from llama.cpp's load-time memory breakdown in the server log (~/.local/share/cookbook/local-llm/server.log) after a first load. - Rebuild (
pnpm run build). A running server rereads the list once per module process, so the next call picks the entry up without restarting the server, andmodels.iniin the data directory is rewritten from the config.
To drop a model, delete its entry. Its downloaded files stay in models/ until you delete the models--<user>--<repository> folder there.
- Claude Code: not loaded by the bridge (
isInClaudeCode: falseinsrc/modules/registry.ts). - OpenCode: no agent allows
llm_*. Every agent inopencode.jsonstarts from deny-all and none lists these tools, and OpenCode's built-in agents are disabled, so in OpenCode the tools stay unreachable until an agent gets an allow rule for them.