@octocodeai/freellama-mcp-server 0.6.0 → 0.6.2

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.
@@ -27,7 +27,7 @@ This is the canonical ownership rule; other guides link here.
27
27
  | Forecast memory reservations, host-pressure holds, and requests to unload idle unpinned models | FreeLlama managed execution |
28
28
  | Explicit per-model CPU/GPU backend assignment | FreeLlama and separate Ollama processes |
29
29
  | Grounded research adapters, citations, and verification verdicts | FreeLlama MCP server |
30
- | Bearer authentication of every route | FreeLlama; external ingress owns TLS and tenant authorization |
30
+ | Bearer authentication of every route except the static `/_freellama/ui` shell | FreeLlama; external ingress owns TLS and tenant authorization |
31
31
 
32
32
  FreeLlama is a governed router, not an unrestricted hardware scheduler: a CPU preference cannot move
33
33
  an arbitrary tag, and one managed call contains one task. Only the calling agent knows which tasks
@@ -92,9 +92,10 @@ flowchart TB
92
92
  ADAPTER --> GPU
93
93
  ```
94
94
 
95
- The CLI and MCP server do not reimplement routing. The CLI calls the core directly. The MCP server
96
- uses the feature-gated NAPI boundary for native operations and the control API for serve-backed
97
- operations.
95
+ The CLI and MCP server do not reimplement routing. CLI commands other than `serve`, `doctor`,
96
+ `init`, and benchmarking are HTTP clients of a running `freellama serve`. The MCP server reaches the
97
+ same control API through the feature-gated NAPI boundary, and unless `FREELLAMA_MCP_AUTOSTART_SERVE=0`
98
+ it starts the bundled `serve` in the background at launch when nothing answers on the default endpoint.
98
99
 
99
100
  ## Request classification
100
101
 
@@ -124,17 +125,21 @@ The control API exposes these endpoints:
124
125
  | `POST /_freellama/v1/routes` | Deterministic route decision | No |
125
126
  | `POST /_freellama/v1/sessions` | New model-affinity session | No |
126
127
  | `DELETE /_freellama/v1/sessions/:session_id` | Release model-affinity session | No |
128
+ | `POST /_freellama/v1/sessions/:session_id/kill` | Release a session and cancel its in-flight tasks; the runner stays loaded | No new inference |
127
129
  | `POST /_freellama/v1/scopes` | Create bounded message history | No |
128
130
  | `GET /_freellama/v1/scopes/:id` | Scope metadata; optional explicit history inclusion | No |
129
131
  | `POST /_freellama/v1/scopes/:id/fork` | Fork history at an expected revision | No |
130
132
  | `DELETE /_freellama/v1/scopes/:id` | Delete retained history | No |
131
133
  | `POST /_freellama/v1/warm` | Admitted load of an exact installed runner profile | Load only |
132
134
  | `POST /_freellama/v1/tasks` | Managed chat, vision, tools, or embedding task (body shape under [Request shapes](#request-shapes)) | Yes |
135
+ | `POST /_freellama/v1/task-batches` | Caller-declared independent tasks with ordered per-item receipts | Yes |
133
136
  | `GET /_freellama/v1/jobs` | Deferred job metadata without prompts or results | No |
134
137
  | `GET /_freellama/v1/jobs/:id` | One deferred receipt and retained result | No |
135
138
  | `POST /_freellama/v1/jobs/:id/cancel` | Cancel one deferred task and release its local permits | No new inference |
136
139
  | `DELETE /_freellama/v1/jobs/:id` | Cancel active work, release local permits, and remove its retained record | No new inference |
137
140
 
141
+ Status, usage, metrics, and runtime-config endpoints are listed in [monitoring](MONITORING.md).
142
+
138
143
  ### Request shapes
139
144
 
140
145
  The control API uses snake_case JSON. A managed task body is the routing fields plus the payload:
@@ -173,7 +178,7 @@ A batch nests whole task bodies:
173
178
  `POST /sessions` with an empty object returns `session_id`; `POST /warm` takes `{"model": "<tag>"}`.
174
179
 
175
180
  The machine profile is host-derived rather than model-name-derived. It reports total physical
176
- memory on macOS (`sysctl`), Linux (`/proc`), and Windows (system APIs), along with CPU, OS,
181
+ memory on macOS (`sysctl`), Linux (`/proc`), and Windows (CIM through PowerShell), along with CPU, OS,
177
182
  architecture, and disk. `memory_bytes` is portable host RAM. `unified_memory_bytes` is populated
178
183
  only when host and accelerator memory are known to share a pool; it must not be treated as discrete
179
184
  GPU VRAM. Ollama's `/api/ps` remains the placement authority.
@@ -208,7 +213,7 @@ Every field comes from the model's own metadata and from what FreeLlama observed
208
213
  name. `GET /_freellama/v1/models` adds, per model:
209
214
 
210
215
  - `context_default`: the context Ollama gives a request that leaves `num_ctx` unset (`source` is
211
- `modelfile`, `config`, `ollama_context_length`, `vram_tier`, or `resident_runner`). A managed
216
+ `modelfile`, `config`, `ollama_context_length`, `vram_tier`, `resident_runner`, or `unknown`). A managed
212
217
  task that sends a different `num_ctx` reloads the runner.
213
218
  - `resident_context`: the `context_length` of the loaded runner from `/api/ps`.
214
219
  - `observed_loops`: per task kind, managed runs since start and how many ended in a repetition
@@ -243,7 +248,7 @@ flowchart TD
243
248
  F --> X{"Explicit model?"}
244
249
  X -->|"Yes"| E["Use exact model or refuse"]
245
250
  X -->|"No"| O{"Objective"}
246
- O -->|"fastest"| S["Rank local performance,<br/>residency, and deterministic ties"]
251
+ O -->|"fastest"| S["Rank local benchmark,<br/>smaller size, and deterministic ties"]
247
252
  O -->|"balanced"| B["Prefer policy-qualified candidates;<br/>otherwise use eligible models with low confidence"]
248
253
  O -->|"quality"| Q["Require policy-qualified candidates,<br/>then prefer policy order"]
249
254
  E --> H["Grade advertised-context compatibility and evidence"]
@@ -264,8 +269,9 @@ Routing never ranks by a model's name or architecture. Automatic selection (fall
264
269
  session affinity) ranks a model last for a task kind when it has looped on that kind at least twice
265
270
  in at most twice as many managed runs; the decision then carries
266
271
  `repeats_on_this_task_last_resort`. An operator policy naming the model for the task, or an explicit
267
- `model`, always wins. No request option is added because of which model was chosen: stop sequences
268
- and decoding settings belong to the caller.
272
+ `model`, always wins. No request option is hard-coded for a model: apart from an operator policy's
273
+ per-model `model_options` (`think`, `num_predict`), stop sequences and decoding settings belong to
274
+ the caller.
269
275
 
270
276
  `context_window_fit` means only that requested `num_ctx` fits the model's advertised context
271
277
  window. It is not a live-memory, free-VRAM, K/V-cache, thermal, or runner-admission verdict.
@@ -306,7 +312,7 @@ flowchart TD
306
312
  T["POST /tasks or /task-batches"] --> R["Select eligible model + backend"]
307
313
  R --> K["Estimate metadata-backed F16 K/V<br/>(or report unknown)"]
308
314
  K --> A{"Acquire selected backend's<br/>3:2:1 fair weighted budget"}
309
- A -->|"No"| E503["503 server busy"]
315
+ A -->|"No"| E503["429 queue full or<br/>503 wait timeout"]
310
316
  A -->|"Yes"| B{"Execution backend"}
311
317
  B -->|"Primary"| GL{"GPU model resident<br/>under transition lock?"}
312
318
  B -->|"CPU assignment"| CO["Add num_gpu: 0"]
@@ -334,8 +340,8 @@ falling back to two. The optional CPU pool defaults to one. A saturated GPU burs
334
340
  consume the permit held for a small CPU helper. With one parallel slot, those values represent one
335
341
  ordinary chat and one embedding. Operators can tune both budgets from queue-wait and resident-memory evidence.
336
342
  After FreeLlama admission, each Ollama process applies its own `OLLAMA_MAX_QUEUE`, scheduler,
337
- `OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy requests bypass weighted admission but
338
- still pass the raw concurrency cap, backend exclusion, and host-pressure gate.
343
+ `OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy generation requests bypass weighted
344
+ admission but still pass the raw concurrency cap, backend exclusion, and host-pressure gate.
339
345
 
340
346
  | Task | Default cost |
341
347
  |---|---:|
@@ -375,9 +381,10 @@ stable ID and `independent:true`; dependent workflow execution is rejected befor
375
381
  It bounds local dispatch and returns ordered per-item success/error receipts. Every child still
376
382
  passes the same global admission and transition path above.
377
383
 
378
- The K/V preflight derives single-sequence F16 K+V bytes-per-token only for supported model
379
- architectures with sufficient metadata. Explicit key/value dimensions take precedence; sliding-window,
380
- shared-KV, recurrent, and unknown layouts report unknown. F16 is advisory because quantized KV can
384
+ The K/V preflight derives single-sequence F16 K+V bytes-per-token for any architecture whose
385
+ metadata declares the block count and head counts. Explicit key/value dimensions take precedence
386
+ over embedding length divided by heads; sliding-window, shared-KV, recurrent, cross-attention, and
387
+ unknown layouts report unknown. F16 is advisory because quantized KV can
381
388
  use less memory. The coarse model-file budget refuses files over 80% of local CPU/unified RAM only
382
389
  for loopback backends. Forecasts use each backend's independently attributed parallelism and KV
383
390
  cache settings where process inspection is available, with explicit fallback sources otherwise.
@@ -388,14 +395,17 @@ authority. Configured settings alone do not prove physical processor placement.
388
395
  The shared [resource governor](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/resources/mod.rs) samples local
389
396
  available-memory estimates, load per logical CPU, OS memory pressure, active swap-out growth, and
390
397
  thermal throttling when reported. OS memory pressure holds every local demand; active swap-out
391
- growth holds new loads; CPU-load and thermal holds apply to CPU-assigned work and to cold loads
392
- (decoding weights is CPU work and a new heat source) but not to a bounded request on a runner
393
- already resident on the GPU, which neither waits for the cores nor cools the package by waiting (`ResourceDemand::is_held_by`; the assessment's
398
+ growth holds new loads; the CPU-load hold applies only to CPU-assigned work (saturated cores slow
399
+ nothing on the GPU, so raw passthrough, which cannot know residency, is not refused for it), and to
400
+ all work on a host whose sampler sees no accelerator; the
401
+ thermal hold applies to CPU-assigned work and to cold loads (a new heat source) but not to a bounded
402
+ request on a runner already resident on the GPU (`ResourceDemand::is_held_by`; the assessment's
394
403
  `held_by` lists the holds that applied). Low available RAM (below the larger of 15% or 1 GiB, clearing after two
395
404
  healthy samples above the larger of 20% or 2 GiB) depends on the platform (`ResourcePolicy::low_memory`).
405
+ A new load must leave that reserve free, but never more than its own size above the 1 GiB floor.
396
406
  On macOS (`advisory`) the estimate undercounts what the OS can reclaim, so a load that does not cover
397
- it first unloads idle runners and then dispatches with a `memory_advisory`. On Linux, Windows, and
398
- other platforms (`refuse`) a new load that does not fit first unloads idle runners and then waits
407
+ it first unloads idle runners and then dispatches with a `memory_advisory`. On Linux and other
408
+ platforms (`refuse`) a new load that does not fit first unloads idle runners and then waits
399
409
  with `held_insufficient_capacity` until a permit release or a fresh sample frees capacity, or refuses
400
410
  with 503 at its deadline: Linux `MemAvailable` already includes reclaimable cache and is the hard cap
401
411
  in a container, so over-admitting invites the OOM killer. Already-resident runners add no memory and
@@ -406,8 +416,10 @@ seconds; unavailable signals stay unknown. Remote backends bypass this host gate
406
416
  Each pressure signal recovers independently after two fresh samples that meet its recovery
407
417
  threshold. A recovered CPU-load signal therefore clears even while RAM remains low.
408
418
 
409
- The default `require_memory` telemetry policy holds local inference when available RAM is missing,
410
- even when a raw request has no model-footprint estimate. `best_effort` is an explicit opt-in;
419
+ On Linux and macOS the default `require_memory` telemetry policy holds local inference when
420
+ available RAM is missing, even when a raw request has no model-footprint estimate. Windows and other
421
+ platforms have no host collector, so they default to `best_effort` and admit with
422
+ `admitted_telemetry_unknown`; elsewhere `best_effort` is an explicit opt-in;
411
423
  `require_all` additionally requires the collector's applicable load, OS-pressure, and thermal signals.
412
424
  Unsupported Linux collector signals remain explicitly unavailable rather than being fabricated.
413
425
  The pure `ResourceSnapshot::assess_capacity` calculation is shared by admission and previews.
@@ -442,11 +454,11 @@ Resident tasks share a backend lock. Nonresident tasks take that backend's exclu
442
454
  load cannot race another managed task on the same server. CPU and GPU backends have separate locks
443
455
  and admission pools, so they can progress independently.
444
456
 
445
- In `serve`, mutating raw proxy requests also take the primary backend's exclusive transition lock,
446
- returning 503 when busy. The response body holds this guard through EOF or disconnect. Metadata
447
- GET/HEAD and `/api/show` requests remain available. Raw inference waits at most 500 ms for host
448
- pressure to clear, then returns 503 with `Retry-After`; empty `keep_alive:0` unload requests bypass
449
- pressure. Raw request-body collection has the configured request deadline too; an incomplete upload
457
+ In `serve`, raw generation requests also take the primary backend's exclusive transition lock,
458
+ returning 503 when it stays busy past the raw queue wait. The response body holds this guard through
459
+ EOF or disconnect. Metadata GET/HEAD, `/api/show`, model-store calls (pull, push, copy, delete, and
460
+ blob uploads), and empty `keep_alive:0` unload requests take no lock and bypass pressure. Raw
461
+ inference waits at most two seconds for host pressure to clear, then returns 503 with `Retry-After`. Raw request-body collection has the configured request deadline too; an incomplete upload
450
462
  returns HTTP 408 and releases its guards without contacting Ollama. Admission receipts report
451
463
  `queue_wait_ms`, `resource_wait_ms`, `transition_wait_ms`, and
452
464
  their sum as `total_wait_ms`. Health and route receipts expose sampled pressure and reservations.
@@ -599,7 +611,7 @@ expected revision. Warming uses ordinary admission and fit checks; omitted resid
599
611
  a finite measured policy. See [Scope history and model warming](SCOPES_AND_WARMING.md) for the
600
612
  public contract and configured limits.
601
613
 
602
- In `serve`, mutating passthrough traffic shares the primary transition lock and receives
614
+ In `serve`, raw generation traffic shares the primary transition lock and receives
603
615
  host-pressure gating, while managed tasks also reserve forecast memory. Direct Ollama callers bypass
604
616
  these controls. FreeLlama cannot preempt inference or constrain other processes. A configured but
605
617
  unreachable CPU backend makes managed catalog discovery fail closed; raw primary passthrough remains
@@ -614,8 +626,9 @@ explicit model that is not installed, or a task that exceeds the advertised cont
614
626
  ## Product boundary
615
627
 
616
628
  FreeLlama is not an inference engine, remote provider marketplace, billing layer, hosted model
617
- registry, general agent runtime, or A2A coordinator. It can authenticate every route with one
618
- operator-managed bearer token and refuses unauthenticated nonloopback listeners. An external layer
629
+ registry, general agent runtime, or A2A coordinator. It can authenticate every route except the
630
+ static UI shell with one operator-managed bearer token and refuses unauthenticated nonloopback
631
+ listeners. An external layer
619
632
  must still provide TLS, tenant authorization, tenant-specific limits, and isolation.
620
633
 
621
634
  Use Ollama directly when a caller only needs raw speed from one exact model. Use FreeLlama when the
package/docs/CLI.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The `freellama` command starts and inspects the local management layer that agents use to offload
4
4
  work. It exposes diagnostics, model routing, managed execution, resource controls, benchmarking,
5
- and Ollama-compatible passthrough. Use the [README CLI section](https://github.com/bgauryy/FreeLlama/blob/main/README.md#or-use-the-cli) for first
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
8
  ## Choose a command
@@ -78,8 +78,9 @@ npx @octocodeai/freellama serve \
78
78
  --cpu-model nomic-embed-text:latest
79
79
  ```
80
80
 
81
- FreeLlama rejects a CPU upstream with no assignments, nonloopback upstreams, and endpoints that are
82
- aliases for the same socket. It also sets `options.num_gpu=0` on CPU-assigned managed requests.
81
+ FreeLlama rejects a CPU upstream with no assignments, `--cpu-model` without `--cpu-upstream`, and a
82
+ CPU upstream that is the same Ollama endpoint as `--upstream`, including loopback aliases on one port.
83
+ It also sets `options.num_gpu=0` on CPU-assigned managed requests.
83
84
  Read [CPU and GPU model routing](CPU_GPU_ROUTING.md) before deploying this layout.
84
85
 
85
86
  After startup, require `contracts.placement_observation` and `placement_evidence_gate`, then inspect
@@ -145,20 +146,27 @@ FreeLlama first unloads idle unpinned runners, then dispatches anyway: the recei
145
146
  load fits. On Linux, Windows, and other platforms, where available RAM already counts reclaimable
146
147
  cache (and is the hard cap in a container), the same shortfall unloads idle runners and then holds the
147
148
  new load with `held_insufficient_capacity` until capacity returns, refusing with 503 at its deadline.
148
- OS memory pressure holds work on every platform. Active swapping and low RAM hold new loads only:
149
- an already-resident runner adds no memory and stays admitted. CPU-load (one-minute load at or above
150
- 1.5 per logical CPU) and thermal-throttling holds apply to CPU-assigned work and to cold loads, which
151
- decode weights on the host cores and add a new heat source; a bounded request on a runner already
152
- resident on the GPU neither waits for the cores nor cools the package by waiting, so it proceeds. The receipt's `resource_admission.assessment.held_by`
149
+ OS memory pressure holds all work where the OS reports it (macOS, and Linux when the kernel
150
+ exposes `/proc/pressure/memory`; Windows has no host collector yet). Active swapping and low RAM hold new loads only:
151
+ an already-resident runner adds no memory and stays admitted. The CPU-load hold (one-minute load at
152
+ or above 1.5 per logical CPU) applies only to CPU-assigned work, which decodes on those cores; GPU
153
+ work, loaded or not, proceeds, so another program's build never blocks a GPU model. On a host where
154
+ the sampler sees no accelerator (no GPU device or GPU memory), every model runs on the CPU, so the
155
+ hold applies to all work there. The
156
+ thermal-throttling hold applies to CPU-assigned work and to cold loads, a new heat source; a bounded
157
+ request on a runner already resident on the GPU proceeds. The receipt's `resource_admission.assessment.held_by`
153
158
  lists the holds that applied to the request.
154
159
  Both `serve` and `proxy` accept `--resource-telemetry-policy`:
155
160
 
156
- - `require-memory` (default): wait or refuse local inference when available RAM is unknown, including
157
- raw requests with zero forecast bytes. Missing thermal data alone does not block this policy.
158
- - `require-all`: require all signals applicable to the collector. The built-in Linux collector does
159
- not provide OS pressure or thermal signals and marks those as unsupported; custom collectors must
160
- provide them. Unsupported signals are not evidence of healthy hardware.
161
- - `best-effort`: explicitly permit unknown telemetry unless an observed pressure hold is active.
161
+ - `require-memory` (default on Linux and macOS): wait or refuse local inference when available RAM is
162
+ unknown, including raw requests with zero forecast bytes. Missing thermal data alone does not block
163
+ this policy.
164
+ - `require-all`: require all signals applicable to the collector. The built-in Linux collector reads
165
+ OS memory pressure from `/proc/pressure/memory` when the kernel provides it, but has no thermal
166
+ signal and marks it as unsupported; custom collectors must provide it. Unsupported signals are not
167
+ evidence of healthy hardware.
168
+ - `best-effort`: explicitly permit unknown telemetry unless an observed pressure hold is active. This
169
+ is the default on Windows, which has no built-in memory collector.
162
170
 
163
171
  Remote upstreams bypass local host policy. Raw metadata and empty unload requests remain available
164
172
  during a hold. Route receipts separate `queue_readiness` from `resource_readiness`, and include
@@ -320,8 +328,6 @@ The MCP server autostarts `serve` with its own environment and an arbitrary work
320
328
  put a machine-wide policy in the config directory or set the variables in the MCP client's `env`.
321
329
  `serve` prints each file it picked up.
322
330
 
323
- Replace `MODEL` with the directory containing your completed quality-evaluation aggregate.
324
-
325
331
  `policy from-eval` reads `pass_at_1`, not the throughput produced by `bench-all`. It refuses
326
332
  expired aggregates and fewer than three trials unless `--allow-smoke` is explicit, and it skips
327
333
  models that are not installed on `--ollama-endpoint` (default `FREELLAMA_OLLAMA_ENDPOINT`, then
@@ -56,7 +56,7 @@ flowchart TB
56
56
  CP --> CPU["CPU runner"]
57
57
  ```
58
58
 
59
- FreeLlama does not inspect or rewrite raw passthrough request bodies. A direct Ollama client that
59
+ FreeLlama does not rewrite raw passthrough request bodies or route them by model. A direct Ollama client that
60
60
  needs model-aware CPU placement must use the managed task endpoint instead.
61
61
 
62
62
  ## Start the backends
@@ -117,7 +117,7 @@ unsafe or ambiguous configurations:
117
117
  - A CPU model assignment without `--cpu-upstream`
118
118
  - Empty model names
119
119
  - The same socket expressed through aliases such as `localhost:11434` and `127.0.0.1:11434`
120
- - A nonloopback upstream
120
+ - An upstream that points back to the FreeLlama listener, or an unspecified host such as `0.0.0.0`
121
121
 
122
122
  A nonloopback FreeLlama listener requires `--allow-remote` and bearer-token authentication.
123
123
  See the [production runbook](PRODUCTION.md) for remote ingress requirements.
@@ -216,7 +216,7 @@ curl --silent http://127.0.0.1:11436/api/ps | jq '.models[] | {name, size, size_
216
216
  A fully CPU-loaded model reports `size_vram:0`; a fully GPU-loaded model reports positive VRAM
217
217
  covering its resident `size`. Compare both values from the same `/api/ps` entry. Installed `/api/tags`
218
218
  file bytes can differ from resident memory and cannot prove a mixed placement. Positive VRAM without
219
- total resident size stays unknown. `observation.status` is `verified`, `mismatch`, or unavailable. Unknown, mixed, and mismatched
219
+ total resident size stays unknown. `observation.status` is `verified`, `mismatch`, `not_resident`, or `unavailable`. Unknown, mixed, and mismatched
220
220
  samples are reported but excluded from adaptive feedback.
221
221
 
222
222
  ## Understand concurrency
@@ -252,17 +252,18 @@ FreeLlama can overlap requests across the two servers even when each Ollama proc
252
252
  `OLLAMA_NUM_PARALLEL=1`. Raising `OLLAMA_NUM_PARALLEL` affects parallel requests within one server
253
253
  and multiplies K/V-cache memory; it is a separate tuning decision. `OLLAMA_MAX_QUEUE` likewise
254
254
  bounds each Ollama process after FreeLlama admission. It does not replace either backend's weighted
255
- FreeLlama budget, bounded waiter count, or queue-wait deadline. Raw passthrough uses a separate
256
- one-stream default cap in `serve`; its permit is held until the streamed response ends or is dropped.
255
+ FreeLlama budget, bounded waiter count, or queue-wait deadline. Raw generation passthrough uses a
256
+ separate one-stream default cap in `serve`; its permit is held until the streamed response ends or is dropped.
257
257
 
258
258
  The runtime file's `[task_costs]` table can tune individual task-kind weights without increasing
259
259
  the entire pool's ceiling. Default vision weight four can serialize two vision requests under a
260
260
  four-unit budget even when Ollama has two slots. A lower weight requires a qualified workload;
261
261
  it does not add engine slots or relax memory checks. See
262
262
  [monitoring controls](MONITORING.md#adaptive-concurrency).
263
- Mutating raw requests also require exclusive access to the primary backend's managed transition
264
- lock and receive 503 while it is busy. Metadata GET/HEAD calls remain available. This prevents raw
265
- inference or model lifecycle calls from overlapping a managed generation on that backend.
263
+ Raw generation requests also require exclusive access to the primary backend's managed transition
264
+ lock and receive 503 if it stays busy past the raw queue wait. Metadata GET/HEAD, `/api/show`,
265
+ model-store calls (pull, push, copy, delete), and empty `keep_alive:0` unloads remain available.
266
+ This prevents raw inference from overlapping a managed generation on that backend.
266
267
 
267
268
  ## Understand automatic feedback
268
269
 
@@ -382,9 +383,8 @@ A configured but unreachable CPU backend causes model discovery to fail closed.
382
383
  can also be unavailable until the secondary server returns. Raw passthrough to the primary server
383
384
  continues to work because it does not depend on catalog discovery.
384
385
 
385
- For the surrounding runtime settings, see
386
- [Ollama and FreeLlama optimization](dev/OLLAMA_SYSTEM_OPTIMIZATION.md). For all `serve` flags, see the
387
- [CLI reference](CLI.md).
386
+ For the surrounding Ollama settings, see [Production](PRODUCTION.md#configure-ollama-explicitly). For
387
+ all `serve` flags, see the [CLI reference](CLI.md).
388
388
 
389
389
  ## Upstream sources
390
390
 
@@ -4,8 +4,9 @@ To offload a task, select an installed model with the required capabilities and
4
4
  then check its memory fit under the intended concurrency. FreeLlama applies model eligibility
5
5
  and admission; the calling agent still verifies the output.
6
6
 
7
- The measurements in this guide come from one Apple M4 Pro machine with 48 GB unified memory and
8
- are not compiled into routing. Re-run
7
+ The measurements in this guide come from single machines and are not compiled into routing:
8
+ [Recommended starting points](#recommended-starting-points-by-form) from an Apple M2 Pro with 32 GB,
9
+ the sections after it from an Apple M4 Pro with 48 GB unified memory. Re-run
9
10
  `npx @octocodeai/freellama doctor`, `models`, `bench-all`, and your quality suite on the target hardware.
10
11
  `doctor.machine.memory_bytes` is host RAM; a discrete GPU's usable VRAM must be observed separately.
11
12
 
@@ -26,14 +27,55 @@ flowchart TD
26
27
  M -->|"Yes"| B["Benchmark locally and add policy evidence"]
27
28
  ```
28
29
 
30
+ ## Recommended starting points by form
31
+
32
+ Measured on an Apple M2 Pro with 32 GB, Ollama 0.40.8 and FreeLlama 0.6.1 on 2026-10-30, while
33
+ other builds kept the one-minute load near 14 on 12 cores. Every check had a known answer; the
34
+ batteries are small, so treat these as first picks to re-check on your machine, not rankings.
35
+
36
+ | Form | Model (download) | Measured | Speed (warm) | How an agent calls it |
37
+ |---|---|---|---|---|
38
+ | Text, tools, JSON, reasoning | `gpt-oss:20b` (13.8 GB) | 7/7: exact output, arithmetic, bat-and-ball, code reading, JSON extraction, tool call, 9/9 ticket routing | 1.4 s per routed ticket | `run_task {model:"gpt-oss:20b", prompt, think:"low"}`; add `tools` or `format:"json"` |
39
+ | Embeddings | `nomic-embed-text:v1.5` (274 MB) | Retrieval 4/4 top-1; ticket routing 9/9 from two examples per team | 51 ms per item, batched | `run_task {task:"embedding", input:[...], returnEmbeddings:true}` |
40
+ | Decisions (classify, route, score) | `strands-decider:2b-nvfp4` (1.3 GB) | Ticket routing 9/9, return-policy yes/no 3/3, damage rubric 2/3 | 0.17 s per question | `POST <serve>/v1/systemone {model, state, questions}`: passthrough, no MCP tool yet |
41
+ | OCR | `glm-ocr:latest` (2.2 GB) | 4/4 phrases from a dense report strip; 0/7 on text tasks | 11 s per strip at a 400-token cap | `run_task {task:"vision", model:"glm-ocr", prompt:"Text Recognition:", images:[...], stopOnRepetition:true}` |
42
+
43
+ `gpt-oss:20b` reasons before answering; the battery ran it with `think:"low"` and a 2,048-token
44
+ output cap, so leave room for the reasoning when you set `num_predict`. `glm-ocr` reads text from images only and keeps generating
45
+ after the visible text ends, so cap its output or set `stopOnRepetition`. The decision model needs
46
+ Apple silicon and answers only through Ollama's decision API; reach it through `serve` so the call
47
+ still passes FreeLlama's memory checks and stream cap (from 0.6.2 a busy CPU no longer refuses it).
48
+
49
+ ### Classify text three ways
50
+
51
+ The same nine support tickets, routed to billing, technical or returns:
52
+
53
+ | Approach | Model | Correct | Per item | Use when |
54
+ |---|---|---:|---:|---|
55
+ | Embeddings and per-class centroids | `nomic-embed-text:v1.5` | 9/9 | 51 ms | Many items, stable classes, a few labeled examples per class |
56
+ | Decision model | `strands-decider:2b-nvfp4` | 9/9 | 0.5 s (first call includes the load) | You need probabilities, yes/no policy checks or rubric scores |
57
+ | Instruction model with JSON output | `gpt-oss:20b` | 9/9 | 1.4 s | Classes change per request or need explanation |
58
+
59
+ A decision API request names its questions; each answer comes back under the same name:
60
+
61
+ ```bash
62
+ curl -s http://127.0.0.1:11435/v1/systemone -H 'content-type: application/json' -d '{
63
+ "model": "strands-decider:2b-nvfp4",
64
+ "state": "Why did my credit card get billed two times?",
65
+ "questions": {"team": {"type": "choice", "instructions": "Which team handles this ticket?",
66
+ "criteria": {"billing": "Charges and invoices", "technical": "App errors", "returns": "Returns and refunds"}}}
67
+ }'
68
+ ```
69
+
29
70
  ## Text, code, and vision
30
71
 
31
72
  `qwen3.8:27b-mlx` was the strongest general model in the local evaluations. It handled coding,
32
73
  grounded research, image description, OCR, and summarization. Using one large multimodal model also
33
74
  avoided contention between separate heavy text and vision runners.
34
75
 
35
- Read the [retained model profile](https://github.com/bgauryy/FreeLlama/blob/main/skills/freellama/references/model-profile-qwen3.8-27b-mlx.md)
36
- and [delegation measurements](https://github.com/bgauryy/FreeLlama/blob/main/skills/freellama/references/task-delegation.md) for scope and limits.
76
+ In those evaluations it was strong at grounded lookup, long-context retrieval and strict instruction
77
+ following, needed `think:true` and a generous output budget for multi-step reasoning, and about a
78
+ third of its unsupervised code-review findings were false or context-limited.
37
79
  These examples do not establish that a tag is installed or suitable on your host.
38
80
 
39
81
  Dedicated OCR models answer best to the prompts their model card documents; send them with base64
@@ -118,8 +118,9 @@ The ledger lives next to the feedback file in the platform data directory. Choos
118
118
  `/metrics` exposes counters (`freellama_tasks_total`, `freellama_prompt_tokens_total`,
119
119
  `freellama_output_tokens_total`, `freellama_task_seconds_total`, `freellama_queue_wait_seconds_total`,
120
120
  `freellama_load_seconds_total`, `freellama_raw_requests_total`, `freellama_evictions_total`,
121
- `freellama_circuit_open_total`, `freellama_concurrency_limit_changes_total`) labelled by model,
122
- backend, task, and outcome, plus gauges for admission, queues, the raw proxy, host and GPU memory,
121
+ `freellama_circuit_open_total`, `freellama_concurrency_limit_changes_total`); task counters are
122
+ labelled by model, backend, task, and outcome, raw requests by kind and outcome, and the rest by
123
+ backend. Gauges cover admission, queues, the raw proxy, host and GPU memory,
123
124
  loaded models, and `freellama_ollama_num_parallel`. Point Prometheus, Grafana Agent, or any
124
125
  OpenMetrics-compatible scraper at it.
125
126
 
@@ -132,7 +133,7 @@ FreeLlama waits for safe capacity within a deadline and bounds the retained back
132
133
  | Managed queue full | `429` | `admission_queue_full` |
133
134
  | Raw proxy over its cap after `raw_queue_wait_seconds` | `429` | |
134
135
  | Managed wait exceeded `max_queue_wait_seconds` (or the task's `max_wait_seconds`) | `503` | `admission_timeout` |
135
- | OS pressure, swapping, CPU, thermal, or required telemetry still holds when the waiting budget expires (low available RAM alone only warns; low RAM and swapping never hold an already-resident runner) | `503` | `resource_admission_unavailable` |
136
+ | OS pressure, swapping, CPU, thermal, or required telemetry still holds when the waiting budget expires (low available RAM alone only warns on macOS; low RAM and swapping never hold an already-resident runner, and CPU load and thermal never hold one on the GPU) | `503` | `resource_admission_unavailable` |
136
137
  | Backend circuit open | `503` | `upstream_circuit_open` |
137
138
 
138
139
  These capacity refusals carry a `Retry-After` header and `retry_after_seconds` in the body. For a queue it
@@ -267,9 +268,10 @@ out-of-memory shrink-and-retry only applies when the context was not requested e
267
268
 
268
269
  With `context_mode = "ollama_default"` (the default), FreeLlama sizes the context as before but
269
270
  omits `num_ctx` when Ollama's own default for the model is known, covers the request, and is at most
270
- `auto_context_max` (32768). The default is resolved in Ollama's order: the Modelfile's `num_ctx`,
271
- then `OLLAMA_CONTEXT_LENGTH`, then (discrete GPU only) Ollama's VRAM tier — 4096 below 24 GiB, 32768
272
- below 48 GiB, 262144 above. Receipts show `context_sizing.num_ctx_sent` and the default used.
271
+ `auto_context_max` (32768). The default is the Modelfile's `num_ctx`, then the
272
+ runtime file's `ollama_default_context`, then `OLLAMA_CONTEXT_LENGTH`, then (primary backend
273
+ on a discrete GPU only) Ollama's VRAM tier, from total VRAM minus `OLLAMA_GPU_OVERHEAD` — 4096 below
274
+ 23 GiB, 32768 below 47 GiB, 262144 from 47 GiB. Receipts show `context_sizing.num_ctx_sent` and the default used.
273
275
  Memory estimates always use the context Ollama can allocate. Use `"explicit"` to always
274
276
  send `num_ctx`.
275
277
 
@@ -34,17 +34,18 @@ published tag:
34
34
  powershell -ExecutionPolicy Bypass -File .\scripts\install.ps1 -Version VERSION
35
35
  ```
36
36
 
37
- The repository has no CI or release workflows; they were removed (see git history). Build, verify,
38
- and publish artifacts with the [release procedure](../RELEASE.md). The publish verifier checks that
39
- a version tag matches the workspace version.
37
+ The repository has no CI or release workflows; they were removed (see git history). Release from a
38
+ clean checkout: `yarn release:version <version>`, `yarn release:build` (builds each target's
39
+ executable and addon into `release-artifacts/<target>/`), `node scripts/assemble-release.mjs
40
+ release-artifacts release`, then `yarn release:verify:publish`. The publish verifier checks that
41
+ `FREELLAMA_RELEASE_TAG`, when set, matches the workspace version.
40
42
  Live E2E and hardware qualification remain separate operator checks; verify those receipts before tagging.
41
43
 
42
- For manual assembly, build and test every claimed target in a clean
43
- environment, place each executable and matching addon under `release-artifacts/<target>/`, run
44
- `node scripts/assemble-release.mjs release-artifacts release` and `yarn release:verify:publish`, then attach
45
- `release/SHA256SUMS` and binaries to the release. Publish the eight `@octocodeai/freellama-native-*` packages
46
- first, followed by the exact-version `@octocodeai/freellama` and `@octocodeai/freellama-mcp-server` packages. Configure
47
- registry credentials in the release environment; never store them in the repository.
44
+ Attach `release/SHA256SUMS` and binaries to the release. Publish the eight `@octocodeai/freellama-native-*` packages
45
+ first, followed by the exact-version `@octocodeai/freellama` and `@octocodeai/freellama-mcp-server` packages. Publish
46
+ the Rust crates from the same commit with `cargo publish --workspace` (it uploads `freellama-core`
47
+ before `freellama-cli`; run it with `--dry-run` first). Configure registry credentials (`npm login`,
48
+ `cargo login`) in the release environment; never store them in the repository.
48
49
 
49
50
  ## Configure Ollama explicitly
50
51
 
@@ -82,7 +83,7 @@ FreeLlama and Ollama have separate queues. FreeLlama caps retained managed waite
82
83
  `--max-queued-tasks`/`--cpu-max-queued-tasks`, then waits for at most
83
84
  `--max-queue-wait-seconds`. An admitted request can then enter Ollama's internal queue,
84
85
  which is bounded by `OLLAMA_MAX_QUEUE`. Raw compatibility traffic bypasses weighted managed
85
- admission and enters the primary Ollama queue through a separate one-stream default cap in `serve`.
86
+ admission and, for generation, enters the primary Ollama queue through a separate one-stream default cap in `serve`.
86
87
  Set all limits from observed latency and overload behavior; changing one does not configure the
87
88
  others.
88
89
 
@@ -91,7 +92,7 @@ The following flow shows where each production setting applies:
91
92
  ```mermaid
92
93
  flowchart LR
93
94
  C["Client"] --> FQ["FreeLlama admission<br/>weighted budget + wait deadline"]
94
- FQ -->|"refused"| E503["503 server busy"]
95
+ FQ -->|"refused"| E503["429 or 503 server busy"]
95
96
  FQ -->|"admitted"| OQ["Ollama queue<br/>OLLAMA_MAX_QUEUE"]
96
97
  RAW["Raw /api/* or /v1/*"] --> RC["Raw stream cap<br/>default 1 in serve"]
97
98
  RC -->|"refused"| E503
@@ -102,14 +103,24 @@ flowchart LR
102
103
 
103
104
  These values are conservative starting points, not universal constants. NVIDIA, AMD, Apple, and
104
105
  CPU-only hosts must pass hardware validation before promotion. See
105
- [Ollama and FreeLlama optimization](dev/OLLAMA_SYSTEM_OPTIMIZATION.md) for the ownership boundary and
106
106
  [Run models on CPU and GPU](CPU_GPU_ROUTING.md) for device-specific controls.
107
107
 
108
+ Ollama already picks the accelerator, fits the model to device memory, splits layers between CPU
109
+ and GPU, and schedules requests; FreeLlama decides which tasks reach it. These changes do not help:
110
+
111
+ - Forcing one model's layers between CPU and GPU: partial CPU offload usually lowers decode speed.
112
+ Run a separate CPU-only Ollama process for a second model instead.
113
+ - Running Ollama in Docker on macOS: Docker Desktop gives it no GPU acceleration there.
114
+ - Raising context globally: K/V memory grows with context; size `num_ctx` per request.
115
+ - Pinning several large models in one process without the memory arithmetic: weights, runtime
116
+ buffers and every active context must fit together.
117
+ - `q4_0` K/V cache as a default: it trades more quality than `q8_0` for the memory it saves.
118
+
108
119
  Put the values in the service manager that owns each Ollama process: launchd on macOS, systemd or
109
120
  the container definition on Linux, and the Windows service wrapper. Shell exports and
110
121
  `launchctl setenv` are useful diagnostics but are login-session state, not a reboot-persistent
111
- production configuration. After a real service restart, rerun `doctor` and `skills/freellama/scripts/check.sh` and
112
- require the same visible values before admitting work.
122
+ production configuration. After a real service restart, rerun `freellama doctor` and `freellama status`
123
+ and require the same visible values before admitting work.
113
124
 
114
125
  ## Create the security and state files
115
126
 
@@ -170,7 +181,8 @@ only model identifiers, scale, and sample count; they contain no prompts.
170
181
  Keep adapter context settings distinct from Ollama server context. `FREELLAMA_AGENT_NUM_CTX`
171
182
  budgets the adapter conversation; older tool observations are compacted inside that budget while
172
183
  the system prompt and original question remain pinned by default. `OLLAMA_CONTEXT_LENGTH` controls
173
- direct Ollama requests, and managed FreeLlama tasks send their own `num_ctx`.
184
+ direct Ollama requests; managed FreeLlama tasks size their own context and send `num_ctx` unless
185
+ Ollama's known default covers it.
174
186
 
175
187
  ## Verify lifecycle and placement
176
188
 
@@ -187,10 +199,10 @@ Require these health contracts before admitting traffic:
187
199
  - `placement_observation: ollama_api_ps_after_execution`
188
200
  - `placement_evidence_gate: configured_or_observed`
189
201
 
190
- Run `skills/freellama/scripts/check.sh` with `FREELLAMA_AUTH_TOKEN_FILE` set. It verifies the
191
- active versions, visible Ollama settings, persisted-feedback contract, authentication contract,
192
- backend topology, and binary freshness. Also inspect the service definition for any setting that
193
- the Ollama process does not expose to FreeLlama.
202
+ With `FREELLAMA_AUTH_TOKEN_FILE` set, run `freellama doctor` (Ollama and CLI versions, visible Ollama
203
+ settings, machine) and read `/_freellama/v1/health` (FreeLlama version, `security`, `feedback.persistence`,
204
+ `backends`) and `freellama status` (backend topology, loaded models, admission). Also inspect the
205
+ service definition for any setting that the Ollama process does not expose to FreeLlama.
194
206
 
195
207
  ## Validate every hardware class
196
208
 
package/docs/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  FreeLlama helps agents offload bounded work to local models through Ollama. The calling agent owns
4
4
  the task and verifies the answer; FreeLlama manages model selection and resources around execution.
5
- Start with the [repository MCP setup](https://github.com/bgauryy/FreeLlama/blob/main/README.md#connect-your-agent-mcp) to run one managed task.
5
+ Start with the [repository MCP setup](https://github.com/bgauryy/FreeLlama/blob/main/README.md#mcp-server) to run one managed task.
6
6
 
7
7
  ## Choose a guide
8
8
 
@@ -19,7 +19,7 @@ Start with the [repository MCP setup](https://github.com/bgauryy/FreeLlama/blob/
19
19
  | Understand raw Ollama compatibility | [Ollama sidecar](ARCHITECTURE.md#ollama-sidecar) |
20
20
  | Evaluate context offload and its cost | [Token economics](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md#token-economics) |
21
21
  | Deploy the management layer | [Production runbook](PRODUCTION.md) |
22
- | Run development and release checks | [Testing](TESTING.md), [release procedure](https://github.com/bgauryy/FreeLlama/blob/main/RELEASE.md) |
22
+ | Run development and release checks | [Testing](TESTING.md), [release packages](TESTING.md#verify-release-packages) |
23
23
  | Embed the routing core | [Rust core](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/README.md) |
24
24
  | Use the read-only status page | `/_freellama/ui` on a running `serve` (see [Monitoring](MONITORING.md)) |
25
25
 
@@ -27,10 +27,6 @@ Start with the [repository MCP setup](https://github.com/bgauryy/FreeLlama/blob/
27
27
 
28
28
  | Goal | Source |
29
29
  |---|---|
30
- | Review dated findings and project comparisons | [Findings report](dev/FINDINGS_AND_POSITIONING.md) |
31
- | Measure inference, device activity, and thermal behavior | [Local performance testing](dev/LOCAL_PERFORMANCE_TESTING.md) |
32
- | Separate management controls from inference tuning | [Ollama optimization boundary](dev/OLLAMA_SYSTEM_OPTIMIZATION.md) |
33
- | Understand backend selection decisions | [Resource-routing ADR](dev/ADR_RESOURCE_AWARE_BACKEND_ROUTING.md) |
34
30
 
35
31
  Historical measurements describe one model, machine, and workload. Inspect current state and
36
32
  repeat the relevant checks before using them to select models or set resource limits.
package/docs/TESTING.md CHANGED
@@ -24,8 +24,8 @@ yarn install
24
24
  yarn build
25
25
  ```
26
26
 
27
- The root build compiles the host Rust release binary, NAPI addon, and single-file MCP JavaScript
28
- bundle. The native `.node` addon remains external to the JavaScript bundle because it is selected
27
+ The root build compiles the host Rust release binary, NAPI addon, and MCP JavaScript bundles
28
+ (server and research adapters). The native `.node` addon remains external to the JavaScript bundle because it is selected
29
29
  by platform triple at runtime. Published packages resolve it from an OS/CPU/libc-specific optional
30
30
  dependency rather than compiling Rust during installation.
31
31
 
@@ -40,7 +40,6 @@ Yarn. Use the `:js` or `:rust` variants to run one side only.
40
40
  | All package unit tests (JS + Rust) | `yarn test` | Rust toolchain |
41
41
  | JavaScript unit | `yarn test:js` | None |
42
42
  | Rust contracts | `yarn test:rust` | Rust toolchain |
43
- | Local-agent contracts | `yarn test:agents` | None after install; builds the MCP package |
44
43
  | TypeScript | `yarn typecheck` | None after install |
45
44
  | MCP integration | `yarn test:integration` | Ollama at `FREELLAMA_OLLAMA_ENDPOINT` (default `127.0.0.1:11434`) |
46
45
  | MCP end to end | `yarn test:e2e` | Ollama, `freellama serve`, and required models |
@@ -137,11 +136,11 @@ Usage-ledger unit contracts cover ordered writes, bounded queue overflow, failed
137
136
  validation, replay, and flush behavior. `monitoring_contract` also checks that a blocked ledger path
138
137
  does not hold task completion. Persistence remains best effort, not a zero-loss promise.
139
138
 
140
- MCP `serve.test.ts` checks startup permission failure, retry, and child-lifecycle isolation. The
139
+ MCP `serve.test.ts` checks startup permission failure, retry, child-lifecycle isolation, and the
140
+ background start at launch. The
141
141
  protocol integration contracts verify that spawn failure leaves the MCP connection usable and that
142
142
  fixture-backed library requests fail on unexpected product errors. External catalog availability
143
- is separate from these deterministic checks. Runtime-view `app-windows.test.ts` checks legitimate
144
- Windows static paths alongside traversal refusal.
143
+ is separate from these deterministic checks.
145
144
 
146
145
  Launcher fixtures use `process.execPath` to avoid an extra PATH-resolved shell wrapper. After the
147
146
  MCP fixture's real executable-permission refusal, a test-only transport launches that exact fixture
@@ -211,7 +210,8 @@ yarn release:verify:publish
211
210
  Build every claimed CLI/native target in a clean environment, assemble `SHA256SUMS`, run the full
212
211
  deterministic suite, and inspect every platform package dry run before publishing explicitly.
213
212
  Publish the eight `@octocodeai/freellama-native-*` packages before `@octocodeai/freellama` and `@octocodeai/freellama-mcp-server`, all
214
- at the exact same version. A successful local pack dry run proves package contents; it does not
213
+ at the exact same version, then the crates with `cargo publish --workspace` (`--dry-run` first packages
214
+ and compiles both crates from their published sources). A successful local pack dry run proves package contents; it does not
215
215
  prove registry publication or another hardware class.
216
216
 
217
217
  ## Test one layer while iterating