@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
@@ -45,10 +45,18 @@ flowchart LR
45
45
  F -->|"raw /api and /v1"| G["Primary Ollama"]
46
46
  F -->|"managed primary task"| G
47
47
  F -->|"managed assigned task"| U["Optional CPU Ollama"]
48
+ F -->|"/v1 for an engine's model"| O["Optional OpenAI-compatible engines<br/>LM Studio, llama.cpp, vLLM, cloud"]
48
49
  G --> E["Metal, MLX, CUDA, ROCm, Vulkan, or CPU runner"]
49
50
  U --> E2["CPU runner"]
50
51
  ```
51
52
 
53
+ Bearer checks run first: the operator token, or a client key whose budget and rights are checked
54
+ and whose name tags the request (`proxy::Caller`). Raw responses stream through unchanged while
55
+ `proxy/usage.rs` reads the token counts and `load_duration` they report; the platform's raw hooks
56
+ turn them into usage-ledger rows, audit events and load-time samples. `proxy/upstreams.rs` routes
57
+ OpenAI-compatible requests by model to other engines. `platform/preload.rs` runs startup loads and
58
+ learns model successors (`next:<model>` facts) for predicted loads.
59
+
52
60
  FreeLlama does not make an individual model decode faster. It can improve workload completion by
53
61
  avoiding unsuitable routes, reducing avoidable transitions, refusing unsupported work early, and
54
62
  overlapping independent CPU and GPU model requests when the machine benefits from that layout.
@@ -159,7 +167,8 @@ The control API uses snake_case JSON. A managed task body is the routing fields
159
167
  installed tag of that family; an unknown or ambiguous name is refused with the installed candidates.
160
168
  `messages` (Ollama message objects, images included) replaces `prompt`; `input` serves embeddings;
161
169
  `context_tokens` owns `num_ctx` and placement owns `num_gpu`, so neither is accepted inside
162
- `request_options.options`. `stop_on_repetition: true` streams the chat from Ollama and stops as soon
170
+ `request_options.options`. `stop_on_repetition` (on by default when no `num_predict` caps the output,
171
+ `false` to turn it off) streams the chat from Ollama and stops as soon
163
172
  as the output repeats its unit twice after the first copy over 96+ characters: the connection
164
173
  closes, which cancels generation upstream, and everything generated is kept with
165
174
  `done_reason: "repetition"`. An Ollama "token repeat limit" abort is kept the same way
@@ -179,9 +188,9 @@ A batch nests whole task bodies:
179
188
 
180
189
  The machine profile is host-derived rather than model-name-derived. It reports total physical
181
190
  memory on macOS (`sysctl`), Linux (`/proc`), and Windows (CIM through PowerShell), along with CPU, OS,
182
- architecture, and disk. `memory_bytes` is portable host RAM. `unified_memory_bytes` is populated
183
- only when host and accelerator memory are known to share a pool; it must not be treated as discrete
184
- GPU VRAM. Ollama's `/api/ps` remains the placement authority.
191
+ architecture, and disk. `memory_bytes` is portable host RAM. `memory_kind` is `unified` only
192
+ when host and accelerator memory are known to share a pool; host RAM must not be treated as
193
+ discrete GPU VRAM. Ollama's `/api/ps` remains the placement authority.
185
194
 
186
195
  All other paths use the compatibility proxy. It buffers request bodies up to 64 MB so a retry can
187
196
  resend identical bytes, then streams the upstream response. It removes hop-by-hop headers, adds
@@ -314,13 +323,13 @@ flowchart TD
314
323
  K --> A{"Acquire selected backend's<br/>3:2:1 fair weighted budget"}
315
324
  A -->|"No"| E503["429 queue full or<br/>503 wait timeout"]
316
325
  A -->|"Yes"| B{"Execution backend"}
317
- B -->|"Primary"| GL{"GPU model resident<br/>under transition lock?"}
326
+ B -->|"Primary"| GL{"GPU runner resident<br/>at expected identity?"}
318
327
  B -->|"CPU assignment"| CO["Add num_gpu: 0"]
319
- CO --> CL{"CPU model resident<br/>under transition lock?"}
328
+ CO --> CL{"CPU runner resident<br/>at expected identity?"}
320
329
  GL -->|"Yes"| GR["GPU shared lock"]
321
- GL -->|"No"| GX["GPU exclusive transition lock"]
330
+ GL -->|"No"| GX["GPU load lock until runner observed"]
322
331
  CL -->|"Yes"| CR["CPU shared lock"]
323
- CL -->|"No"| CX["CPU exclusive transition lock"]
332
+ CL -->|"No"| CX["CPU load lock until runner observed"]
324
333
  GR --> OQ["Send to selected Ollama queue"]
325
334
  GX --> OQ
326
335
  CR --> OQ
@@ -357,6 +366,15 @@ After six capacity bypasses, the scheduler reserves released capacity for the ol
357
366
  so a stream of small requests cannot indefinitely starve a larger request. Cancellation releases
358
367
  that reservation. Admission, resource waiting, and transition locking share one queue deadline.
359
368
 
369
+ Within the class the round robin picks, admission prefers model affinity: the oldest fitting waiter
370
+ whose selected model has a task running on that backend, or was the backend's most recently
371
+ admitted model, goes ahead of older waiters for other models, which would otherwise make Ollama
372
+ load or swap a runner. Affinity never crosses priority classes, never admits a waiter that does not
373
+ fit, and never overrides the capacity reservation. It is bounded by aging: a waiter passed over four
374
+ times, or queued for two seconds, restores FIFO order for its class. Affinity uses only admission's
375
+ own record of admitted work, never an Ollama call. Each pool's receipt counts
376
+ `affinity_promotions`; health reports the bounds under `admission.priority_fairness.model_affinity`.
377
+
360
378
  A task waiting for host resources returns its execution slot after two seconds and continues
361
379
  waiting within the same deadline. It remains counted against the backend's bounded queue, so
362
380
  memory pressure cannot create an invisible backlog. Health reports slot and resource waiters
@@ -365,9 +383,11 @@ new arrivals and a smaller reloaded queue limit cannot displace accepted work. E
365
383
  prevent admission even when an execution slot is free. Cancellation removes either waiter, and releasing a memory reservation wakes resource
366
384
  waiters immediately; host telemetry still obeys the sampling and recovery policy below.
367
385
 
368
- FreeLlama acquires the admission slot before a transition lock to avoid deadlock. A model marked
369
- resident during discovery is checked again while the shared transition lock is held; stale or
370
- unavailable residency falls back to the exclusive transition path. Session affinity is bound only
386
+ FreeLlama acquires the admission slot before the backend load lock to avoid deadlock. A model marked
387
+ resident during discovery is observed again before dispatch; a stale or unavailable listing means a
388
+ load, which takes the load lock and observes once more after winning it (a same-model load that
389
+ finished meanwhile is then shared).
390
+ See [Load lock and use lease](CPU_GPU_ROUTING.md#load-lock-and-use-lease). Session affinity is bound only
371
391
  after successful upstream execution, so refused and failed tasks cannot change later routing.
372
392
  An explicit `done:false` chat response or a reported upstream error is a failure, even with HTTP 200.
373
393
  It cannot train runtime feedback or bind affinity. Completed failed exchanges retain the raw response
@@ -385,7 +405,10 @@ The K/V preflight derives single-sequence F16 K+V bytes-per-token for any archit
385
405
  metadata declares the block count and head counts. Explicit key/value dimensions take precedence
386
406
  over embedding length divided by heads; sliding-window, shared-KV, recurrent, cross-attention, and
387
407
  unknown layouts report unknown. F16 is advisory because quantized KV can
388
- use less memory. The coarse model-file budget refuses files over 80% of local CPU/unified RAM only
408
+ use less memory: reservations scale it by each backend's `OLLAMA_KV_CACHE_TYPE` unless
409
+ `OLLAMA_FLASH_ATTENTION` forces flash attention off (unset is Ollama's `auto`), and keep Ollama's
410
+ F16 fit-check figure beside the quantized one to flag a load Ollama may evict or offload for
411
+ ([KV cache and flash attention](TUNING.md#kv-cache-and-flash-attention)). The coarse model-file budget refuses files over 80% of local CPU/unified RAM only
389
412
  for loopback backends. Forecasts use each backend's independently attributed parallelism and KV
390
413
  cache settings where process inspection is available, with explicit fallback sources otherwise.
391
414
  Process observations refresh on requests after five seconds; measurements made under different
@@ -394,24 +417,29 @@ authority. Configured settings alone do not prove physical processor placement.
394
417
 
395
418
  The shared [resource governor](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/resources/mod.rs) samples local
396
419
  available-memory estimates, load per logical CPU, OS memory pressure, active swap-out growth, and
397
- thermal throttling when reported. OS memory pressure holds every local demand; active swap-out
398
- growth holds new loads; the CPU-load hold applies only to CPU-assigned work (saturated cores slow
420
+ thermal throttling when reported. OS memory pressure holds new loads, and resident runners only
421
+ at the critical level; active swap-out growth holds new loads (a raw passthrough request counts as
422
+ resident when its `model` is in `/api/ps` at a compatible context); the CPU-load hold applies only to CPU-assigned work (saturated cores slow
399
423
  nothing on the GPU, so raw passthrough, which cannot know residency, is not refused for it), and to
400
424
  all work on a host whose sampler sees no accelerator; the
401
425
  thermal hold applies to CPU-assigned work and to cold loads (a new heat source) but not to a bounded
402
426
  request on a runner already resident on the GPU (`ResourceDemand::is_held_by`; the assessment's
403
427
  `held_by` lists the holds that applied). Low available RAM (below the larger of 15% or 1 GiB, clearing after two
404
- healthy samples above the larger of 20% or 2 GiB) depends on the platform (`ResourcePolicy::low_memory`).
428
+ healthy samples above the larger of 20% or 2 GiB) holds nothing host-wide: each load is judged by the capacity
429
+ check below, which follows `ResourcePolicy::low_memory`. Active swapping holds new loads of 1 GiB or more, or of
430
+ unknown size; a smaller load of known size still has to fit its reserve. A held or refused load names the largest
431
+ memory users (`resources::memory_holders`, one memory-sorted `ps` sample cached for 10 s).
405
432
  A new load must leave that reserve free, but never more than its own size above the 1 GiB floor.
406
- On macOS (`advisory`) the estimate undercounts what the OS can reclaim, so a load that does not cover
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
433
+ By default (`refuse`, every platform) a new load that does not fit first unloads idle runners and then waits
409
434
  with `held_insufficient_capacity` until a permit release or a fresh sample frees capacity, or refuses
410
435
  with 503 at its deadline: Linux `MemAvailable` already includes reclaimable cache and is the hard cap
411
436
  in a container, so over-admitting invites the OOM killer. Already-resident runners add no memory and
412
437
  are admitted in both modes, also during an active-swapping hold, so a large load never locks out
413
438
  reuse of its own runner; swapping still holds new loads. Historical swap occupancy alone is not pressure. Samples are cached for two
414
439
  seconds; unavailable signals stay unknown. Remote backends bypass this host gate.
440
+ On macOS the available figure adds bounded reclaimable pages (inactive, speculative, purgeable), so a
441
+ load past it pushes the host into compression and swap. `--low-memory advisory` (`FREELLAMA_LOW_MEMORY`)
442
+ admits such a load with a `memory_advisory` instead, after unloading idle runners.
415
443
 
416
444
  Each pressure signal recovers independently after two fresh samples that meet its recovery
417
445
  threshold. A recovered CPU-load signal therefore clears even while RAM remains low.
@@ -424,14 +452,15 @@ platforms have no host collector, so they default to `best_effort` and admit wit
424
452
  Unsupported Linux collector signals remain explicitly unavailable rather than being fabricated.
425
453
  The pure `ResourceSnapshot::assess_capacity` calculation is shared by admission and previews.
426
454
  Previews use the same footprint lookup, subtract existing reservations, and expose both queue and
427
- resource readiness. Insufficient model headroom reports `runnable_low_memory_estimate` and admits
428
- no siblings on macOS; elsewhere it reports `held_insufficient_capacity`. Preview capacity is advisory and conservative, never a reservation.
455
+ resource readiness. Insufficient model headroom reports `held_insufficient_capacity`, or
456
+ `runnable_low_memory_estimate` under `--low-memory advisory`. Preview capacity is advisory and conservative, never a reservation.
429
457
 
430
- Managed CPU/unified-memory tasks reserve estimated additional model-file and known F16 KV bytes
458
+ Managed CPU/unified-memory tasks reserve estimated additional model-file and known KV bytes
431
459
  across both backends. Verified runner footprints replace that estimate only for the same backend,
432
460
  immutable model digest, and equal-or-larger observed context. Exact resident digest/context matches
433
- avoid double-counting existing allocations. The reservation is rechecked after transition locking
434
- without waiting while holding the backend lock; an increased requirement that no longer fits the
461
+ avoid double-counting existing allocations. The reservation is rechecked after winning the load lock
462
+ (forecast from the residency listing that decided the transition) without waiting while holding
463
+ that lock; an increased requirement that no longer fits the
435
464
  estimate dispatches with a `memory_advisory`, and a pressure hold refuses with 503. Permits release on completion or cancellation. On discrete-GPU hosts, observed free
436
465
  VRAM discounts the estimated host-memory requirement by the bytes the GPU can hold. Missing VRAM
437
466
  telemetry reserves the full estimated footprint in host RAM. The governor cannot constrain external
@@ -442,24 +471,41 @@ that covers the UTF-8 prompt/schema estimate, output reserve, and 512-token temp
442
471
  by the selected model's advertised limit. Larger requests require explicit context or compaction.
443
472
  Multimodal and embedding requests retain their profiles. The execution receipt exposes
444
473
  `context_sizing`; previews contain routing fields only and therefore retain task defaults.
445
- Managed chat sends `truncate:false` and `shift:false` so supporting Ollama backends refuse overflow
446
- instead of silently losing earlier instructions. Managed embeddings send `truncate:false` too.
447
- Only the dedicated CPU backend supplies a thread default: half the logical CPU count on other
448
- platforms, while macOS leaves Ollama to select its performance cores. `FREELLAMA_CPU_NUM_THREAD`
449
- overrides that default; explicit request options take precedence. The primary backend leaves thread
450
- selection to Ollama to avoid changing the options of a shared runner. `runtime_options` reports
451
- forwarded values; runner support is not assumed.
452
-
453
- Resident tasks share a backend lock. Nonresident tasks take that backend's exclusive lock so a cold
454
- load cannot race another managed task on the same server. CPU and GPU backends have separate locks
455
- and admission pools, so they can progress independently.
456
-
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
474
+ Managed chat sends `truncate:false`, so supporting Ollama backends refuse an oversized prompt
475
+ instead of silently losing earlier instructions, and by default `shift:false`, so a generation that
476
+ reaches the context limit stops instead of discarding its earliest tokens. Ollama reloads a runner
477
+ whose launch-time context-shift setting differs from a request's, so the runtime setting
478
+ `context_shift = "ollama_default"` omits `shift` to share runners with raw clients that leave it at
479
+ Ollama's default; `truncate:false` stays in both modes. Managed embeddings send `truncate:false` too.
480
+ Thread selection defaults to Ollama on every platform. `FREELLAMA_CPU_NUM_THREAD` explicitly
481
+ overrides the dedicated CPU backend; explicit request options take precedence. FreeLlama does not
482
+ guess physical cores from half the logical CPU count. `runtime_options` reports forwarded values;
483
+ runner support is not assumed. Overrides of load controls absent from `/api/ps` take the conservative
484
+ transition path rather than claiming verified runner compatibility.
485
+
486
+ A compatible resident runner needs no backend lock: nonempty model digest and effective context must
487
+ match the observed runner (a request that sends no `num_ctx` also accepts a runner whose context
488
+ `/api/ps` omits). Name-only residency is insufficient, and `admission.reason` names the degraded
489
+ identity. Every task holds a per-model use lease for its whole request, which keeps its runner from
490
+ being evicted or unloaded. Forecast release invalidates the
491
+ host sample generation so an older available-memory observation cannot spend released capacity.
492
+ A task that must load or reload a runner takes that backend's load lock until the runner is
493
+ observed in `/api/ps`, so loads and unloads on one server are serialized and same-model arrivals
494
+ share the loaded runner; decoding on another model does not wait for it. CPU and GPU backends have
495
+ separate locks and admission pools, so they can progress independently.
496
+
497
+ In `serve`, raw generation requests follow the same rules on the primary backend: a resident match
498
+ takes no lock, and a load takes the load lock and returns 503 when it stays busy past the raw queue
499
+ wait. The response body holds the model's use lease through EOF or disconnect. A generation whose
500
+ allocation cannot be forecast is refused with 503 `model_forecast_unavailable`. Metadata GET/HEAD, `/api/show`, model-store calls (pull, push, copy, delete, and
460
501
  blob uploads), and empty `keep_alive:0` unload requests take no lock and bypass pressure. Raw
461
502
  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
462
- returns HTTP 408 and releases its guards without contacting Ollama. Admission receipts report
503
+ returns HTTP 408 and releases its guards without contacting Ollama. Before any body is read, raw
504
+ and control-API requests reserve its size (`Content-Length`, or the request limit when none is
505
+ declared) from one process-wide buffered-body budget, `FREELLAMA_MAX_BUFFERED_BYTES` (256 MiB by
506
+ default), and return it when the buffered body is released. A request that finds the budget full
507
+ waits up to the raw queue wait (control API: the managed queue wait), then gets 503
508
+ `request_body_budget_exhausted` with `Retry-After`; empty bodies reserve nothing. Admission receipts report
463
509
  `queue_wait_ms`, `resource_wait_ms`, `transition_wait_ms`, and
464
510
  their sum as `total_wait_ms`. Health and route receipts expose sampled pressure and reservations.
465
511
 
@@ -496,14 +542,24 @@ flowchart TD
496
542
 
497
543
  The hint never changes the operator's CPU assignment. If the preferred backend has no eligible
498
544
  model, routing falls back and returns `preference_satisfied: false`. Feedback is task-specific,
499
- bounded, versioned, and prompt-free. The CLI persists it with atomic file replacement by default;
500
- restart-reset behavior is available only through the explicit `--ephemeral-feedback` mode. It
545
+ bounded, versioned, and prompt-free. The CLI keeps it in the shared database by default;
546
+ restart-reset behavior is available only through the explicit `--ephemeral` mode. It
501
547
  records normalized latency only for already-resident, physically verified tasks so prompt size and
502
548
  cold-load cost do not poison the comparison. It requires more than a 10% advantage; capacity can
503
549
  break a tie before the sample threshold. Quality routing, explicit model selection, and session
504
550
  affinity do not follow the latency loop.
505
551
 
506
- The same selection function supplies preview receipts and managed task execution. This keeps advertised placement and actual request routing aligned.
552
+ Payload context fit is checked before automatic model ranking. Objective/quality gates apply to the
553
+ complete eligible catalog before it is split by backend. Qualified idle resident candidates can use
554
+ setup + prefill + decode estimates instead of a decode-rate-only comparison; cold and busy candidates
555
+ retain the conservative fallback. Exact-profile history is bounded to 32 profiles × 32 observations
556
+ per task/backend, with idempotent process snapshot merging.
557
+
558
+ The same selection function supplies preview receipts and managed task execution. Preview cannot
559
+ estimate an execution payload it does not accept; its placement is a non-reserving snapshot.
560
+ Execution receipts expose `completion_planning`, including estimated costs and whether the comparison
561
+ influenced selection. Ollama's cached-token counts inform prefill-rate learning; FreeLlama does not
562
+ export or manage KV tensors.
507
563
 
508
564
  Raw proxy traffic always uses the primary upstream. For setup and runtime verification, see
509
565
  [Run models on CPU and GPU](CPU_GPU_ROUTING.md).
@@ -600,18 +656,18 @@ FreeLlama keeps these values in memory:
600
656
  - Independent GPU and CPU priority-fair weighted admission pools
601
657
  - Per-task normalized warm latency and queue feedback for each backend, restored from an optional
602
658
  versioned atomic snapshot
603
- - Separate CPU and GPU transition locks
659
+ - Per-model use leases and separate CPU and GPU backend load locks
604
660
 
605
661
  Sessions are bounded (default 1024) and expire after idle time (default one hour); they store only
606
662
  model affinity, never prompts or Ollama KV. Restarting `freellama serve` clears sessions and the catalog cache. Persisted placement feedback is
607
- reloaded; `--ephemeral-feedback` intentionally resets it. Ollama owns model residency, so loaded
663
+ reloaded; `--ephemeral` intentionally resets it. Ollama owns model residency, so loaded
608
664
  models survive a FreeLlama restart. Scope histories and their revisions do not survive restart.
609
665
  They store messages independently from affinity and append only a successful generation at the
610
666
  expected revision. Warming uses ordinary admission and fit checks; omitted residency controls use
611
667
  a finite measured policy. See [Scope history and model warming](SCOPES_AND_WARMING.md) for the
612
668
  public contract and configured limits.
613
669
 
614
- In `serve`, raw generation traffic shares the primary transition lock and receives
670
+ In `serve`, raw generation traffic shares the primary load lock and use leases and receives
615
671
  host-pressure gating, while managed tasks also reserve forecast memory. Direct Ollama callers bypass
616
672
  these controls. FreeLlama cannot preempt inference or constrain other processes. A configured but
617
673
  unreachable CPU backend makes managed catalog discovery fail closed; raw primary passthrough remains
@@ -0,0 +1,31 @@
1
+ # Build from source and operating notes
2
+
3
+ Moved from the root README.
4
+
5
+ ## Good to know
6
+
7
+ - Local inference still costs memory, power and time; offloading shrinks the calling agent's
8
+ context, not the total work.
9
+ - Searches and recommendations never download; a pull needs approval for one exact tag and size.
10
+ - Managed tasks return one result; raw Ollama passthrough keeps streaming.
11
+ - The MCP server saves every tool result except `read_response`'s, prompts included, to
12
+ `~/.octocode/freellama/responses/` (owner-only, oldest dropped past 1 GiB). `FREELLAMA_RESPONSE_STORE_BYTES=0` keeps nothing on disk.
13
+ - Without CA certificates, as in slim containers, FreeLlama still serves plain-HTTP Ollama. Install
14
+ `ca-certificates` for HTTPS upstreams.
15
+ - The service binds to loopback. For remote access, see [Production](PRODUCTION.md).
16
+ - Port 11435 is also Ollaya's default ([Ollaya API](https://github.com/ollaya-dev/ollaya/blob/main/docs/api.md)).
17
+ To run both, use `serve --listen` on another port and set the MCP server's
18
+ `FREELLAMA_SERVE_ENDPOINT` to match.
19
+
20
+ ## Build from source
21
+
22
+ Requires Rust 1.89+ (`rust-toolchain.toml` pins the compiler) and Node.js 22.12+ with Corepack.
23
+
24
+ ```bash
25
+ corepack enable && yarn install && yarn build
26
+ ./target/release/freellama serve
27
+ node packages/mcp/dist/index.js # MCP server from this checkout
28
+ ```
29
+
30
+ npm releases can lag this checkout. [Testing](TESTING.md) describes the checks; the release gate
31
+ is `yarn verify:production`.
package/docs/CLI.md CHANGED
@@ -26,20 +26,23 @@ flowchart TD
26
26
  | `serve` | Run the control plane and Ollama-compatible proxy | Starts it |
27
27
  | `auth-token` | Create a new mode-0600 bearer-token file without printing the secret | No |
28
28
  | `models` | List installed models, capabilities, residency, and evidence | Yes |
29
- | `machine` | Print portable host RAM, CPU, OS, architecture, disk, and the local Ollama endpoint | Yes |
29
+ | `catalog` | Tested model picks per job, with evidence, what is installed, and what fits now (`--job`); without `serve`, `installed` and `fits_now` stay null | No |
30
+ | `machine` | Print the machine execution profile (CPU, memory, GPU, backends) visible to the platform | Yes |
30
31
  | `status` | Live queues (also as a page at `http://127.0.0.1:11435/_freellama/ui`), current and adaptive limits, circuit breakers, loaded models, host memory, Ollama's effective settings, today's usage | Yes |
31
32
  | `usage` | Task and token totals per day and per model (`--days`, default 7) | Yes |
32
33
  | `config` | Effective runtime settings and the source of each; `--reload` re-reads the runtime file | Yes |
33
34
  | `session` | Create model affinity for related tasks | Yes |
34
35
  | `scope` | Create, inspect, fork, or delete bounded message history | Yes |
35
- | `warm` | Warm an installed model through managed admission | Yes |
36
+ | `ready` | Exit 0 once `serve` can run managed work (Ollama answers and its catalog reads); otherwise print what it waits for and exit non-zero | Yes |
37
+ | `warm` | Load an installed model (chat or embedding) through managed admission | Yes |
38
+ | `unload` | Unload a resident model once its running tasks finish; a model that is not loaded is reported (`status: not_resident`), never loaded first, and still exits 0 (idempotent, like `rm -f`) | Yes |
36
39
  | `route` | Choose a model and request profile without executing it | Yes |
37
40
  | `task` | Route and execute one nonstreaming task | Yes |
38
41
  | `jobs` | List, inspect, cancel, or remove deferred tasks and warming operations | Yes |
39
42
  | `proxy` | Run only the Ollama-compatible retry and telemetry sidecar | No |
40
43
  | `bench-all` | Measure installed models by capability group | No |
41
44
  | `policy from-eval` | Write a task policy (`platform.toml`) from a quality-evaluation aggregate and the installed tags | No |
42
- | `doctor` | Inspect Ollama, hardware, versions, and effective settings | No |
45
+ | `doctor` | Verify that an Ollama server exposes the required diagnostic APIs and report the hardware, versions, and settings it found | No |
43
46
 
44
47
  Run `npx @octocodeai/freellama <command> --help` for every accepted flag and enum value. The executable's help
45
48
  is authoritative; this page explains how the commands fit together.
@@ -58,10 +61,18 @@ file must be a regular file, not a symlink, and hold one token of at least 32 by
58
61
  whitespace is trimmed. On Unix it must also be owner-only (mode 0600). Windows file permissions
59
62
  (ACLs) are not checked, so protect the file with your user profile's permissions there.
60
63
 
61
- `serve` persists bounded adaptive feedback under the platform data directory by default. Override
62
- the path with `--feedback-file`; use `--ephemeral-feedback` only for disposable runs. See the
64
+ `serve` keeps what it learns (runner sizes, placement feedback, usage and the residency audit) in
65
+ `~/.octocode/freellama/freellama.sqlite`, shared with every other FreeLlama process on the machine.
66
+ `--home` (or `FREELLAMA_HOME`) moves the home; use `--ephemeral` only for disposable runs. See the
63
67
  [production runbook](PRODUCTION.md) for token generation, state files, and ingress requirements.
64
68
 
69
+ `--client-keys` (or `FREELLAMA_CLIENT_KEYS`, default `clients.toml` in the home) adds named client
70
+ keys with daily token budgets, and `--upstreams` (or `FREELLAMA_UPSTREAMS`, default
71
+ `upstreams.toml` in the home) adds OpenAI-compatible engines that `/v1/*` requests route to by
72
+ model; see [client keys and other engines](PRODUCTION.md#client-keys). The runtime file's
73
+ `[preload]` table loads models at startup and predicts the next one; see
74
+ [Preload](SCOPES_AND_WARMING.md#preload).
75
+
65
76
  `serve` exposes both managed routes under `/_freellama/v1/*` and byte-preserving Ollama routes under
66
77
  `/api/*` and `/v1/*`. Use `proxy` when you need only the latter. See
67
78
  [Architecture](ARCHITECTURE.md) for the request flow.
@@ -78,8 +89,17 @@ npx @octocodeai/freellama serve \
78
89
  --cpu-model nomic-embed-text:latest
79
90
  ```
80
91
 
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.
92
+ Or let `serve` start and own the CPU process (stopped when `serve` exits; a port that already answers
93
+ is refused):
94
+
95
+ ```bash
96
+ npx @octocodeai/freellama serve --spawn-cpu-backend --cpu-model nomic-embed-text:latest \
97
+ [--cpu-backend-port 11436] [--ollama-binary /path/to/ollama]
98
+ ```
99
+
100
+ FreeLlama rejects a CPU upstream with no assignments, `--cpu-model` without `--cpu-upstream` or
101
+ `--spawn-cpu-backend`, both of those together, and a CPU upstream that is the same Ollama endpoint as
102
+ `--upstream`, including loopback aliases on one port.
83
103
  It also sets `options.num_gpu=0` on CPU-assigned managed requests.
84
104
  Read [CPU and GPU model routing](CPU_GPU_ROUTING.md) before deploying this layout.
85
105
 
@@ -129,7 +149,7 @@ over the cap waits up to `--raw-queue-wait-seconds` (default 10) and is then ref
129
149
  All of these, plus pinned models, idle eviction, adaptive concurrency, the circuit breaker, and
130
150
  context sizing, can also live in a runtime file (`--runtime-config`) that `serve` re-reads when it
131
151
  changes. See [Monitoring and live tuning](MONITORING.md). The standalone
132
- `proxy` command keeps its cap opt-in for compatibility.
152
+ `proxy` command leaves its cap off unless set.
133
153
 
134
154
  The local host-pressure gate reports low available RAM below 15% and clears it above 20% after two
135
155
  healthy samples; minimum reserves are 1 GiB and 2 GiB, respectively. Tune the percentages with
@@ -137,18 +157,21 @@ healthy samples; minimum reserves are 1 GiB and 2 GiB, respectively. Tune the pe
137
157
  lower than the resume value, and neither can exceed 100. Health reports the policy, sampled signals,
138
158
  unknown measurements, and reserved memory. These flags do not change Ollama or OS settings.
139
159
 
140
- On macOS, low available RAM is advisory and never refuses a task. Effective available RAM is free
141
- memory plus a bounded reclaimable estimate, so it can be far lower than what the OS can actually
142
- reclaim. When it does not cover already-reserved bytes, the task's estimated bytes, and the reserve,
143
- FreeLlama first unloads idle unpinned runners, then dispatches anyway: the receipt reports
160
+ When effective available RAM does not cover already-reserved bytes, the task's estimated bytes and the
161
+ reserve, FreeLlama first unloads idle unpinned runners, then holds the new load with
162
+ `held_insufficient_capacity` until capacity returns, refusing with 503 at its deadline. This is the
163
+ default on every platform: Linux and Windows available RAM already counts reclaimable cache (and is
164
+ the hard cap in a container), and on macOS effective available RAM is free memory plus a bounded
165
+ reclaimable estimate. A load already resident is never held by it. `--low-memory advisory` (or
166
+ `FREELLAMA_LOW_MEMORY=advisory`) instead dispatches such a load: the receipt reports
144
167
  `status: "admitted_low_memory_estimate"` and `assessment.memory_advisory: "below_reserve"` with
145
- `required_with_reserve_bytes` and `effective_available_bytes`. Ollama and the OS decide whether the
146
- load fits. On Linux, Windows, and other platforms, where available RAM already counts reclaimable
147
- cache (and is the hard cap in a container), the same shortfall unloads idle runners and then holds the
148
- new load with `held_insufficient_capacity` until capacity returns, refusing with 503 at its deadline.
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
168
+ `required_with_reserve_bytes` and `effective_available_bytes`, and Ollama and the OS decide whether
169
+ it fits.
170
+ OS memory pressure holds new loads where the OS reports it (macOS, and Linux when the kernel
171
+ exposes `/proc/pressure/memory`; Windows has no host collector yet), and holds already-resident
172
+ runners too only at the critical level: a warning is what a large load itself raises. Active
173
+ swapping and low RAM hold new loads only: an already-resident runner adds no memory and stays
174
+ admitted, for raw passthrough requests too when the request's `model` is in Ollama's `/api/ps`. The CPU-load hold (one-minute load at
152
175
  or above 1.5 per logical CPU) applies only to CPU-assigned work, which decodes on those cores; GPU
153
176
  work, loaded or not, proceeds, so another program's build never blocks a GPU model. On a host where
154
177
  the sampler sees no accelerator (no GPU device or GPU memory), every model runs on the CPU, so the
@@ -189,7 +212,8 @@ Start with the read-only guided receipt, then inspect available models. `init` n
189
212
  it stops at exact-tag approval and prints the next prerequisite, serve, managed-task, and MCP steps.
190
213
  Its `status` is `blocked` when Ollama is unavailable, `setup_required` when the managed service or
191
214
  installed models are missing, and `ready` when all three are present. Separate `readiness` flags
192
- identify the missing part. Readiness does not qualify a model for a particular task; use a route
215
+ identify the missing part. `init` prints the receipt in every case but exits non-zero only when the
216
+ status is `blocked`; `setup_required` and `ready` exit 0. Readiness does not qualify a model for a particular task; use a route
193
217
  preview to inspect eligibility and rejection reasons.
194
218
 
195
219
  ```bash
@@ -199,6 +223,14 @@ npx @octocodeai/freellama models
199
223
  npx @octocodeai/freellama machine
200
224
  ```
201
225
 
226
+ `serve` answers `GET /_freellama/v1/health` as soon as it listens (there is no bare `/health` route). Wait on `ready` instead before sending work from a
227
+ script; it returns 503 with `waiting_for: "ollama" | "cpu_backend" | "catalog"` until it can serve:
228
+
229
+ ```bash
230
+ until npx @octocodeai/freellama ready >/dev/null 2>&1; do sleep 1; done
231
+ npx @octocodeai/freellama unload --model nomic-embed-text:v1.5
232
+ ```
233
+
202
234
  Preview a deterministic decision without spending generation tokens:
203
235
 
204
236
  ```bash
@@ -209,13 +241,24 @@ npx @octocodeai/freellama route \
209
241
  --required-capability vision
210
242
  ```
211
243
 
212
- Task kinds are `completion`, `coding`, `code-repair`, `tools`, `browser`, `vision`, `embedding`, and
213
- `long-context`. Objectives are `fastest`, `balanced`, and `quality`. `fastest` can use capability
244
+ Task kinds are `completion`, `coding`, `code-repair`, `tools`, `browser`, `vision`, `embedding`,
245
+ `long-context`, and `decision` (hyphens and underscores are interchangeable). Objectives are `fastest`, `balanced`, and `quality`. `fastest` can use capability
214
246
  and local benchmark evidence alone. Default `balanced` prefers eligible task-policy candidates;
215
247
  without them it falls back to capability/context-compatible models and reports low confidence
216
248
  and missing quality evidence. `quality` requires a policy unless you supply an explicit model.
217
249
  An explicit minimum-confidence gate still refuses insufficient evidence in every objective.
218
250
 
251
+ `route` ranks by task eligibility and quality evidence, not by free memory, so under memory pressure
252
+ it can pick a large model whose receipt says `dispatch_readiness: held_host_pressure`. That is
253
+ deliberate: the route is a deterministic preview from the catalog, and host memory is checked later
254
+ at admission. When admission refuses for memory, the error states `needs X GB incl. Y GB reserve,
255
+ Z GB free`, names the largest memory users, and lists installed models of the same task capability
256
+ that fit the free memory (or says none does). Pin one with `--model`.
257
+
258
+ Failures exit non-zero (1 for runtime errors, 2 for usage errors): `init` when `blocked`, and every
259
+ command that cannot reach `serve` or is refused by it. A malformed JSON flag names itself, for
260
+ example `--questions is not valid JSON: <parser error>`.
261
+
219
262
  ## Execute tasks
220
263
 
221
264
  The prompt for `task` is positional:
@@ -225,6 +268,10 @@ npx @octocodeai/freellama task --task completion --objective fastest "Reply with
225
268
  npx @octocodeai/freellama task --task coding --min-confidence medium "Explain this patch."
226
269
  ```
227
270
 
271
+ `freellama catalog [--job JOB]` prints the shipped model picks for each job, with their evidence, what
272
+ is installed and what fits now. Without a running `serve`, `installed` and `fits_now` stay null. Recipes
273
+ are in the [cookbooks](cookbooks/README.md).
274
+
228
275
  Useful task options include:
229
276
 
230
277
  - `--model` for an exact installed model.
@@ -234,7 +281,9 @@ Useful task options include:
234
281
  - `--context-tokens` for the complete input and output context window (`num_ctx`).
235
282
  - repeatable `--required-capability` constraints.
236
283
  - repeatable `--image` paths for vision tasks.
237
- - `--input-file` reads the input from a file: one item per line for `--task embedding` (batched in one request), otherwise the whole file is the prompt.
284
+ - `--input-file` reads the input from a file: one item per line for `--task embedding` (batched in one request), the state for `--task decision` (JSON stays JSON), otherwise the whole file is the prompt.
285
+ - `--nearest QUERY [--top N]` for `--task embedding --input-file FILE`: ranks the file's lines against the query and prints the closest (line number, score, text) instead of vectors; use the model's own prefixes, such as `search_query: ` for nomic.
286
+ - `--questions JSON|@FILE` for `--task decision`: named `noul`, `choice` or `score` questions answered by a decision model through `/v1/systemone`, for example `freellama task --task decision --questions '{"kind":{"type":"choice","instructions":"Which kind?","criteria":{"bug":"Broken","feature":"New"}}}' "Checkout returns 500 since 9am."`.
238
287
  - `--min-confidence low|medium` to refuse insufficiently evidenced routes before generation.
239
288
  - `--priority interactive|normal|background` for fair admission scheduling.
240
289
  - `--max-wait-seconds` for a shorter admission/resource wait budget.
@@ -251,8 +300,8 @@ through MCP `task_jobs`.
251
300
  Cancellation and removal wait for local execution permits to be released; they do not unload a shared model
252
301
  or confirm that the physical runner has stopped.
253
302
 
254
- Jobs stay in server memory: at most 64 receipts, 1 MiB per input, and 2 MiB per retained result or
255
- error. Finished receipts expire after 10 minutes and can be evicted earlier to make room for new
303
+ Jobs stay in server memory: at most 64 receipts, 16 MiB per input (64 MiB across queued and running
304
+ jobs), and 2 MiB per retained result or error. Finished receipts expire after 10 minutes and can be evicted earlier to make room for new
256
305
  work. A restart discards all jobs. Admission queue limits still apply, and deferred work can fail
257
306
  after acceptance. See [monitoring](MONITORING.md) for states and waiting reasons.
258
307
 
@@ -297,8 +346,8 @@ npx @octocodeai/freellama serve
297
346
  ```
298
347
 
299
348
  `serve` resolves each file in this order: the explicit flag, its environment variable, the working
300
- directory, then the per-user config directory (`$XDG_CONFIG_HOME/freellama/` or
301
- `~/.config/freellama/`; `%APPDATA%\FreeLlama\` on Windows).
349
+ directory, then the FreeLlama home (`~/.octocode/freellama`, or `--home` / `FREELLAMA_HOME`). The home also holds
350
+ `runtime.toml`, read when neither `--runtime-config` nor `FREELLAMA_RUNTIME_CONFIG` is set.
302
351
 
303
352
  | File | Flag | Environment variable | Discovered name |
304
353
  |---|---|---|---|
@@ -0,0 +1,40 @@
1
+ # How FreeLlama compares
2
+
3
+ Moved from the root README.
4
+
5
+ ## How it compares
6
+
7
+ FreeLlama is not an inference engine: Ollama loads and runs the model, FreeLlama decides whether,
8
+ where and how each task runs. Other projects' behavior is taken from their docs and source, checked
9
+ on 2026-11-01. Moving-branch features may differ from installed releases.
10
+
11
+ | Decision | Other local stacks | FreeLlama |
12
+ |---|---|---|
13
+ | Can this load fit right now? | Ollama already estimates memory, reuses runners, queues work and evicts models ([scheduler](https://github.com/ollama/ollama/blob/main/server/sched.go), [FAQ](https://docs.ollama.com/faq)). llama.cpp has model loading and eviction ([server docs](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md)); llama-swap documents declared capacity pools and cost-based eviction ([config](https://github.com/mostlygeek/llama-swap/blob/main/docs/config.example.yaml)); LocalAI has memory-management policies with backend-dependent estimates ([docs](https://localai.io/docs/advanced/vram-management/)). | Checks each load against live available memory plus a reserve sized to the load, unloading idle runners first. A load that still does not fit waits, then is refused with a reason and a `Retry-After` from measured service time; resident models keep serving. While models are loaded, a background sweep unloads idle ones when the host runs short. |
14
+ | Is the machine busy or hot? | Ollama and the llama.cpp router do not gate on host CPU load or heat; llama-swap exports swap and GPU temperature as metrics without acting on them ([metrics](https://github.com/mostlygeek/llama-swap/blob/main/internal/perf/prometheus.go)). | Holds CPU-bound work (all work on a host without a GPU) while the 1-minute load average is at least 1.5 per CPU, and CPU-bound work and new loads under Mac thermal pressure; the receipt names the signal. Another program's CPU load never blocks GPU work. |
15
+ | What loads before it is asked for? | llama-swap, the llama.cpp router (`load-on-startup`) and LocalAI (`--load-to-memory`) load a fixed list at startup ([llama-swap](https://github.com/mostlygeek/llama-swap/blob/main/docs/config.example.yaml), [llama.cpp](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md), [LocalAI](https://github.com/mudler/LocalAI/blob/master/docs/content/reference/cli-reference.md)). These examples describe startup configuration, not an exhaustive feature comparison. | A startup list, optionally with a prompt prefix evaluated into the runner's prompt cache, plus the model that usually comes next, loaded only when it fits without unloading anything. |
16
+ | Who may spend what? | LiteLLM enforces per-key budgets from its database ([users](https://docs.litellm.ai/docs/proxy/users)); llama-swap and the llama.cpp router accept a static key list. | Named client keys with per-key usage, a daily token budget that excludes prompt-cache reuse, and admin rights for model files, on managed and raw traffic alike. |
17
+ | Other engines? | LiteLLM, Olla and LocalAI route across engines and fail over between them ([LocalAI failover](https://github.com/mudler/LocalAI/blob/master/docs/content/features/model-failover.md)). | OpenAI-compatible engines (LM Studio, llama.cpp, vLLM, a cloud API) behind the same `/v1` endpoint, routed by model name. |
18
+ | Who goes first? | LiteLLM returns 429 once a deployment's parallel limit is reached, with an optional priority queue ([routing](https://docs.litellm.ai/docs/routing), [scheduler](https://docs.litellm.ai/docs/scheduler)). | Weighted fair admission per backend (interactive 3 : normal 2 : background 1) with starvation prevention and bounded waits; separate GPU and CPU pools. |
19
+ | Which model? | Ollama runs the tag it is given. LiteLLM picks among deployments by load, latency or cost; RouteLLM trains a strong/weak router ([RouteLLM](https://github.com/lm-sys/RouteLLM)). | Picks an installed model by capabilities, context window, memory estimate and residency; a model that keeps looping on a task kind ranks last for it. |
20
+ | Did it loop? | Ollama aborts some loops with a "token repeat limit" error. | Streams, stops once the output repeats and keeps the text (`stopOnRepetition`); `output_check` on every task. |
21
+ | Loading or waiting? | Ollama streams generation and reports final timing; outer admission and host-resource phases belong to the coordinator. | MCP progress notifications (when the client sends a `progressToken`): `queued`, `waiting for memory: need 9.1 GB, 6.4 GB available`, `loading MODEL (~N s)`, `running`; the answer line adds `loaded N s, evicted X`. `GET /ready` answers 503 naming what is missing (`ollama`, `cpu_backend`, `catalog`). |
22
+ | What reaches the agent's context? | Ollama returns its native response; presentation and filtering depend on the client. | A compact result (answer, verdict, model, tokens) plus a `response_file` holding thinking, vectors, research observations and receipts, read a section or page at a time with `read_response`. |
23
+ | Where did it run? | Ollama exposes running-model memory/placement in `/api/ps` and generation timing in responses. | A receipt with the chosen model and why, queue and resource waits, and the placement Ollama reported in `/api/ps`, so a silent CPU fallback is visible. |
24
+
25
+ Limits: FreeLlama does not change Ollama's decode speed, does not fail over between engines, and runs
26
+ CPU work only on a second, CPU-only Ollama process (`serve --spawn-cpu-backend` starts and stops one for you).
27
+
28
+ Measured on one Apple M2 Pro (32 GB) unless noted; single-machine measurements of specific
29
+ workloads, not a general speed claim. The gains come from not loading, waiting or generating what the
30
+ task does not need:
31
+
32
+ | Workload | Without | With FreeLlama |
33
+ |---|---|---|
34
+ | OCR of 12 report strips, `glm-ocr`, 1,500-token cap | 12,948 output tokens, 126 s | `stopOnRepetition`: 6,452 tokens, 69 to 103 s, every label still read |
35
+ | Resident GPU tasks during a compiler build | 64 to 120 s waits (host-wide CPU-load hold, 0.5.0) | No wait: the CPU-load hold applies only to CPU-assigned work |
36
+ | `Retry-After` for a queued burst | 76 s quoted for a 32 s wait (0.5.0) | 32 s |
37
+ | A GPU model and a CPU model side by side (M4 Pro, 48 GB) | 38.0 s median, one Ollama process | 28.2 s median with a second CPU-only Ollama process (1.35x; one of three runs was slower) |
38
+ | Cost of a managed task over raw Ollama, resident embedding, n=20 (0.8.0, 2026-10-31) | raw passthrough: 2.7–2.9 ms median | 4.0 ms median, 5–7 ms p90, with routing, admission and a receipt (56 ms before the per-task process probe was cached) |
39
+ | CPU placement through a second CPU-only Ollama (0.8.0, 2026-10-31) | — | `nomic-embed-text:v1.5` reported `processor: "cpu"`, `size_vram: 0`, verified from `/api/ps`; 265 ms load |
40
+ | `gpt-oss:20b` cold load while the host had 12.7 GB available (0.8.0, 2026-10-31) | 0.7.0 admitted it with a memory warning on macOS; a run like this froze the machine | Held (`held_insufficient_capacity`: needs 18.0 GB with the reserve), then refused at the caller's deadline with `Retry-After`; the model never loaded |