@octocodeai/freellama-mcp-server 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CONFIGURATION.md +82 -3
  2. package/DEVELOPING.md +40 -9
  3. package/README.md +316 -213
  4. package/dist/agent-cli.js +16963 -0
  5. package/dist/index.js +4896 -4036
  6. package/dist/research/bash-agent.js +287 -108
  7. package/dist/research/coder-agent.js +455 -127
  8. package/dist/research/github-agent.js +276 -94
  9. package/dist/research/octocode-agent.js +276 -94
  10. package/docs/AGENT_CLI.md +234 -0
  11. package/docs/API.md +160 -0
  12. package/docs/ARCHITECTURE.md +158 -50
  13. package/docs/ASSESSMENT.md +131 -0
  14. package/docs/BENCHMARK.md +381 -0
  15. package/docs/BUILD_AND_NOTES.md +12 -3
  16. package/docs/CLI.md +62 -17
  17. package/docs/CPU_GPU_ROUTING.md +22 -9
  18. package/docs/DEFERRED_JOBS.md +17 -4
  19. package/docs/FEATURES.md +97 -43
  20. package/docs/LLAMACPP.md +133 -0
  21. package/docs/LOCAL_MODELS.md +26 -20
  22. package/docs/MODEL_FORMS_AND_RESULTS.md +14 -6
  23. package/docs/MODEL_INTROSPECTION.md +9 -2
  24. package/docs/MODEL_SELECTION.md +38 -9
  25. package/docs/MONITORING.md +110 -12
  26. package/docs/OLLAMA_REVIEW.md +226 -0
  27. package/docs/PRODUCTION.md +68 -25
  28. package/docs/PROMPTS.md +119 -0
  29. package/docs/README.md +22 -6
  30. package/docs/RELIABILITY.md +207 -0
  31. package/docs/RUST_RESOURCE_AUDIT.md +65 -0
  32. package/docs/SCOPES_AND_WARMING.md +12 -5
  33. package/docs/TESTING.md +112 -6
  34. package/docs/TUNING.md +12 -8
  35. package/docs/assets/benchmark-results-2026-11-14.html +4 -0
  36. package/docs/assets/tool-calling-2026-11-14-bonsai-9b/TABLE.md +14 -0
  37. package/docs/assets/tool-calling-2026-11-14-lfm3-vl/TABLE.md +14 -0
  38. package/docs/assets/tool-calling-table.md +14 -0
  39. package/docs/cookbooks/README.md +9 -2
  40. package/docs/cookbooks/batch-text.md +4 -2
  41. package/docs/cookbooks/classify.md +19 -3
  42. package/docs/cookbooks/coding.md +13 -4
  43. package/docs/cookbooks/find-code.md +8 -3
  44. package/docs/cookbooks/jobs.md +8 -8
  45. package/docs/cookbooks/memory-and-caching.md +2 -2
  46. package/docs/cookbooks/ocr.md +3 -3
  47. package/docs/cookbooks/research-agent.md +32 -2
  48. package/docs/cookbooks/speech-to-text.md +1 -1
  49. package/docs/cookbooks/sweep.md +4 -1
  50. package/docs/octocode/GITHUB.md +4 -4
  51. package/docs/octocode/LOCAL.md +5 -4
  52. package/native/index.d.ts +1 -1
  53. package/package.json +6 -6
  54. package/docs/COMPARISON.md +0 -40
@@ -0,0 +1,381 @@
1
+ # Benchmark
2
+
3
+ One run, measured on 2026-11-14 on an Apple M2 Pro (32 GB): FreeLlama against Ollama alone,
4
+ llama.cpp, llama-swap and LocalAI, all loading the same hash-verified model file. Every one of the
5
+ 360 measured answers was correct and byte-identical for that single extraction fixture.
6
+ This is separate from [result-check accuracy and local tool-calling evidence](RELIABILITY.md).
7
+ The current explicit [managed llama.cpp text path](LLAMACPP.md) is also separate from these
8
+ Ollama-backed FreeLlama measurements.
9
+
10
+ ![Ollama, FreeLlama and the most popular local runners on six measured KPIs](assets/benchmark-overview.png)
11
+
12
+ Then the [features](#features): what FreeLlama's management changes, each measured against the
13
+ same work without it.
14
+
15
+ ## What the measured scenarios show
16
+
17
+ FreeLlama manages model selection, admission and receipts around external engines. The
18
+ FreeLlama rows below used Ollama. In this workload, its layer added a few milliseconds and
19
+ used about 30 MB of server RSS. These results describe the recorded configurations:
20
+
21
+ | Scenario | Setup measured here | Observations in this run |
22
+ |---|---|---|
23
+ | An agent (Claude Code, Cursor, scripts) that offloads bounded work to local models on a machine that also runs editors, browsers and builds | **FreeLlama on Ollama** | Same speed as Ollama (274 vs 272 ms median, first streamed token 95 vs 93 ms); its own work is 3.7 ms per task and its server uses 26–31 MB. Its built-in workflow prompts passed 45/46 completed graded cases versus 25/48 for one-line prompts (planned-denominator limits below), in less time and fewer tokens. An interactive task behind 8 queued ones answered in 1.40 s instead of 3.52 s; repeated code searches and sweeps reuse saved work (55.9 s → 0.13 s, 49.8 s → 18 ms); long tasks return a job id in 29 ms. It refuses a load that would not fit and names what holds the memory (live: needed 7.2 GB, 4.2 GB free, refused), selects eligible installed models with runtime admission, and returns a receipt with per-phase timing. 9.7 ms CPU per task. MCP server, CLI and an Ollama/OpenAI/Anthropic gateway. |
24
+ | One application, one model you chose, little else running | **Ollama alone** | Fastest here by 2 ms (272 ms), one-line install, the largest model library. You size models and watch memory yourself. |
25
+ | Many concurrent requests to one model | **llama.cpp `llama-server`** | Slower per request here (336 ms) but 291 tasks/min at 4 clients against 225 for Ollama, which serves one request at a time unless `OLLAMA_NUM_PARALLEL` is set (not measured). Model loads at process start. |
26
+ | Several models swapped on demand behind one OpenAI endpoint, on any engine | **llama-swap** | 332 ms, the best 4-client throughput (305 tasks/min), 1.58 s to load on first request. Limits are pools you declare, not measured memory. |
27
+ | One server for many modalities and backends (images, speech, video) | **LocalAI** | About 3× slower on this task (802 ms; 541 ms to first token) and 3.39 s cold, in exchange for breadth. |
28
+ | A desktop app for people | LM Studio, Jan | Not measured here. |
29
+
30
+ FreeLlama and Ollama are not either-or: FreeLlama runs on the Ollama you have, and Ollama's own
31
+ clients keep working through it.
32
+
33
+ ## KPIs
34
+
35
+ Six setups, the same request, 60 measured requests each over two run orders (forward and
36
+ reversed), after 2 warm-ups per pass.
37
+
38
+ | KPI | What it measures | How | Ollama alone | FreeLlama | FreeLlama streamed | llama.cpp | LocalAI | llama-swap |
39
+ |---|---|---|---:|---:|---:|---:|---:|---:|
40
+ | Correct answers | The task's JSON, field by field | Exact match against the known answer | 60/60 | 60/60 | 60/60 | 60/60 | 60/60 | 60/60 |
41
+ | Median task time | What one request costs | Client wall clock, warm model, pooled over both orders | **272 ms** | **274 ms** | **273 ms** | 336 ms | 802 ms | 332 ms |
42
+ | p95 task time | The slow tail | Same samples | 290 ms | 303 ms | 299 ms | 375 ms | 911 ms | 372 ms |
43
+ | First token | When text starts to appear | Client clock to the first non-empty content | 93 ms | — | 95 ms | 128 ms | 541 ms | 130 ms |
44
+ | Throughput, 1 client | Tasks finished per minute in sequence | Verified tasks ÷ measured window, mean of both orders | 209 | 206 | 217 | 178 | 76 | 178 |
45
+ | Throughput, 4 clients | Tasks per minute under contention | 4 concurrent clients, 30 requests (forward pass) | 225 | 221 | 222 | 291 | 145 | 305 |
46
+ | Cold request | First answer when the model is not loaded | 3 samples after an unload (`keep_alive: 0`) or a process restart | 1.13 s | 1.21 s | 1.17 s | n/c¹ | 3.39 s | 1.58 s |
47
+ | Decode speed | Engine token rate | Ollama's own timing, median of each order, averaged | 124 tok/s | 125 tok/s | 124 tok/s | — | — | — |
48
+ | Layer overhead | Time added on top of the engine | Client time minus Ollama engine time, 40 requests | — | 3.7 ms (p90 5.6) | — | — | — | — |
49
+ | Layer memory | RAM the management process itself uses | Peak RSS of the FreeLlama server process | — | 26–31 MB | 26–31 MB | — | — | — |
50
+ | Swap growth | Whether the run pushed the host into swap | Swap used at block start versus peak | 0 | 0 | 0 | 0 | 0 | 0 |
51
+ | Layer CPU | CPU the management process uses per task | `ps` CPU time of FreeLlama's server over 100 warm tasks | — | 9.7 ms | — | — | — | — |
52
+ | GPU placement | Whether the model ran fully on the GPU | Ollama `/api/ps` after each answer, from the receipt | — | 59/60 | 60/60 | — | — | — |
53
+
54
+ ¹ llama.cpp loads the model before it reports ready, so its first request after a restart is warm
55
+ (0.33 s); its load time is part of process start, not the request.
56
+
57
+ Bold marks the three setups within 2 ms of each other. Gaps under about 50 ms changed order
58
+ between earlier runs on a busier host; read them as ties.
59
+
60
+ ## Results
61
+
62
+ [Full report](assets/benchmark-results-2026-11-14.html): pooled and per-order tables,
63
+ host samples. Between the two run orders LocalAI's median moved by 100 ms (851 → 751 ms) and
64
+ llama.cpp's by 34 ms (311 → 345 ms); Ollama, FreeLlama and FreeLlama streamed stayed within
65
+ 268–275 ms in both.
66
+
67
+ **Concurrent clients** (forward pass, 30 requests per level):
68
+
69
+ | System | 2 clients: p50, tasks/min | 4 clients: p50, tasks/min |
70
+ |---|---:|---:|
71
+ | Ollama alone | 527 ms, 226 | 1,058 ms, 225 |
72
+ | FreeLlama | 534 ms, 221 | 1,068 ms, 221 |
73
+ | FreeLlama streamed | 535 ms, 220 | 1,074 ms, 222 |
74
+ | llama.cpp | 561 ms, 210 | 777 ms, 291 |
75
+ | llama-swap | 564 ms, 213 | 787 ms, 305 |
76
+ | LocalAI | 913 ms, 128 | 1,621 ms, 145 |
77
+
78
+ Ollama serves one request at a time by default, so its time per request doubles with two clients;
79
+ FreeLlama keeps its throughput within 2%. llama.cpp's parallel slots overlap requests.
80
+
81
+ ### FreeLlama's overhead, profiled
82
+
83
+ Each receipt carries `timing`: milliseconds per phase inside `serve`. On 40 warm requests the old
84
+ build spent its time on three things every request paid for: reading Ollama's `/api/tags` for a
85
+ pinned model (21 ms median; Ollama reads every manifest from disk), waiting for a fresh host
86
+ sample every 2 s (55 ms at p90), and writing learned feedback to SQLite (5 ms median). The new
87
+ build caches each endpoint's tag list for 10 s, samples the host and probes Ollama's settings in
88
+ the background, and writes feedback after the response.
89
+
90
+ | Client time minus Ollama engine time | Old build | New build |
91
+ |---|---:|---:|
92
+ | Median | 22.9 ms | 3.7 ms |
93
+ | p90 | 60.3 ms | 5.6 ms |
94
+ | Max | 81.5 ms | 29.6 ms |
95
+
96
+ Both builds measured back to back on the same host at the same load (load average about 7).
97
+
98
+ ### Memory gate, live
99
+
100
+ With the host under pressure (load average 16, swapping, 5.9 GB free of 32 GB), a FreeLlama
101
+ `summarize-text` task was held for its 20 s wait limit and then refused: "needs 7.2 GB incl. 3.6 GB reserve, 4.2 GB
102
+ free; largest memory users: Google Chrome Helper ×54 4.6 GB, Code Helper ×3 2.6 GB, node ×13
103
+ 2.4 GB…". Nothing was loaded and nothing swapped. The suite has no matching row for the other
104
+ systems yet (`--hog-gb` exists but was not run).
105
+
106
+ ## Features
107
+
108
+ ![FreeLlama's management features, measured](assets/benchmark-features.png)
109
+
110
+ Each feature measured against the same work without it, through the MCP server an agent uses,
111
+ with a fresh `serve` on the build above (`scripts/bench/features.mjs`). Same Mac, 2026-11-14. Raw
112
+ numbers: [`benchmark-features-raw-2026-11-14.json`](assets/benchmark-features-raw-2026-11-14.json).
113
+
114
+ | Feature | Measured against | Without it | With FreeLlama | How |
115
+ |---|---|---:|---:|---|
116
+ | Priority under load | One interactive task sent after 8 background tasks | 3.52 s (Ollama alone, arrival order) | **1.40 s** | Median of 7 rounds; `priority: "interactive"` vs `"background"`. The whole batch of 9 took 3.62 s and 3.67 s (+1%) |
117
+ | Built-in workflow prompts | The one-line prompt an agent would write | 52% of graded cases | **98%** | 24 cases × 2 samples on `bonsai-2:9b`; [per workflow](#built-in-prompts-measured) |
118
+ | Code search index | The first search of `packages/mcp/src` by meaning | 55.9 s | **0.13 s** (repeat) | Job `find-code` with `nearest`; the repeat embeds no unchanged chunk and returns the same top file |
119
+ | Sweep with a baseline | A first sweep: 83 chunks judged one by one by `strands-decider:2b` | 49.8 s | **18 ms** | `sweep {baseline}` reused all 83 unchanged chunks |
120
+ | Long task as a job | Waiting on one call | — | **29 ms** to a job id | `run_task {defer:true}` for `summarize-log` over `CHANGELOG.md`; done in 19.2 s while the caller polled `task_jobs` |
121
+ | Memory gate | Loading a model that does not fit | — | **Refused** | 7.2 GB needed with 4.2 GB free; nothing was loaded ([details](#memory-gate-live)) |
122
+ | GPU placement | — | — | **119/120** verified | Ollama `/api/ps` after each answer showed the whole model in VRAM |
123
+ | Layer cost | — | — | **9.7 ms CPU, 26–31 MB** per task and server | `ps` CPU time over 100 tasks; peak server RSS |
124
+
125
+ The first index and sweep runs include embedding or judging every chunk; their repeats read saved
126
+ results, so the size of the gain depends on how much changed.
127
+
128
+ Not a FreeLlama-only gain, so not in the table: a second question about the same 3,144-token
129
+ document took 5.1 s instead of 11.9 s (2,120 of 3,151 prompt tokens from cache). That is Ollama's
130
+ prompt cache; FreeLlama keeps the context size and settings stable between calls so the cache
131
+ stays usable, and Ollama alone gives the same when a client does the same. Ollama reuses it across
132
+ turns only for attention models such as `bonsai-2:9b`, not hybrid ones such as `lfm3`.
133
+
134
+ ### Built-in prompts, measured
135
+
136
+ FreeLlama's workflows send model-specific prompts evaluated on the dated cases below. To measure what that adds, the
137
+ workflow evaluation (`scripts/local-test/workflows.mjs`) ran the 9 text workflows' graded cases on
138
+ `bonsai-2:9b`, 2 samples each, once with the shipped prompts and once with the one-line prompt an
139
+ agent would write itself ([`baseline-prompts.mjs`](../scripts/local-test/baseline-prompts.mjs),
140
+ naming the answer format where the workflow needs one). Everything else (model, options, grading)
141
+ was the same. On the 24 cases graded both ways:
142
+
143
+ | | Shipped workflow prompts | One-line prompts |
144
+ |---|---:|---:|
145
+ | Pass among completed graded samples | **45/46 (98%)** | 25/48 (52%) |
146
+ | Pass / nominal 48 planned trials | 45/48 (94%) | 25/48 (52%) |
147
+ | Median time per task | **2.54 s** | 3.29 s |
148
+ | Median output tokens | **46.5** | 72.5 |
149
+
150
+ Per workflow (pass rate, median time per task):
151
+
152
+ | Workflow | Built-in prompt | One-line prompt |
153
+ |---|---:|---:|
154
+ | `summarize-text` | 7/7, 1.1 s | 6/8, 1.5 s |
155
+ | `summarize-log` | 9/9, 4.0 s | 2/10, 6.6 s |
156
+ | `summarize-diff` | 4/4, 3.1 s | 4/4, 5.2 s |
157
+ | `extract-json` | 9/10, 2.1 s | 9/10, 1.7 s |
158
+ | `explain-code` | 2/2, 6.9 s | 0/2, 27.7 s |
159
+ | `review-code` | 6/6, 1.6 s | 2/6, 13.2 s |
160
+ | `fix-bug` | 4/4, 3.0 s | 0/4, 2.7 s |
161
+ | `change-code` | 2/2, 2.7 s | 1/2, 2.8 s |
162
+ | `write-tests` | 2/2, 13.6 s | 1/2, 28.6 s |
163
+
164
+ `extract-json` is a tie: a schema-shaped task the one-line prompt already states.
165
+
166
+ The one-line prompts failed by padding past length limits (a log summary of 170 words for a 90-word
167
+ bound), inventing causes ("database" in a log that never mentions one), missing the failed step,
168
+ and leaving out the edit block. The retained aggregates contain 46 shipped completions versus 48
169
+ baseline completions over a nominal 48 planned trials per arm. The 98% figure is 45/46 completed
170
+ samples; counting the two absent shipped completions as not passed gives 45/48 (93.75%). The
171
+ individual traces for those absent samples are not in the checked-in aggregates. Earlier notes
172
+ report six memory-held attempts, but the retained data does not reconstruct those attempts or
173
+ explain their relationship to these two missing completions. Treat this as
174
+ incomplete completion evidence, not a production reliability percentage. The newer
175
+ [tool-calling smoke suite](RELIABILITY.md#public-tool-calling-smoke-suite) tests a different scope.
176
+
177
+ ## Scorecard
178
+
179
+ FreeLlama rated on measured numbers only: each target is parity with Ollama alone (which
180
+ FreeLlama runs on) or a fixed bar, and each value comes from this page.
181
+
182
+ | KPI | Target | Measured | How | Result |
183
+ |---|---|---|---|---|
184
+ | Quality: benchmark task | Same as Ollama (100%) | 60/60 correct, byte-identical to Ollama | [KPIs](#kpis) | Met |
185
+ | Quality: built-in prompts | Beat one-line prompts | 45/46 completed vs 25/48; nominal planned 45/48 vs 25/48 | [Denominators and limits](#built-in-prompts-measured) | Observed gain; incomplete shipped trials |
186
+ | Time: median task | Within 5% of Ollama | 274 vs 272 ms (+0.7%) | [KPIs](#kpis) | Met |
187
+ | Time: own overhead | p95 ≤ 15 ms | p95 9.8 ms, median 3.7 ms | [Overhead](#freellamas-overhead-profiled) | Met |
188
+ | Time: first token | Within 10% of Ollama | 95 vs 93 ms (+2%) | [KPIs](#kpis) | Met |
189
+ | Load: 4 clients | ≥ 95% of Ollama's throughput | 221 vs 225 tasks/min (98%) | [Results](#results) | Met |
190
+ | Switching: cold request | Within 5% of Ollama | 1.21 vs 1.13 s (+7%) | [KPIs](#kpis) | **Missed** |
191
+ | CPU | < 2% of one core while serving | 9.7 ms CPU per task, 1.6% of a core | below | Met |
192
+ | Memory: the layer | ≤ 50 MB | 26–31 MB server RSS | [KPIs](#kpis) | Met |
193
+ | Memory: under pressure | Refuse rather than swap | Refused 7.2 GB with 4.2 GB free; swap +0 in every block | [Memory gate](#memory-gate-live) | Met |
194
+ | GPU placement | Every receipt verified | 119/120 verified fully on the GPU | below | **Missed** |
195
+
196
+ The two misses: a cold request costs about 80 ms more through FreeLlama (admission, a host sample
197
+ and the residency watch around the load), and one receipt could not verify placement because the
198
+ model was no longer resident when FreeLlama looked after the answer.
199
+
200
+ **CPU** was measured as the `ps` CPU time (user + system) of FreeLlama's server process over 100
201
+ warm managed tasks, background host sampling included: 9.7 ms per task, against 10.8 ms for
202
+ Ollama's own server process over the same tasks, with tasks 0.6 s apart. **GPU placement** is the
203
+ receipt's `execution.observation`: Ollama's `/api/ps` after each answer showed the model's whole
204
+ size in VRAM (`processor: gpu`) for 119 of 120 FreeLlama tasks.
205
+
206
+ ## Competitor landscape
207
+
208
+ Checked on 2026-11-13 using GitHub repository metadata (displayed stars and last push). Stars help
209
+ choose which tools to look at. They do not measure quality or speed.
210
+
211
+ **Runtimes and servers.** These load and run the model themselves.
212
+
213
+ | Project | Stars | Status here |
214
+ |---|---:|---|
215
+ | [Ollama](https://github.com/ollama/ollama) | 186.9k | Benchmarked (baseline; FreeLlama's upstream) |
216
+ | [llama.cpp](https://github.com/ggml-org/llama.cpp) | 134.9k | Benchmarked (`llama-server`) |
217
+ | [vLLM](https://github.com/vllm-project/vllm) | 96.9k | Not run: no Apple Metal backend |
218
+ | [GPT4All](https://github.com/nomic-ai/gpt4all) | 77.3k | Not run: desktop app, last push 2025-05 |
219
+ | [LocalAI](https://github.com/mudler/LocalAI) | 50.4k | Benchmarked |
220
+ | [exo](https://github.com/exo-explore/exo) | 48.4k | Not run: built for clusters of several machines |
221
+ | [textgen (oobabooga)](https://github.com/oobabooga/textgen) | 47.7k | Not run: desktop app |
222
+ | [Jan](https://github.com/janhq/jan) | 45.7k | Not run: desktop app |
223
+ | [SGLang](https://github.com/sgl-project/sglang) | 38.8k | Not run: GPU-server engine |
224
+ | [llamafile](https://github.com/mozilla-ai/llamafile) | 26.6k | Not run: same engine family as llama.cpp |
225
+ | [oMLX](https://github.com/jundot/omlx) | 23.6k | Not run: needs a separate MLX artifact and a third-party tap |
226
+
227
+ **Lifecycle, routing and agent layers.** These are FreeLlama's closest peers. They sit in front
228
+ of an engine.
229
+
230
+ | Project | Stars | Relation to FreeLlama | Status here |
231
+ |---|---:|---|---|
232
+ | [LiteLLM](https://github.com/BerriAI/litellm) | 63.2k | Gateway for many providers: budgets, retries | Not run: a different layer |
233
+ | [llama-swap](https://github.com/mostlygeek/llama-swap) | 6.2k | Starts engines on demand and swaps models | Benchmarked |
234
+ | [GPUStack](https://github.com/gpustack/gpustack) | 6.0k | Schedules models across GPU clusters | Not run: needs several machines |
235
+
236
+ Smaller runtimes, not run: [KoboldCpp](https://github.com/LostRuins/koboldcpp) (12.3k) and
237
+ [mlx-lm](https://github.com/ml-explore/mlx-lm) (7.7k) share an engine family or artifact format with
238
+ those above; [Lemonade](https://github.com/lemonade-sdk/lemonade) (6.2k),
239
+ [LM Studio's CLI](https://github.com/lmstudio-ai/lms) (5.4k), [RamaLama](https://github.com/containers/ramalama)
240
+ (3.1k) and [Docker Model Runner](https://github.com/docker/model-runner) (0.7k) were not installed.
241
+ Ollama-specific layers under 1k stars ([ollama-mcp-bridge](https://github.com/patruff/ollama-mcp-bridge),
242
+ [Olla](https://github.com/thushan/olla), [ollama-mcp](https://github.com/rawveg/ollama-mcp),
243
+ [herd](https://github.com/tom-swift-tech/herd), [ollama-queue-proxy](https://github.com/TadMSTR/ollama-queue-proxy))
244
+ cover one piece each: MCP pass-through, load balancing, GPU routing or a priority queue.
245
+
246
+ Tools at other layers (Open WebUI 158.6k, Fabric 44.6k, llm 12.8k, aichat 10.6k) are clients that
247
+ sit above FreeLlama, not peers. [TensorZero](https://github.com/tensorzero/tensorzero) is now archived,
248
+ and [RouteLLM](https://github.com/lm-sys/RouteLLM) was last pushed in 2024-08.
249
+
250
+ **How systems were chosen.** A system was benchmarked when it serves an HTTP API on this Mac and
251
+ can load the same GGUF bytes. llama-swap was added as the closest lifecycle manager. oMLX was left
252
+ out by the operator: it would need a third-party Homebrew tap plus a 1.71 GB MLX copy of the model
253
+ (`mlx-community/LFM3-VL-2B-OptiQ-4bit`), and that copy would be a separate cohort.
254
+
255
+ ### Features beyond speed
256
+
257
+ Checked on 2026-11-14 against each project's README, docs and release notes, not by running
258
+ them. "Partial" means the feature exists with a limit named in the cell.
259
+
260
+ | System | Memory check before a load | Suggests a model that fits | MCP server for agents | OpenAI / Anthropic / Ollama API | Streaming | Prometheus metrics |
261
+ |---|---|---|---|---|---|---|
262
+ | FreeLlama 0.12.0 | Yes: refuses a cold load that would not fit live free memory and names what holds it | Yes: machine-local calibrated routing, then installed catalog fallback and runtime admission | Yes (13 tools), plus a CLI | Gateway: all three | Yes: managed tasks stream with their receipt (`stream: true`); gateway workflows stream through Ollama | Yes, plus a receipt per task |
263
+ | Ollama 0.40.16 | Partial: splits layers to fit VRAM; refuses only when total RAM is short | Partial: featured models, not fitted to the machine | No (`ollama launch` configures agents) | Yes / partial / native | Yes | No |
264
+ | llama.cpp 1.0.0 | Partial: `--fit` on by default; the router unloads to make room, then loads anyway | No | No (MCP client only) | Yes / yes / no | Yes | Yes (opt-in) |
265
+ | LM Studio 0.4.28 | Yes: resource guardrails, `lms load --estimate-only` | Partial: estimates | No (MCP client) | Yes / yes / no | Yes | No |
266
+ | LocalAI 4.12.1 | Yes: `--vram-budget` cap and context auto-fit | Yes: hardware-based recommendations | Unclear (client documented) | Yes / yes / yes | Yes | Yes |
267
+ | oMLX 0.8.0rc1 | Yes: process memory cap and eviction | Yes: first-run fit helper | No (MCP client) | Yes / yes / no | Yes | No |
268
+ | llama-swap v272 | Partial: evicts by declared pools, not measured memory | No | No | Yes / yes / no (passthrough) | Yes | Yes |
269
+ | vLLM 0.32.0 | Not found in docs | Partial: Metal recipes by Mac memory | No | Yes / yes / no | Yes | Yes |
270
+ | Jan 0.8.8 | Partial: fit check; auto-fit off by default since 0.8.1 | Partial | No (MCP client) | Chat completions / unknown / no | Unknown | Unknown |
271
+ | GPT4All 3.10.0 | No (static RAM labels) | Partial: static labels | No | Partial / no / no | Unknown | No |
272
+
273
+ FreeLlama inherits Ollama's engine, formats and modalities (vision, embeddings, speech) and adds
274
+ the gate, the pick and the receipts. Memory-aware loading is no longer rare: LocalAI, oMLX, LM
275
+ Studio and llama.cpp's router each have their own. FreeLlama's distinct parts are a gate driven
276
+ by live host signals, [machine-local calibrated picks with catalog fallback](MODEL_SELECTION.md#turn-measurements-into-routing-evidence),
277
+ job prompts, and an MCP server for bounded agent work. These current selection rules were not
278
+ measured by the pinned single-model comparison above.
279
+
280
+ The dated feature snapshot above lists llama.cpp 1.0.0 (2026-11-13) and Ollama 0.40.16; the measured
281
+ run used llama.cpp build 11960 and Ollama 0.40.15. Do not transfer the results to different versions.
282
+
283
+ ## Setup
284
+
285
+ | Item | Value |
286
+ |---|---|
287
+ | Host | Apple M2 Pro, 12 CPUs, 32 GiB unified memory; other applications open; 1-minute load average 9.8 at the start, 5.3 at the end |
288
+ | Model | `lfm3-vl:2b-q4_K_M` (LFM3 1.7B, Q4_K_M), one GGUF file, SHA-256 `56dac2e192ff0d9ab8507f4f1aeb4aed0463449bbbefaac79497f041c95f21eb`, checked with `shasum -a 256` |
289
+ | Task | Extract `id`, `team` and `priority` from a support ticket as JSON; temperature 0, seed 42, 64 output tokens, 4,096 context, thinking off |
290
+ | Tokens per task | 75 prompt + 22 output (Ollama, FreeLlama, LocalAI); 74 + 24 (llama.cpp, llama-swap): each server applies the model's chat template its own way |
291
+ | Ollama | 0.40.15 (`/api/chat`) |
292
+ | FreeLlama | 0.12.0 plus the unreleased changes in the [changelog](../CHANGELOG.md): managed tasks (`/_freellama/v1/tasks`), streamed with `stream: true` |
293
+ | llama.cpp | `llama-server` 0.8.0, build 11960 (`8642541b2`), `-ngl 99 --jinja --reasoning-budget 0` |
294
+ | llama-swap | v272 (`802f1c4`) over the same `llama-server` |
295
+ | LocalAI | 4.12.1, `llama-cpp-development` backend, `gpu_layers: 99`, reasoning off |
296
+ | Procedure | One system at a time; 2 warm-ups, 30 measured requests per pass; forward and reversed order; then 2- and 4-client levels and 3 cold requests (forward only). After each Ollama-backed block the harness unloads the model it loaded |
297
+
298
+ llama.cpp 1.0.0 and Ollama 0.40.16 are outside this measured version cohort.
299
+
300
+ ## Limits
301
+
302
+ - One machine, one small model, one short task. Larger models, long prompts and long outputs can
303
+ order the systems differently.
304
+ - Ollama ran with its defaults (`OLLAMA_NUM_PARALLEL` unset); with parallel slots its concurrent
305
+ throughput would change. Not measured.
306
+ - Whole-host CPU includes other applications, so it is recorded in the report, not compared.
307
+ - Process RSS undercounts GPU buffers on unified memory; only FreeLlama's own server RSS is quoted.
308
+ - The memory-pressure comparison (`--hog-gb`) and the agent-token meter are built but not run.
309
+ - Prompt quality was measured on one model (`bonsai-2:9b`), 2 samples per case, with deterministic
310
+ graders; other models gain differently. The one-line baseline is one reasonable agent prompt, not
311
+ the best an agent could write.
312
+ - The feature runs are one sample of each scenario on a busy workstation (load average 10–17),
313
+ except priority (7 rounds). An earlier priority run gave the same interactive gain (3.80 s →
314
+ 1.57 s) but a 17% slower batch; the batch cost varies with load.
315
+ - CPU and the overhead before/after were measured outside the benchmark run, on the same host, with
316
+ the same request, minutes apart.
317
+ - Targets in the scorecard were set by the FreeLlama team: parity with Ollama, or the RFC's bars.
318
+
319
+ ## Reproduce
320
+
321
+ 1. Install the engines: `bash scripts/bench/setup-macos.sh --install` (llama.cpp, LocalAI, oMLX).
322
+ Get llama-swap from its [GitHub release](https://github.com/mostlygeek/llama-swap/releases) and
323
+ check the archive's SHA-256. No model is downloaded automatically.
324
+ 2. Set up LocalAI from
325
+ [`localai-model.example.yaml`](../scripts/bench/localai-model.example.yaml). If the stable
326
+ `llama-cpp` backend rejects your model's architecture (4.12.1 rejected `lfm3`), use
327
+ `llama-cpp-development`. Keep
328
+ `reasoning.disable`, or LocalAI spends the 64-token cap on reasoning and returns an empty answer.
329
+ Set up llama-swap from [`llama-swap.example.yaml`](../scripts/bench/llama-swap.example.yaml).
330
+ 3. Add `start` commands, or start each service yourself, then run preflight, a forward pass and a
331
+ reversed pass:
332
+
333
+ ```sh
334
+ node scripts/bench/preflight.mjs --config CONFIG.json --hash-model --out NEW_PREFLIGHT_DIR
335
+ node scripts/bench/run.mjs --config CONFIG.json --samples 30 --out NEW_FORWARD_DIR
336
+ node scripts/bench/run.mjs --config CONFIG.reversed.json --samples 30 --out NEW_REVERSED_DIR
337
+ node scripts/bench/report.mjs --report NEW_FORWARD_DIR/summary.json --report NEW_REVERSED_DIR/summary.json --out NEW_REPORT.html
338
+ ```
339
+
340
+ With two or more `--report` files, the page shows a pooled table (p50/p95 from each run's raw
341
+ `trials.jsonl`) and one table per run order. Preflight reports vendors with `start` as
342
+ `owned_start` and checks only their start command.
343
+
344
+ After each Ollama-backed block, the runner unloads the benchmark model it warmed (`keep_alive: 0`
345
+ at `ollama_base_url`, confirmed by `/api/ps`); `--keep-loaded` skips this. Optional coverage flags,
346
+ described in the [bench README](../scripts/bench/README.md#run-options):
347
+
348
+ - `--max-load-per-cpu X [--strict-load]`: record load and top CPU processes at each block start;
349
+ warn above X, or skip the block as `not_run`.
350
+ - `--concurrency 2,4,8`: C1, N concurrent clients per level on the warm model.
351
+ - `--cold [--cold-samples N]`: C3, unload or restart, then time the first request.
352
+ - `--hog-gb N [--force-hog]` with `--cold`: C2, hold N GiB during each block. Refused above
353
+ estimated available minus 2 GiB unless forced.
354
+
355
+ Keep the background workload fixed and note it. Before claiming a difference, compare it with the
356
+ drift between passes. The runner stops every later vendor if an inference outcome is uncertain or
357
+ if it cannot stop a server it started.
358
+
359
+ The [features](#features) run against a fresh `serve` with its own home and the installed models
360
+ (`--chat-model`, `--decide-model`); it pulls nothing:
361
+
362
+ ```sh
363
+ node scripts/bench/features.mjs --binary target/release/freellama --out FEATURES.json
364
+ ```
365
+
366
+ Local evidence (Git-ignored): `.octocode/bench/live-2026-11-14-v2/` holds the configs, both
367
+ passes (`run-forward/`, `run-reversed/`), raw `trials.jsonl`, cold and concurrency samples, server
368
+ logs, host samples and `background-load.txt`. The charts are drawn from it by
369
+ `node scripts/bench/readme-charts.mjs --run DIR --run DIR --facts docs/assets/benchmark-facts-2026-11-14.json --out docs/assets`.
370
+
371
+ ## Verification
372
+
373
+ Historical verification recorded on 2026-11-14 after the benchmark build (before the later
374
+ calibration, engine and refactor changes): `yarn test:js` **1,280 pass** (1 skipped), `yarn
375
+ test:bench` **112 pass**, MCP integration against live Ollama **160 pass**, and the Rust suite
376
+ all unit tests pass (427) with **3 integration-contract failures** (293 pass), all in tests the
377
+ earlier staged branch work did not update (digest-verified
378
+ placement and no inference retry after a 500); none touches the changes measured here. `cargo
379
+ clippy -D warnings` is clean. The production gate (`yarn verify:production`) was not run for that snapshot. These counts and
380
+ failures are preserved as historical context; they are not the current checkout's verification
381
+ status. Use [Testing](TESTING.md) for the current gate commands.
@@ -1,6 +1,8 @@
1
1
  # Build from source and operating notes
2
2
 
3
- Moved from the root README.
3
+ Published npm installs need Node.js 22+ and the matching prebuilt platform package; they do not
4
+ compile Rust. Use the [installation guide](../README.md) for that path. The commands below are for
5
+ developing this checkout or building your own executable and addon.
4
6
 
5
7
  ## Good to know
6
8
 
@@ -13,19 +15,26 @@ Moved from the root README.
13
15
  - Without CA certificates, as in slim containers, FreeLlama still serves plain-HTTP Ollama. Install
14
16
  `ca-certificates` for HTTPS upstreams.
15
17
  - The service binds to loopback. For remote access, see [Production](PRODUCTION.md).
18
+ - Default loopback ports: 11434 Ollama, 11435 `freellama serve` (`--listen`), 11436 the CPU-only
19
+ Ollama that `serve --spawn-cpu-backend` starts (`--cpu-backend-port`), 11437 `freellama gateway`
20
+ (`--port`).
16
21
  - Port 11435 is also Ollaya's default ([Ollaya API](https://github.com/ollaya-dev/ollaya/blob/main/docs/api.md)).
17
22
  To run both, use `serve --listen` on another port and set the MCP server's
18
23
  `FREELLAMA_SERVE_ENDPOINT` to match.
19
24
 
20
25
  ## Build from source
21
26
 
22
- Requires Rust 1.89+ (`rust-toolchain.toml` pins the compiler) and Node.js 22.12+ with Corepack.
27
+ Requires Node.js 22.12+ with Corepack and the Rust compiler selected by `rust-toolchain.toml`
28
+ (currently 1.99.0). `Cargo.toml` declares a 1.89 minimum; that floor is separate from the pinned
29
+ development compiler and needs its own check before claiming minimum-version compatibility.
23
30
 
24
31
  ```bash
25
32
  corepack enable && yarn install && yarn build
26
33
  ./target/release/freellama serve
27
34
  node packages/mcp/dist/index.js # MCP server from this checkout
35
+ node packages/cli/bin/freellama.js run --help # the agent commands (run, chat, gateway…)
28
36
  ```
29
37
 
30
38
  npm releases can lag this checkout. [Testing](TESTING.md) describes the checks; the release gate
31
- is `yarn verify:production`.
39
+ is `yarn verify:production`. It includes live-system tiers and all-platform release artifacts;
40
+ building successfully on the current host does not satisfy that complete gate.
package/docs/CLI.md CHANGED
@@ -5,6 +5,9 @@ work. It exposes diagnostics, model routing, managed execution, resource control
5
5
  and Ollama-compatible passthrough. Use the [README CLI section](https://github.com/bgauryy/FreeLlama/blob/main/README.md#from-the-terminal) for first
6
6
  use and this reference to look up commands and controls.
7
7
 
8
+ To use local models from a shell the way `ollama run` works, with job picks, workflows and files,
9
+ use the [agent commands](AGENT_CLI.md) (`freellama run`, `chat`, `gateway`…). [Every API](API.md) puts all the surfaces on one page.
10
+
8
11
  ## Choose a command
9
12
 
10
13
  ```mermaid
@@ -73,6 +76,12 @@ model; see [client keys and other engines](PRODUCTION.md#client-keys). The runti
73
76
  `[preload]` table loads models at startup and predicts the next one; see
74
77
  [Preload](SCOPES_AND_WARMING.md#preload).
75
78
 
79
+ An upstream with an explicit `[upstream.llamacpp]` configuration also supports bounded managed
80
+ text tasks through an exact `--model NAME/MODEL` assignment. It requires fixed context/output
81
+ limits and a conservative memory reservation; physical placement and benchmark confidence stay
82
+ unknown. It does not participate in automatic job selection or Ollama model lifecycle controls.
83
+ See [managed llama.cpp](LLAMACPP.md) for setup and supported fields.
84
+
76
85
  `serve` exposes both managed routes under `/_freellama/v1/*` and byte-preserving Ollama routes under
77
86
  `/api/*` and `/v1/*`. Use `proxy` when you need only the latter. See
78
87
  [Architecture](ARCHITECTURE.md) for the request flow.
@@ -93,10 +102,12 @@ Or let `serve` start and own the CPU process (stopped when `serve` exits; a port
93
102
  is refused):
94
103
 
95
104
  ```bash
96
- npx @octocodeai/freellama serve --spawn-cpu-backend --cpu-model nomic-embed-text:latest \
97
- [--cpu-backend-port 11436] [--ollama-binary /path/to/ollama]
105
+ npx @octocodeai/freellama serve --spawn-cpu-backend \
106
+ --cpu-model nomic-embed-text:latest --cpu-backend-port 11436
98
107
  ```
99
108
 
109
+ Use `--ollama-binary /absolute/path/to/ollama` if the intended executable is not `ollama` on `PATH`.
110
+
100
111
  FreeLlama rejects a CPU upstream with no assignments, `--cpu-model` without `--cpu-upstream` or
101
112
  `--spawn-cpu-backend`, both of those together, and a CPU upstream that is the same Ollama endpoint as
102
113
  `--upstream`, including loopback aliases on one port.
@@ -121,7 +132,7 @@ runtime-file value it defaults to 2 units per `OLLAMA_NUM_PARALLEL` slot of
121
132
  the Ollama server (read from its process environment where visible; Ollama's own default is 1, so 2).
122
133
  `freellama config` shows the effective value and where it came from.
123
134
  `--cpu-max-concurrent-tasks` controls the independent CPU pool and defaults to 1. Default base costs
124
- are embedding 1, chat 2, and vision 4. Embeddings multiply their base by `ceil(input_items/4)`;
135
+ are embedding and decision 1, chat 2, and vision 4. Embeddings multiply their base by `ceil(input_items/4)`;
125
136
  the acquired charge is capped to the selected backend's pool. Configure overrides in `[task_costs]`;
126
137
  see [monitoring controls](MONITORING.md#adaptive-concurrency). A saturated GPU pool does
127
138
  not consume CPU permits. `--max-queue-wait-seconds` defaults to 120; when no permit becomes
@@ -129,6 +140,8 @@ available, FreeLlama refuses the task with HTTP 503 (`admission_timeout`) instea
129
140
  A task can ask for a shorter wait with `max_wait_seconds` (`maxWaitSeconds` in MCP); it can never
130
141
  exceed the configured one. Omit it to use the server's waiting budget.
131
142
  That deadline covers weighted admission, host-resource waiting, and model-transition locking together.
143
+ `FREELLAMA_MAX_REQUEST_BYTES` also caps each managed Ollama response at 64 MiB by default, including
144
+ embedding, decision, error and NDJSON wire bytes; an oversized response fails with nonretryable 502.
132
145
  `--max-queued-tasks` (default 16) and `--cpu-max-queued-tasks` (default 8) also bound how many
133
146
  parsed requests can be retained per backend; a full queue receives 429 (`admission_queue_full`)
134
147
  immediately, and cancelled clients release their waiter. Every capacity refusal carries a
@@ -160,15 +173,15 @@ unknown measurements, and reserved memory. These flags do not change Ollama or O
160
173
  When effective available RAM does not cover already-reserved bytes, the task's estimated bytes and the
161
174
  reserve, FreeLlama first unloads idle unpinned runners, then holds the new load with
162
175
  `held_insufficient_capacity` until capacity returns, refusing with 503 at its deadline. This is the
163
- default on every platform: Linux and Windows available RAM already counts reclaimable cache (and is
164
- the hard cap in a container), and on macOS effective available RAM is free memory plus a bounded
165
- reclaimable estimate. A load already resident is never held by it. `--low-memory advisory` (or
176
+ default on every platform: Linux uses kernel `MemAvailable` capped by a smaller cgroup limit,
177
+ Windows uses CIM `FreePhysicalMemory`, and macOS adds a bounded reclaimable estimate to free RAM.
178
+ A load already resident is never held by this capacity check. `--low-memory advisory` (or
166
179
  `FREELLAMA_LOW_MEMORY=advisory`) instead dispatches such a load: the receipt reports
167
180
  `status: "admitted_low_memory_estimate"` and `assessment.memory_advisory: "below_reserve"` with
168
181
  `required_with_reserve_bytes` and `effective_available_bytes`, and Ollama and the OS decide whether
169
182
  it fits.
170
183
  OS memory pressure holds new loads where the OS reports it (macOS, and Linux when the kernel
171
- exposes `/proc/pressure/memory`; Windows has no host collector yet), and holds already-resident
184
+ exposes `/proc/pressure/memory`; Windows does not expose this signal through its collector), and holds already-resident
172
185
  runners too only at the critical level: a warning is what a large load itself raises. Active
173
186
  swapping and low RAM hold new loads only: an already-resident runner adds no memory and stays
174
187
  admitted, for raw passthrough requests too when the request's `model` is in Ollama's `/api/ps`. The CPU-load hold (one-minute load at
@@ -181,15 +194,17 @@ request on a runner already resident on the GPU proceeds. The receipt's `resourc
181
194
  lists the holds that applied to the request.
182
195
  Both `serve` and `proxy` accept `--resource-telemetry-policy`:
183
196
 
184
- - `require-memory` (default on Linux and macOS): wait or refuse local inference when available RAM is
197
+ - `require-memory` (default on Linux, macOS and Windows): wait or refuse local inference when available RAM is
185
198
  unknown, including raw requests with zero forecast bytes. Missing thermal data alone does not block
186
199
  this policy.
187
200
  - `require-all`: require all signals applicable to the collector. The built-in Linux collector reads
188
201
  OS memory pressure from `/proc/pressure/memory` when the kernel provides it, but has no thermal
189
202
  signal and marks it as unsupported; custom collectors must provide it. Unsupported signals are not
190
203
  evidence of healthy hardware.
191
- - `best-effort`: explicitly permit unknown telemetry unless an observed pressure hold is active. This
192
- is the default on Windows, which has no built-in memory collector.
204
+ - `best-effort`: explicitly permit unknown telemetry unless an observed pressure hold is active.
205
+ Unsupported platforms without a collector use this default. Windows instead requires its bounded
206
+ PowerShell/CIM memory observation; its CPU-load, OS-pressure, thermal, swap and GPU signals remain
207
+ unknown, so `require-all` holds work there. See [Windows memory telemetry](MONITORING.md#read-the-live-status).
193
208
 
194
209
  Remote upstreams bypass local host policy. Raw metadata and empty unload requests remain available
195
210
  during a hold. Route receipts separate `queue_readiness` from `resource_readiness`, and include
@@ -291,6 +306,10 @@ Useful task options include:
291
306
  - `--stop-on-repetition` to stream the task and stop once the output repeats itself, keeping all
292
307
  generated text.
293
308
  - `--defer` to return a job ID immediately.
309
+ - repeatable `--fallback-model TAG` for an ordered fallback chain (up to 4): each model runs only
310
+ after the one before it was refused before anything executed (no room by the wait deadline, input
311
+ over its context window, not installed or ineligible, circuit open), never after a model started.
312
+ The receipt's `fallback` lists every attempt ([fallback chains](FEATURES.md#fallback-chains)).
294
313
 
295
314
  Inspect deferred work with `jobs`, retrieve a result with `jobs --id <uuid>`, and cancel one task
296
315
  with `jobs --id <uuid> --cancel`. Remove one task with `jobs --id <uuid> --remove`: active work is
@@ -300,10 +319,13 @@ through MCP `task_jobs`.
300
319
  Cancellation and removal wait for local execution permits to be released; they do not unload a shared model
301
320
  or confirm that the physical runner has stopped.
302
321
 
303
- Jobs stay in server memory: at most 64 receipts, 16 MiB per input (64 MiB across queued and running
322
+ By default jobs stay in server memory: at most 64 receipts, 16 MiB per input (64 MiB across queued and running
304
323
  jobs), and 2 MiB per retained result or error. Finished receipts expire after 10 minutes and can be evicted earlier to make room for new
305
324
  work. A restart discards all jobs. Admission queue limits still apply, and deferred work can fail
306
325
  after acceptance. See [monitoring](MONITORING.md) for states and waiting reasons.
326
+ Opt-in durable receipts retain terminal results and idempotency records in the file-backed store;
327
+ they never replay prompts after restart. See [durable deferred jobs](DEFERRED_JOBS.md) before
328
+ enabling `FREELLAMA_DURABLE_JOBS` or using an idempotency key.
307
329
 
308
330
  Create a session with `session`, then pass its identifier to related `route` or `task` calls. A
309
331
  route preview honors an existing affinity but does not create or change one. Only a successfully
@@ -345,6 +367,15 @@ npx @octocodeai/freellama bench-all --output benchmark-report.json
345
367
  npx @octocodeai/freellama serve
346
368
  ```
347
369
 
370
+ Without `--output`, `bench-all` writes `benchmark-report.json` in the FreeLlama home so the next
371
+ `serve` start discovers it automatically. Use repeated `--include EXACT_TAG` flags to bound the run.
372
+ Reports qualify only their measured host/hardware, endpoint and artifact digests; old or foreign
373
+ reports do not qualify routing. Schema-2 reports record a hashed machine identity and observed
374
+ hardware; missing identity or remote endpoints cannot establish machine-local throughput. A primary
375
+ endpoint report does not qualify a separately assigned CPU runner. Throughput screens do not
376
+ establish job accuracy; that requires task-quality evidence and verification. See
377
+ [machine calibration](MODEL_SELECTION.md#turn-measurements-into-routing-evidence).
378
+
348
379
  `serve` resolves each file in this order: the explicit flag, its environment variable, the working
349
380
  directory, then the FreeLlama home (`~/.octocode/freellama`, or `--home` / `FREELLAMA_HOME`). The home also holds
350
381
  `runtime.toml`, read when neither `--runtime-config` nor `FREELLAMA_RUNTIME_CONFIG` is set.
@@ -362,6 +393,10 @@ schema_version = 1
362
393
  [policies.code_repair]
363
394
  qualified_models = ["qwen3.8:27b-mlx"] # best first; quality-qualified only
364
395
 
396
+ # Preserve the digest recorded by the evaluation, never substitute today's tag digest.
397
+ [policies.code_repair.model_digests]
398
+ "qwen3.8:27b-mlx" = "sha256:REPLACE_WITH_EVALUATED_DIGEST"
399
+
365
400
  # Optional request defaults for one exact tag on this task: think and num_predict (>= 1).
366
401
  [policies.code_repair.model_options."qwen3.8:27b-mlx"]
367
402
  think = false
@@ -374,11 +409,12 @@ num_predict = 512
374
409
  [platform.example.toml](https://github.com/bgauryy/FreeLlama/blob/main/platform.example.toml).
375
410
 
376
411
  The MCP server autostarts `serve` with its own environment and an arbitrary working directory, so
377
- put a machine-wide policy in the config directory or set the variables in the MCP client's `env`.
412
+ put a machine-wide policy in the FreeLlama home or set the variables in the MCP client's `env`.
378
413
  `serve` prints each file it picked up.
379
414
 
380
415
  `policy from-eval` reads `pass_at_1`, not the throughput produced by `bench-all`. It refuses
381
- expired aggregates and fewer than three trials unless `--allow-smoke` is explicit, and it skips
416
+ expired aggregates and fewer than three trials unless `--allow-smoke` is explicit. That flag
417
+ produces smoke-only output for inspection, which `serve` refuses as a routing policy. It skips
382
418
  models that are not installed on `--ollama-endpoint` (default `FREELLAMA_OLLAMA_ENDPOINT`, then
383
419
  `http://127.0.0.1:11434`). This prevents speed data from being mislabeled as quality evidence.
384
420
  It accepts the task kinds `route` accepts, with `-` or `_` (`code-repair` or `code_repair`), writes
@@ -386,7 +422,16 @@ to stdout unless `--out` is given, and reports a refusal as one `error: ...` lin
386
422
 
387
423
  ## Compare the CLI and MCP surfaces
388
424
 
389
- MCP-only operations are `delegate_research` and the online `models { view: "library" }` view.
390
- CLI-only operations include `serve`, `proxy`, `bench-all`, `policy from-eval`, and `auth-token`.
391
-
392
- For the MCP tool contracts, read the [MCP package reference](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md).
425
+ | Need | Operator commands | MCP | Agent commands (`run`, `chat`, `gateway`, `tool`) |
426
+ |---|---|---|---|
427
+ | Run the service, the proxy, benchmarks, policies, tokens | `serve`, `proxy`, `bench-all`, `policy from-eval`, `auth-token` | — | — |
428
+ | One task with a job's pick, prompts and files | `task` (by task kind, no jobs) | `run_task {job\|workflow, files}` | `freellama run -j\|-w … -f …` |
429
+ | Batches, embeddings with `nearest`, decisions | `task` (one item) | `run_task {tasks}`, `nearest`, `questions` | `freellama run -j find-code`, `--ask`; batches through `freellama tool run_task` |
430
+ | Model library search and pulls | — | `models {view:"library"}`, `ollama_pull` | `freellama tool models\|ollama_pull` (approval still applies) |
431
+ | Research, sweeps, speech, saved responses | — | `delegate_research`, `sweep`, `audio`, `read_response` | `freellama tool NAME JSON` |
432
+ | Ollama, OpenAI and Anthropic clients | `serve` passthrough (real tags) | — | `freellama gateway` (virtual models and real tags) |
433
+
434
+ `freellama tool` reaches all 13 MCP tools with the same schemas, because both surfaces register from
435
+ one list. For the MCP tool contracts, read the
436
+ [MCP package reference](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md)
437
+ and [Every API](API.md#mcp-tools).