@octocodeai/freellama-mcp-server 0.7.0 → 0.10.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 (45) hide show
  1. package/CONFIGURATION.md +11 -3
  2. package/DEVELOPING.md +6 -6
  3. package/README.md +267 -403
  4. package/dist/index.js +7186 -1601
  5. package/dist/research/bash-agent.js +1504 -71
  6. package/dist/research/coder-agent.js +4677 -0
  7. package/dist/research/github-agent.js +2932 -0
  8. package/dist/research/octocode-agent.js +1416 -116
  9. package/docs/ARCHITECTURE.md +101 -45
  10. package/docs/BUILD_AND_NOTES.md +31 -0
  11. package/docs/CLI.md +76 -27
  12. package/docs/COMPARISON.md +40 -0
  13. package/docs/CPU_GPU_ROUTING.md +127 -25
  14. package/docs/DEFERRED_JOBS.md +79 -0
  15. package/docs/FEATURES.md +303 -0
  16. package/docs/LOCAL_MODELS.md +190 -0
  17. package/docs/MODEL_FORMS_AND_RESULTS.md +55 -0
  18. package/docs/MODEL_INTROSPECTION.md +128 -0
  19. package/docs/MODEL_SELECTION.md +75 -23
  20. package/docs/MONITORING.md +115 -33
  21. package/docs/PRODUCTION.md +74 -11
  22. package/docs/README.md +11 -6
  23. package/docs/SCOPES_AND_WARMING.md +55 -7
  24. package/docs/TESTING.md +24 -2
  25. package/docs/TUNING.md +86 -0
  26. package/docs/cookbooks/README.md +22 -0
  27. package/docs/cookbooks/batch-text.md +19 -0
  28. package/docs/cookbooks/classify.md +25 -0
  29. package/docs/cookbooks/coding.md +17 -0
  30. package/docs/cookbooks/duplicates.md +17 -0
  31. package/docs/cookbooks/find-code.md +21 -0
  32. package/docs/cookbooks/image-recognition.md +15 -0
  33. package/docs/cookbooks/image-search.md +13 -0
  34. package/docs/cookbooks/jobs.md +60 -0
  35. package/docs/cookbooks/memory-and-caching.md +31 -0
  36. package/docs/cookbooks/ocr.md +17 -0
  37. package/docs/cookbooks/research-agent.md +27 -0
  38. package/docs/cookbooks/speech-to-text.md +19 -0
  39. package/docs/cookbooks/sweep.md +66 -0
  40. package/docs/cookbooks/text-to-speech.md +15 -0
  41. package/docs/octocode/GITHUB.md +30 -0
  42. package/docs/octocode/LOCAL.md +29 -0
  43. package/native/index.d.ts +0 -11
  44. package/native/index.js +18 -34
  45. package/package.json +6 -9
@@ -31,8 +31,10 @@ token and keeps it only for the browser tab.
31
31
  `status` answers "why is my task waiting?" in one call:
32
32
 
33
33
  - **`backends.gpu` / `backends.cpu`**: admission (units in use, current limit, ceiling, queue depth,
34
- oldest wait, refusal and timeout counters), the adaptive limiter state, and the circuit breaker
35
- (`closed`, `open` with seconds remaining, or `half_open`).
34
+ oldest wait, refusal and timeout counters, and `affinity_promotions`: grants given ahead of older
35
+ same-priority work because the waiter's model was already running or admitted last), the
36
+ adaptive limiter state, and the circuit breaker (`closed`, `open` with seconds remaining, or
37
+ `half_open`).
36
38
  - **`raw_proxy`**: active and waiting raw streams against their cap and wait budget.
37
39
  - **`loaded_models`**: Ollama's `/api/ps` with total and VRAM bytes, context length, expiry, and
38
40
  whether the model is pinned.
@@ -50,6 +52,16 @@ token and keeps it only for the browser tab.
50
52
  deadlines, states, and waiting reasons. States cover discovery, admission, resources, runner
51
53
  transitions, loading, execution, and completion. Metadata excludes the original payload.
52
54
 
55
+ A task receipt explains its own wait. `admission.mode` is `resident_shared` (the runner was
56
+ reused without the backend load lock) or `nonresident_transition_exclusive` (this task loaded the
57
+ runner under it). `admission.reason` says why a present runner could not be shared when its identity
58
+ was not established, for example `resident_identity_unobserved` when `/api/ps` reports no digest;
59
+ it is `null` for a plain match. `queue_wait_ms`, `transition_wait_ms` (waiting for the load lock) and
60
+ `resource_wait_ms` add up to `total_wait_ms`. Waiting for the lock is bounded by the task's queue
61
+ deadline; a raw load that waits too long gets 503 `proxy busy`, and a raw generation whose
62
+ allocation cannot be forecast gets 503 `model_forecast_unavailable`. See
63
+ [Load lock and use lease](CPU_GPU_ROUTING.md#load-lock-and-use-lease).
64
+
53
65
  Use `run_task {defer:true}` in MCP or `task --defer` in the CLI to get a job handle. A list or
54
66
  status request omits results; fetching one ID returns its retained result or structured failure.
55
67
  Terminal states are `completed`, `failed`, `cancelled`, and `expired`. Cancellation is per task,
@@ -64,7 +76,10 @@ cancellation closes the upstream request; physical computation may continue on a
64
76
  `ollama.config` describes the primary process; `ollama.cpu_config` describes the CPU process when
65
77
  configured. Process attribution matches `OLLAMA_HOST` to the selected endpoint, so separate
66
78
  parallelism and KV cache settings size each backend independently. Fallback sources remain visible
67
- when process settings cannot be observed.
79
+ when process settings cannot be observed. Each config's `effective` block reports the resolved
80
+ `num_parallel`, `context_length`, `flash_attention` (`on`, `off`, or `auto` when unset) and the KV
81
+ cache it sizes for (`kv_cache_type`, `kv_cache_percent_of_f16`); see
82
+ [KV cache and flash attention](TUNING.md#kv-cache-and-flash-attention).
68
83
 
69
84
  Process settings are cached for five seconds and refreshed on discovery or monitoring requests,
70
85
  including after an Ollama restart. Each config receipt includes `observed_at` and
@@ -90,12 +105,28 @@ Each backend's admission `queue_depth` includes both `slot_waiters` and `resourc
90
105
  A resource waiter returns its execution slot after two seconds but remains in the bounded queue
91
106
  until capacity recovers, its deadline expires, or it is cancelled.
92
107
 
108
+ Model affinity is bounded: `affinity_max_skips` (4) and `affinity_max_wait_ms` (2000) in the same
109
+ receipt are how often, and how long, an older waiter can be passed over before its class is served
110
+ in arrival order again. A rising `affinity_promotions` with flat queue waits means fewer model
111
+ switches; see [the admission design](ARCHITECTURE.md#managed-task-execution).
112
+
113
+ `GET /health` reports `admission.request_body_budget` (`total_bytes`, `available_bytes`): the
114
+ process-wide memory for request bodies being read or held by running requests,
115
+ `FREELLAMA_MAX_BUFFERED_BYTES` (256 MiB by default). When it is full, a new upload waits up to the
116
+ queue wait and is then refused with 503 `request_body_budget_exhausted`; raise it, or lower
117
+ `FREELLAMA_MAX_REQUEST_BYTES`, when large uploads are normal.
118
+
93
119
  ## Record usage
94
120
 
95
- Every managed task updates in-memory usage totals and offers one record to a bounded writer queue.
96
- The record contains time, model, backend, task kind, priority, outcome, HTTP status, prompt and
97
- output tokens, duration, queue wait, load time, and output tokens per second. It contains no prompt
98
- or response text. One worker writes records in order; task completion does not wait for disk.
121
+ Every managed task, and every raw Ollama or OpenAI-compatible generation, updates in-memory usage
122
+ totals and offers one record to a bounded writer queue. The record contains time, model, backend,
123
+ task kind (`raw` for passthrough, whose `priority` field holds the path), priority, outcome, HTTP
124
+ status, prompt, cached prompt and output tokens, duration, queue wait, load time, output tokens per
125
+ second and, with client keys, the client. It contains no prompt or response text. Raw counts are read
126
+ from the fields each response reports (`prompt_eval_count`, `prompt_eval_cached_count`, `eval_count`,
127
+ OpenAI `usage`, decision `usage`) as it streams through unchanged; a client that leaves early records
128
+ what arrived. `cached_prompt_tokens` totals show how much prompt work the runners' prompt caches
129
+ saved. One worker writes records in order; task completion does not wait for disk.
99
130
 
100
131
  The writer queue defaults to 128 records. Configure its positive startup-only capacity through
101
132
  `usage_queue_capacity` or `FREELLAMA_USAGE_QUEUE_CAPACITY`; runtime reload does not resize the worker.
@@ -105,13 +136,16 @@ failure records a failed write. Inspect `usage_ledger` in `status`, or `ledger`
105
136
  `asynchronous_best_effort`: pending records can be lost on a crash, and in-memory totals can exceed
106
137
  persisted totals. Error evidence remains visible after later successful writes.
107
138
 
108
- `serve` replays a bounded regular-file ledger at startup and rotates it to `usage.jsonl.1` at
109
- 16 MiB. Successful writes contribute to restart totals; completion is not a zero-loss persistence
110
- promise. Embedded callers can use the bounded `flush_ledger` barrier for orderly teardown; it
111
- confirms attempted writes, not recovery of dropped/failed records or a filesystem sync.
112
-
113
- The ledger lives next to the feedback file in the platform data directory. Choose another path with
114
- `--usage-file` (or `FREELLAMA_USAGE_FILE`), or keep totals in memory only with `--ephemeral-usage`.
139
+ The ledger is the `usage` table of the shared database (`~/.octocode/freellama/freellama.sqlite`). `serve`
140
+ keeps daily totals for the last 35 days in memory, computed in the database at startup, and never
141
+ deletes records. `usage --days N` past 35 days reads the older days from the database on request
142
+ (`retention.older_days: "ledger"`); with `--ephemeral` they are `"not_retained"`. Memory holds at
143
+ most 256 models per day, and the task counters at most 256 models: raw requests can name any
144
+ model, so further names are counted under `other`. Successful writes
145
+ contribute to restart totals; completion is not a zero-loss persistence promise. Embedded callers
146
+ can use the bounded `flush_ledger` barrier for orderly teardown; it confirms attempted writes, not
147
+ recovery of dropped/failed records. `--home` moves the database; `--ephemeral` keeps totals in
148
+ memory only.
115
149
 
116
150
  ## Scrape metrics
117
151
 
@@ -120,7 +154,7 @@ The ledger lives next to the feedback file in the platform data directory. Choos
120
154
  `freellama_load_seconds_total`, `freellama_raw_requests_total`, `freellama_evictions_total`,
121
155
  `freellama_circuit_open_total`, `freellama_concurrency_limit_changes_total`); task counters are
122
156
  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,
157
+ backend. `freellama_admission_affinity_promotions_total` counts model-affinity grants per backend. Gauges cover admission, queues, the raw proxy, host and GPU memory,
124
158
  loaded models, and `freellama_ollama_num_parallel`. Point Prometheus, Grafana Agent, or any
125
159
  OpenMetrics-compatible scraper at it.
126
160
 
@@ -167,8 +201,8 @@ A task captures its cost when execution starts. Reload affects tasks that start
167
201
  already queued or active tasks keep their captured cost. `config` reports effective values and
168
202
  the runtime-file source; invalid keys, zero,
169
203
  or overflow reject the reload and retain the last good configuration. Preview advice, health
170
- costs, and execution receipts use the resolved policy. Health retains its summary aliases and
171
- exposes the complete task-kind map under `admission.costs.base_by_task`. Task kinds remain
204
+ costs, and execution receipts use the resolved policy. Health exposes the complete task-kind map
205
+ under `admission.costs.base_by_task` and the embedding batch formula. Task kinds remain
172
206
  caller-declared; these weights are cooperative scheduling policy, not workload classification.
173
207
  See the
174
208
  [runtime example](https://github.com/bgauryy/FreeLlama/blob/main/freellama.runtime.example.toml) for the table syntax.
@@ -198,10 +232,8 @@ report verified placement and `feedback.accepted:true` while
198
232
  `execution.throughput_learning.eligible:false`; it contributes no comparable speed sample.
199
233
 
200
234
  Each backend retains at most one adaptive profile window per task kind. Status exposes
201
- `adaptive.profile_windows` keyed by task; `baseline_output_tokens_per_second` is a compatibility
202
- output-only summary by model and can omit additional task windows for the same model. Embedding
203
- windows report their explicit input-rate kind separately. Use the task windows for learning
204
- evidence. See [automatic feedback](CPU_GPU_ROUTING.md#understand-automatic-feedback) for
235
+ `adaptive.profile_windows` keyed by task. Embedding windows report their explicit input-rate kind
236
+ separately. Use the task windows for learning evidence. See [automatic feedback](CPU_GPU_ROUTING.md#understand-automatic-feedback) for
205
237
  persisted routing windows.
206
238
 
207
239
  The CPU backend is adaptive by default because oversubscribing it shows up as a throughput collapse
@@ -209,14 +241,24 @@ rather than an error. Every change is counted in metrics and shown in `status`.
209
241
 
210
242
  ## Model residency and eviction
211
243
 
212
- Omitted managed `keep_alive` values use finite retention informed by measured reuse and loading.
213
- Explicit values take precedence. For governed prewarming, profile compatibility, and retention
244
+ Omitted managed `keep_alive` values use finite retention informed by measured reuse and loading,
245
+ starting from `base_seconds` (300, Ollama's own default) and shortened by `pressure_factor` under a
246
+ host hold. Explicit values take precedence. The background pressure sweep releases, under
247
+ FreeLlama's own low-memory estimate, only runners idle longer than `base_seconds ×
248
+ pressure_factor` (75 s by default); swapping or OS memory pressure releases any idle runner. For governed prewarming, profile compatibility, and retention
214
249
  settings, see [Scope history and model warming](SCOPES_AND_WARMING.md).
215
250
 
216
251
  When a model about to load needs room, FreeLlama unloads idle runners itself (`keep_alive: 0`)
217
- instead of letting the load wait, fail, or spill to CPU. It does this in three cases: host RAM
218
- can fall below the reserve, the host is already holding for low memory, or (discrete GPU) the
219
- model does not fit in free VRAM, where Ollama otherwise chooses its own victim.
252
+ instead of letting the load wait, fail, or spill to CPU. It does this in four cases, checked in
253
+ this order: (discrete GPU) the model does not fit in free VRAM, where Ollama otherwise chooses its
254
+ own victim; the load would take the backend's loaded models past `model_memory_budget`; host RAM
255
+ can fall below the reserve; or the host is already holding for low memory. Raw passthrough loads
256
+ (an Ollama API call for a model that is not loaded) take the same steps, and raw calls in flight
257
+ count as busy, so their models are not unloaded under them.
258
+
259
+ `model_memory_budget` (runtime file, or `FREELLAMA_MODEL_MEMORY_BUDGET`) is `"75%"` of RAM by
260
+ default; set a size such as `"24GiB"`, another share, or `"off"`. It applies where the runners share
261
+ host memory: unified memory (Apple silicon) or a CPU backend.
220
262
 
221
263
  Which runners go is a small optimisation, in the spirit of llama-swap's `evict_costs` and LocalAI's
222
264
  busy-aware LRU:
@@ -226,7 +268,7 @@ busy-aware LRU:
226
268
  - **Not for a model bigger than the GPU:** it spills to the CPU whatever is unloaded, so VRAM is
227
269
  left alone.
228
270
  - **Cost of unloading a runner** = its reload time (Ollama's measured `load_duration` from earlier
229
- loads, or size ÷ 1.5 GB/s before the first one) × (1 + recent uses, decaying with a 30-minute
271
+ loads, kept in the shared database across restarts, or size ÷ 1.5 GB/s before the first one) × (1 + recent uses, decaying with a 30-minute
230
272
  half-life; only tasks served count) × its `eviction_costs` weight (default 1).
231
273
  - **Choice:** the set of runners with the lowest total cost that frees enough memory; ties go to
232
274
  fewer runners, then less memory freed beyond what is needed. Only the memory that matters is
@@ -234,10 +276,43 @@ busy-aware LRU:
234
276
  memory.
235
277
 
236
278
  Every managed receipt that caused an eviction carries the plan under
237
- `memory_reservation.eviction`: what was unloaded, its cost and reload estimate, and what was kept
238
- and why. `status` and the page show the latest one that unloaded something. Eviction runs once per
239
- task, after admission and before the memory wait, and is never cancelled halfway by a deadline.
240
- Set `evict_idle_models = false` to leave residency entirely to Ollama.
279
+ `memory_reservation.eviction`: what was unloaded, its cost and reload estimate, what was kept and
280
+ why, the `trigger` (`gpu_memory`, `model_memory_budget` or `host_memory`) and `unload_ms`. `status`
281
+ and the page show the latest one that unloaded something. Eviction runs once per task, after
282
+ admission and before the memory wait, and is never cancelled halfway by a deadline. Set
283
+ `evict_idle_models = false` to leave residency entirely to Ollama.
284
+
285
+ `memory_reservation` names the `kv_cache_type` and `flash_attention` state it sized for. With a
286
+ quantized cache it also carries `ollama_f16_preflight_bytes`, the f16-cache size Ollama's own fit
287
+ check predicts. On a CPU or
288
+ unified-memory backend, `ollama_preflight_threshold_bytes` is 80% of the available memory the load
289
+ was admitted against; when the reserved estimate fits under it and the f16 figure does not, the
290
+ receipt adds `ollama_may_evict_or_offload: true` and a one-line `ollama_preflight_reason`: Ollama may
291
+ unload other runners or offload layers even though FreeLlama found room. Run previews carry the
292
+ same fields. The reservation itself is unchanged: measurements still take precedence over the
293
+ estimate.
294
+
295
+ ### The residency audit
296
+
297
+ Every load, unload and eviction is recorded with its cause and duration in the shared database, so
298
+ the audit covers every FreeLlama process on the machine. `status` summarizes the last hour under
299
+ `residency`:
300
+
301
+ | Field | Meaning |
302
+ |---|---|
303
+ | `model_memory_budget` | The setting, its bytes, and per backend whether it applies, what its loaded models hold, and the share used |
304
+ | `audit.counts`, `audit.by_trigger` | Loads, unloads and evictions; causes such as `managed_task`, `raw_request`, `caller_unload`, `model_memory_budget`, `host_memory` |
305
+ | `audit.switch_ms` | Per model: measured loads, average, total and last load time (Ollama's `load_duration`) |
306
+ | `audit.thrashing` | Models loaded 3 or more times in 10 minutes: raise the budget, pin the model, or give it its own backend |
307
+ | `audit.recent` | The 20 newest events (the database keeps every event; counts cover the whole hour) |
308
+
309
+ A raw passthrough load is recorded when its response ends, with the `load_duration` Ollama
310
+ reported in it, and that time also teaches the eviction planner what reloading the model costs.
311
+ Preloads appear as `preload` or `preload_failed` events with trigger `startup` or
312
+ `predicted_after:<model>`.
313
+
314
+ A task refused because the host stayed under pressure says which signals held it:
315
+ `resource_admission.held_by` (such as `["thermal_throttling"]`), also named in the error text.
241
316
 
242
317
  ## Who decides what: FreeLlama and Ollama
243
318
 
@@ -253,8 +328,8 @@ Two schedulers that both guess at memory can disagree. The split is:
253
328
  | Context size | Ollama by default | `num_ctx` is left to Ollama when its default covers the request |
254
329
 
255
330
  To keep the estimates honest, FreeLlama learns from every runner Ollama reports as resident,
256
- including ones loaded by raw clients, and saves those measurements to `footprints.json` next to the
257
- usage ledger. After a restart a model it has seen before is sized from Ollama's measurement, not a
331
+ including ones loaded by raw clients, and saves those measurements in the shared database, keeping
332
+ the larger size when two processes measure the same runner. After a restart a model it has seen before is sized from Ollama's measurement, not a
258
333
  formula. Each measurement records the `OLLAMA_NUM_PARALLEL` and KV-cache type it was taken under and
259
334
  is used only while those match, so changing either setting falls back to the formula until Ollama
260
335
  measures again. A larger context than any measured one is sized as the measurement plus exactly the extra
@@ -275,6 +350,13 @@ on a discrete GPU only) Ollama's VRAM tier, from total VRAM minus `OLLAMA_GPU_OV
275
350
  Memory estimates always use the context Ollama can allocate. Use `"explicit"` to always
276
351
  send `num_ctx`.
277
352
 
353
+ The context-shift setting is part of a runner's identity too: Ollama reloads a runner when a
354
+ request's `shift` differs from the one it was loaded with, and its default is on for most models.
355
+ `context_shift = "off"` (the default) sends `shift: false` on managed chat, so a generation that
356
+ reaches the context limit stops instead of discarding its earliest tokens, at the cost of a reload
357
+ when raw clients use the same model. `"ollama_default"` omits `shift` so raw and managed chat share
358
+ the runner. Both modes send `truncate: false`. `status` reports it under `ollama.context_shift`.
359
+
278
360
  ## Change settings without a restart
279
361
 
280
362
  Put any of the settings above in a TOML file and start `serve --runtime-config <file>` (or set
@@ -38,10 +38,19 @@ The repository has no CI or release workflows; they were removed (see git histor
38
38
  clean checkout: `yarn release:version <version>`, `yarn release:build` (builds each target's
39
39
  executable and addon into `release-artifacts/<target>/`), `node scripts/assemble-release.mjs
40
40
  release-artifacts release`, then `yarn release:verify:publish`. The publish verifier checks that
41
- `FREELLAMA_RELEASE_TAG`, when set, matches the workspace version.
41
+ `FREELLAMA_RELEASE_TAG`, when set, matches the workspace version. It also requires every addon and
42
+ executable to embed `freellama-source-commit=<sha>` (written by `packages/rust-core/build.rs`; also
43
+ shown by `freellama --version`) equal to `git rev-parse HEAD`, and a clean tracked tree; a stale or
44
+ `-dirty` artifact fails naming the file and the commit it was built from. CI checkouts without git
45
+ set `FREELLAMA_RELEASE_COMMIT` to the 40-character release SHA. Then run `yarn release:smoke`: it packs
46
+ `packages/cli`, `packages/mcp` and this host's `packages/native/<id>`, installs them into a fresh
47
+ project and checks `freellama --version`, that `freellama --help` lists every subcommand of
48
+ `target/release/freellama`, that the installed addon exports exactly the functions in
49
+ `packages/mcp/native/index.d.ts`, and that the installed MCP server answers `tools/list` (needs the
50
+ registry for the MCP dependencies). `yarn verify:production` runs both.
42
51
  Live E2E and hardware qualification remain separate operator checks; verify those receipts before tagging.
43
52
 
44
- Attach `release/SHA256SUMS` and binaries to the release. Publish the eight `@octocodeai/freellama-native-*` packages
53
+ Attach `release/SHA256SUMS` and binaries to the release. Publish every `@octocodeai/freellama-native-*` package
45
54
  first, followed by the exact-version `@octocodeai/freellama` and `@octocodeai/freellama-mcp-server` packages. Publish
46
55
  the Rust crates from the same commit with `cargo publish --workspace` (it uploads `freellama-core`
47
56
  before `freellama-cli`; run it with `--dry-run` first). Configure registry credentials (`npm login`,
@@ -127,16 +136,15 @@ and require the same visible values before admitting work.
127
136
  Generate a token without placing the secret in shell history or the process list:
128
137
 
129
138
  ```bash
130
- freellama auth-token --out ~/.local/share/freellama/auth.token
139
+ freellama auth-token --out ~/.octocode/freellama/auth.token
131
140
  ```
132
141
 
133
142
  The command creates a new file with mode `0600` on Unix and refuses to overwrite an existing
134
- token. Start the service with persistent feedback and authentication:
143
+ token. Start the service with authentication:
135
144
 
136
145
  ```bash
137
146
  freellama serve \
138
- --feedback-file ~/.local/share/freellama/feedback.json \
139
- --auth-token-file ~/.local/share/freellama/auth.token \
147
+ --auth-token-file ~/.octocode/freellama/auth.token \
140
148
  --upstream http://127.0.0.1:11434 \
141
149
  --cpu-upstream http://127.0.0.1:11436 \
142
150
  --cpu-model nomic-embed-text:latest
@@ -147,10 +155,65 @@ adapters. Authentication covers both `/_freellama/v1/*` and Ollama-compatible pa
147
155
  Use `--allow-remote` only with an authentication token. The server refuses unauthenticated
148
156
  nonloopback listeners.
149
157
 
150
- The feedback snapshot is versioned, bounded by backend and task type, and replaced atomically.
151
- Only physically verified warm samples are saved. A corrupt or unsupported snapshot makes startup
152
- fail instead of silently discarding routing evidence. Use `--ephemeral-feedback` only for
153
- disposable tests.
158
+ ### Client keys
159
+
160
+ Give each agent, team or tool its own key instead of sharing the operator token. Create one token
161
+ file per client with `freellama auth-token --out`, then list them in `clients.toml` in the home (or
162
+ pass `--client-keys` / `FREELLAMA_CLIENT_KEYS`):
163
+
164
+ ```toml
165
+ [[client]]
166
+ name = "octocode"
167
+ token_file = "keys/octocode.token" # relative to this file; mode 0600
168
+ daily_tokens = 2000000 # optional; unmetered when absent
169
+ admin = false # optional; true allows model files and config reload
170
+ ```
171
+
172
+ A key's usage is the tokens the backend processed for it per UTC day: output plus the prompt tokens
173
+ not reused from a prompt cache, for managed tasks and raw Ollama or OpenAI calls. The budget is
174
+ checked before each generating request, so the request that crosses it finishes; later ones get 429
175
+ with `code: "client_budget_exhausted"` and a `Retry-After` until the next UTC day. Reads still work.
176
+ Non-admin keys get 403 (`client_not_admin`) for pull, push, copy, create, delete, blob uploads and
177
+ `config/reload`. `usage` shows each key's `tokens_today`, `daily_tokens` and `remaining_today`; a
178
+ client key sees only its own row. Today's totals start from the shared database at startup and
179
+ then count this process's traffic. Point a client's `FREELLAMA_AUTH_TOKEN_FILE` at its own key. A
180
+ serve that the MCP server starts itself treats that file as the operator token, so run `serve`
181
+ yourself when you use client keys.
182
+
183
+ ### Other OpenAI-compatible engines
184
+
185
+ `upstreams.toml` in the home (or `--upstreams` / `FREELLAMA_UPSTREAMS`) puts other engines behind
186
+ the same endpoint:
187
+
188
+ ```toml
189
+ [[upstream]]
190
+ name = "lmstudio"
191
+ url = "http://127.0.0.1:1234/v1"
192
+
193
+ [[upstream]]
194
+ name = "cloud"
195
+ url = "https://api.example.com/v1"
196
+ api_key_file = "keys/cloud.key" # mode 0600; sent only over HTTPS or to this machine
197
+ ```
198
+
199
+ Only OpenAI-compatible paths (`/v1/...`) route by model: `name/model` goes to that engine as
200
+ `model`; a bare name goes to Ollama when Ollama has it, else to the first engine whose `/v1/models`
201
+ lists it. `GET /v1/models` merges every list and names unreachable engines. A request to an engine is
202
+ sent once (no retries), streams through unchanged, carries the `x-freellama-upstream` header and is
203
+ counted in usage. An engine on this machine waits out host holds as a loaded Ollama model would; a
204
+ remote one does not. Managed tasks, residency and the memory budget cover Ollama only, and there is no
205
+ automatic failover from one engine to another.
206
+
207
+ Everything the server learns lives in one `SQLite` database, `~/.octocode/freellama/freellama.sqlite`
208
+ (`--home` or `FREELLAMA_HOME` moves the home): measured runner sizes, placement feedback, the usage
209
+ ledger and the residency audit. It uses write-ahead logging, so several FreeLlama processes on one
210
+ machine share it safely and learn from each other. The feedback snapshot is versioned and bounded by
211
+ backend and task type; only physically verified warm samples are saved, and a schema-1 snapshot (0.7.x)
212
+ is migrated on load (aggregates kept as diagnostics, original kept as `placement_feedback_v1_backup`);
213
+ a corrupt or unknown-version snapshot starts empty with a stderr warning and `/health`
214
+ `feedback.persistence.last_error`, and is never overwritten. A database file with
215
+ another schema version is refused with its path; move or delete it to start a new one. Use
216
+ `--ephemeral` only for disposable tests.
154
217
 
155
218
  ## Bound conversation history and residency
156
219
 
@@ -193,7 +256,7 @@ longer resident before returning. Inspect both `execution.observation` and
193
256
 
194
257
  Require these health contracts before admitting traffic:
195
258
 
196
- - `placement_feedback_persistence: versioned_atomic_snapshot_v1`
259
+ - `placement_feedback_persistence: shared_database_snapshot_v2`
197
260
  - `authentication: optional_bearer_all_routes`
198
261
  - `immediate_unload_observation: observe_then_unload`
199
262
  - `placement_observation: ollama_api_ps_after_execution`
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#mcp-server) to run one managed task.
5
+ Start with the [repository README](https://github.com/bgauryy/FreeLlama/blob/main/README.md#install) to install and run one managed task.
6
6
 
7
7
  ## Choose a guide
8
8
 
@@ -11,8 +11,17 @@ Start with the [repository MCP setup](https://github.com/bgauryy/FreeLlama/blob/
11
11
  | Connect an agent and choose a tool | [MCP server](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md) |
12
12
  | Follow the agent's operating workflow | [FreeLlama skill](https://github.com/bgauryy/FreeLlama/blob/main/skills/freellama/SKILL.md) |
13
13
  | Look up commands and task controls | [CLI reference](CLI.md) |
14
+ | Choose a model for a job | `models {view:"catalog"}`, `freellama catalog`, or the [local model catalog](LOCAL_MODELS.md) |
15
+ | Follow a recipe for one job | [Cookbooks](cookbooks/README.md) |
16
+ | Teach an agent the Octocode CLI in under 30 lines | [Local tools](octocode/LOCAL.md), [GitHub tools](octocode/GITHUB.md) |
14
17
  | Understand task and resource ownership | [Architecture](ARCHITECTURE.md) |
18
+ | See every feature, routing and admission in depth, and why FreeLlama exists | [Features](FEATURES.md) |
19
+ | Compare FreeLlama with Ollama, llama.cpp, llama-swap and LocalAI | [Comparison](COMPARISON.md) |
20
+ | See supported model forms and dated measured results | [Model forms and results](MODEL_FORMS_AND_RESULTS.md) |
21
+ | Reduce reloads, prompt cost and memory; tune Ollama settings | [Tuning](TUNING.md) |
22
+ | Build from source; read operating notes (response store, port 11435, certificates) | [Build and notes](BUILD_AND_NOTES.md) |
15
23
  | Choose a model and interpret its metadata | [Model selection](MODEL_SELECTION.md), [model metadata](MODEL_SELECTION.md#model-metadata) |
24
+ | Inspect quantization, placement, or neuron activations | [Model introspection](MODEL_INTROSPECTION.md) |
16
25
  | Configure CPU helpers alongside GPU work | [CPU/GPU routing](CPU_GPU_ROUTING.md) |
17
26
  | Inspect queues, memory, usage, and live settings | [Monitoring](MONITORING.md) |
18
27
  | Retain history, affinity, or residency | [Scopes and warming](SCOPES_AND_WARMING.md) |
@@ -22,11 +31,7 @@ Start with the [repository MCP setup](https://github.com/bgauryy/FreeLlama/blob/
22
31
  | Run development and release checks | [Testing](TESTING.md), [release packages](TESTING.md#verify-release-packages) |
23
32
  | Embed the routing core | [Rust core](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/README.md) |
24
33
  | Use the read-only status page | `/_freellama/ui` on a running `serve` (see [Monitoring](MONITORING.md)) |
25
-
26
- ## Evidence and design
27
-
28
- | Goal | Source |
29
- |---|---|
34
+ | Configure durable receipts, idempotency and bounded job retention | [Deferred jobs and recovery](DEFERRED_JOBS.md) |
30
35
 
31
36
  Historical measurements describe one model, machine, and workload. Inspect current state and
32
37
  repeat the relevant checks before using them to select models or set resource limits.
@@ -145,9 +145,14 @@ Expired entries are removed during store operations.
145
145
 
146
146
  ## Warm API
147
147
 
148
- `POST /warm`, MCP `run_task {warm:true}`, and CLI `warm` load an exact installed model with a matching
149
- non-embedding task profile. They do not accept prompts or messages, pull a model, or store conversation
150
- history. The default task profile is `completion`.
148
+ `POST /warm`, MCP `ollama_manage {action:"warm"}`, and CLI `warm` load an exact installed model with a matching
149
+ task profile; `task: "embedding"` loads an embedding model with an empty `/api/embed` request. A
150
+ chat warm may carry `messages` (MCP: `systemPrompt` and/or `messages`): the runner evaluates them
151
+ into its prompt cache with `num_predict: 0` and generates nothing, so a later request that starts
152
+ with the same messages, at the same context, reuses that work (its receipt reports
153
+ `cached_prompt_tokens`). Warming never pulls a model or stores conversation history. The default task
154
+ profile is `completion`. Ollama has no load-only request for decision models: they load on their
155
+ first `/v1/systemone` call, and `keep_alive` on that call sets their residency.
151
156
 
152
157
  | HTTP field | MCP field | Meaning |
153
158
  |---|---|---|
@@ -161,6 +166,13 @@ history. The default task profile is `completion`.
161
166
  | `max_wait_seconds` | `maxWaitSeconds` | Shorter admission/resource wait budget. |
162
167
  | `timeout_seconds` | `timeoutSeconds` | Total request deadline, capped by server policy. |
163
168
  | `defer` | `defer` | Return a job handle for the same managed operation. |
169
+ | `messages` | `systemPrompt`, `messages` | A prompt prefix to evaluate into the runner's prompt cache. |
170
+
171
+ A warm of a model that is not resident is a load. Like any load it takes the backend's load lock
172
+ (see [Load lock and use lease](CPU_GPU_ROUTING.md#load-lock-and-use-lease)) until its runner is
173
+ observed in `/api/ps`, so it queues behind another load on the same backend but never waits for
174
+ another model's decoding. It makes room only by unloading idle runners: a model with a running
175
+ task is never evicted for a warm.
164
176
 
165
177
  Synchronous warming returns HTTP `200`; deferred warming returns HTTP `202`. Inspect deferred
166
178
  work through `task_jobs` or `jobs` using its returned ID. Warming reuses ordinary routing,
@@ -177,9 +189,17 @@ chat requires Ollama's `done:true`; an absent or non-boolean completion marker i
177
189
  These are separate observations in Ollama's [chat response](https://docs.ollama.com/api/chat) and
178
190
  [running-model inventory](https://docs.ollama.com/api/ps).
179
191
 
180
- With `keep_alive:"0"`, warming observes placement and then unloads. The `warm` receipt describes
181
- residency after unload: `loaded:false` on verified unload, with
182
- `residency_source:"ollama_api_ps_after_unload"`.
192
+ With `keep_alive:"0"` on a model that is not resident, warming loads it, observes placement and then
193
+ unloads. The `warm` receipt describes residency after unload: `loaded:false` on verified unload,
194
+ with `residency_source:"ollama_api_ps_after_unload"`. On a model that is already resident,
195
+ `keep_alive:"0"` is a plain unload: it waits for that model's running tasks, unloads it and returns
196
+ `{"unloaded": true, "status": "unloaded", "unload_ms": …}` without loading or routing anything.
197
+ `POST /_freellama/v1/unload {"model": …}` (CLI `unload`) does the same and never loads a model that is
198
+ not resident (`status: "not_resident"`).
199
+
200
+ A warm without `task` loads an embedding-only model as an embedding task, so
201
+ `{"model":"nomic-embed-text:v1.5"}` works without `"task":"embedding"`. Warms and preloads teach
202
+ FreeLlama a model's load time but do not count as uses, so they never lengthen their own residency.
183
203
 
184
204
  Warming uses the same truncation and context-shift policy as managed chat. This preserves a
185
205
  compatible runner profile instead of forcing a transition solely because warming omitted those
@@ -187,6 +207,34 @@ controls. Use a context/task profile compatible with the following task. Warming
187
207
  require another runner transition. Warm residency does not reserve a later execution slot. Cache reuse depends on the compatible
188
208
  profile, unchanged prefix, and inference backend. See [Managed execution](ARCHITECTURE.md#managed-task-execution).
189
209
 
210
+ ## Preload
211
+
212
+ The runtime file's `[preload]` table loads models without a caller asking:
213
+
214
+ - `startup`: tags, or tables with `model` and optional `context_tokens`, `keep_alive` (default: the
215
+ warming policy's `max_seconds`) and `messages`, loaded in order once the server is up and again
216
+ for entries a reloaded runtime file adds. `FREELLAMA_PRELOAD` sets a comma-separated list.
217
+ - `predictive` (on by default): every switch from one model to another within
218
+ `follow_window_seconds` (600) is counted in the shared database. Once at least `min_observations`
219
+ (3) switches away from a model happened and `min_probability` (0.5) of them went to one successor,
220
+ finishing a caller's work on the model loads that successor in the background, but only when it
221
+ is not loaded, fits without unloading anything (budget, GPU and host memory), the host is under no
222
+ pressure, and nothing is running or queued on the backend. Only the primary backend predicts.
223
+ `FREELLAMA_PREDICTIVE_PRELOAD=0` turns it off.
224
+
225
+ Predictive warm is refused on a discrete-GPU or remote backend. Vendor-wide free VRAM does not
226
+ say which devices that Ollama process may allocate on, and nothing in FreeLlama verifies it
227
+ (`gpu_capacity_scope` stays `host_aggregate_not_verified_for_backend`), so a speculative load
228
+ could displace work on a pool it cannot see. The skip reason is `GPU device eligibility for this
229
+ Ollama backend is unverified` (`opportunistic_warm_skipped`). Unified-memory hosts and the CPU
230
+ backend, where host memory is the budget, still predict. Explicit warms, startup preloads and
231
+ managed tasks on a discrete GPU run through ordinary admission.
232
+
233
+ Each preload is a background warm under the usual admission, memory and pressure rules, and the
234
+ residency audit records it as `preload` (or `preload_failed`) with trigger `startup` or
235
+ `predicted_after:<model>`. Switches are counted per process from the order requests finish, so
236
+ several agents interleaving models add noise that the probability threshold has to outweigh.
237
+
190
238
  For a bounded warm operation, replace `MODEL_TAG` with an exact installed tag:
191
239
 
192
240
  ```bash
@@ -208,7 +256,7 @@ Matching environment values override `[warming]` file values.
208
256
  |---|---|---:|---|
209
257
  | `min_seconds` | `FREELLAMA_KEEP_ALIVE_MIN_SECONDS` | `15` | Minimum automatic duration. |
210
258
  | `max_seconds` | `FREELLAMA_KEEP_ALIVE_MAX_SECONDS` | `900` | Maximum automatic duration. |
211
- | `base_seconds` | `FREELLAMA_KEEP_ALIVE_BASE_SECONDS` | `60` | Base duration before measured reuse and load cost. |
259
+ | `base_seconds` | `FREELLAMA_KEEP_ALIVE_BASE_SECONDS` | `300` | Base duration before measured reuse and load cost; matches Ollama's five-minute default. |
212
260
  | `reuse_gain_seconds` | `FREELLAMA_KEEP_ALIVE_REUSE_GAIN_SECONDS` | `30` | Additional duration per recent use. |
213
261
  | `load_multiplier` | `FREELLAMA_KEEP_ALIVE_LOAD_MULTIPLIER` | `2.0` | Multiplier for measured load seconds. |
214
262
  | `pressure_factor` | `FREELLAMA_KEEP_ALIVE_PRESSURE_FACTOR` | `0.25` | Duration factor while observed resource pressure holds admission. |
package/docs/TESTING.md CHANGED
@@ -80,6 +80,14 @@ cargo clippy --all-targets -- -D warnings
80
80
 
81
81
  Routing regressions run in the default Rust suite; none are hidden behind `#[ignore]`. Add a
82
82
  focused contract test for each repaired behavior and keep it enabled in ordinary release checks.
83
+ Completion-planning regressions cover payload fit before ranking, global quality gates across
84
+ backends, exact-profile history retention, idempotent snapshot merges, unknown phase
85
+ costs, prefill/decode tradeoffs and cached-token normalization. Runner tests reject mismatched
86
+ digests/contexts and distinguish reported setup duration from an observed load transition.
87
+ Resource tests require released forecasts to invalidate both cached and already-returned host
88
+ snapshots. Predictive warm fixtures use a mock Ollama to assert no queuing or eviction under
89
+ contention; they require permission to bind a loopback listener. MCP tests require accepted jobs
90
+ to retain their identity and fail without re-execution if result retention fails.
83
91
  Resource contracts cover conservative full host-memory reservations when discrete-GPU telemetry is
84
92
  missing, and immediate-unload cleanup after a failed inference response body. The latter also checks
85
93
  that uncertain inference is never replayed and ordinary keep-alive requests retain their runner.
@@ -198,13 +206,27 @@ placement, shared-memory contention, or OCR quality.
198
206
  The historical CPU/GPU observations above predate the corrected paired-run acceptance gate.
199
207
  They remain records of those runs and do not qualify a release.
200
208
 
209
+ ## Run live model harnesses
210
+
211
+ These scripts measure installed models through a running `serve`. They are not part of any test
212
+ tier, and each writes a dated report to the git-ignored `.octocode/local-test/`.
213
+
214
+ | Script | Measures |
215
+ |---|---|
216
+ | `scripts/local-test/rivals.mjs` | The graded questions in `cases.mjs` on rival models per job, three rounds, resumable |
217
+ | `scripts/local-test/lifecycle.mjs` | Cold and warm calls, keep-alive, unload, switching, queue priorities, prompt caching, CPU vs GPU |
218
+ | `scripts/local-test/assist.mjs` | Issue to code, duplicates, triage and OCR on a real repository |
219
+ | `scripts/local-test/media.mjs` | Speech round trip, multimodal image search, embedding comparison through MCP |
220
+ | `scripts/local-test/octocode-agent.mjs` | A local model driving Octocode through `delegate_research` |
221
+
201
222
  ## Verify release packages
202
223
 
203
224
  ```bash
204
225
  # Collect each target's `freellama` executable and `freellama.<target>.node`
205
- # under release-artifacts/<target>/, then stage all eight platform packages.
226
+ # under release-artifacts/<target>/, then stage every platform package in scripts/release-platforms.mjs.
206
227
  node scripts/assemble-release.mjs release-artifacts release
207
- yarn release:verify:publish
228
+ yarn release:verify:publish # also requires each artifact's embedded commit == HEAD, clean tree
229
+ yarn release:smoke # packed-install smoke of the host platform package
208
230
  ```
209
231
 
210
232
  Build every claimed CLI/native target in a clean environment, assemble `SHA256SUMS`, run the full