@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.
- package/CONFIGURATION.md +11 -3
- package/DEVELOPING.md +6 -6
- package/README.md +267 -403
- package/dist/index.js +7186 -1601
- package/dist/research/bash-agent.js +1504 -71
- package/dist/research/coder-agent.js +4677 -0
- package/dist/research/github-agent.js +2932 -0
- package/dist/research/octocode-agent.js +1416 -116
- package/docs/ARCHITECTURE.md +101 -45
- package/docs/BUILD_AND_NOTES.md +31 -0
- package/docs/CLI.md +76 -27
- package/docs/COMPARISON.md +40 -0
- package/docs/CPU_GPU_ROUTING.md +127 -25
- package/docs/DEFERRED_JOBS.md +79 -0
- package/docs/FEATURES.md +303 -0
- package/docs/LOCAL_MODELS.md +190 -0
- package/docs/MODEL_FORMS_AND_RESULTS.md +55 -0
- package/docs/MODEL_INTROSPECTION.md +128 -0
- package/docs/MODEL_SELECTION.md +75 -23
- package/docs/MONITORING.md +115 -33
- package/docs/PRODUCTION.md +74 -11
- package/docs/README.md +11 -6
- package/docs/SCOPES_AND_WARMING.md +55 -7
- package/docs/TESTING.md +24 -2
- package/docs/TUNING.md +86 -0
- package/docs/cookbooks/README.md +22 -0
- package/docs/cookbooks/batch-text.md +19 -0
- package/docs/cookbooks/classify.md +25 -0
- package/docs/cookbooks/coding.md +17 -0
- package/docs/cookbooks/duplicates.md +17 -0
- package/docs/cookbooks/find-code.md +21 -0
- package/docs/cookbooks/image-recognition.md +15 -0
- package/docs/cookbooks/image-search.md +13 -0
- package/docs/cookbooks/jobs.md +60 -0
- package/docs/cookbooks/memory-and-caching.md +31 -0
- package/docs/cookbooks/ocr.md +17 -0
- package/docs/cookbooks/research-agent.md +27 -0
- package/docs/cookbooks/speech-to-text.md +19 -0
- package/docs/cookbooks/sweep.md +66 -0
- package/docs/cookbooks/text-to-speech.md +15 -0
- package/docs/octocode/GITHUB.md +30 -0
- package/docs/octocode/LOCAL.md +29 -0
- package/native/index.d.ts +0 -11
- package/native/index.js +18 -34
- package/package.json +6 -9
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -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
|
|
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. `
|
|
183
|
-
|
|
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
|
|
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
|
|
328
|
+
CO --> CL{"CPU runner resident<br/>at expected identity?"}
|
|
320
329
|
GL -->|"Yes"| GR["GPU shared lock"]
|
|
321
|
-
GL -->|"No"| GX["GPU
|
|
330
|
+
GL -->|"No"| GX["GPU load lock until runner observed"]
|
|
322
331
|
CL -->|"Yes"| CR["CPU shared lock"]
|
|
323
|
-
CL -->|"No"| CX["CPU
|
|
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
|
|
369
|
-
resident during discovery is
|
|
370
|
-
|
|
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
|
|
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
|
|
398
|
-
|
|
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)
|
|
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
|
-
|
|
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 `
|
|
428
|
-
|
|
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
|
|
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
|
|
434
|
-
|
|
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
|
|
446
|
-
instead of silently losing earlier instructions
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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.
|
|
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
|
|
500
|
-
restart-reset behavior is available only through the explicit `--ephemeral
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
-
| `
|
|
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
|
-
| `
|
|
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` |
|
|
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`
|
|
62
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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.
|
|
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`,
|
|
213
|
-
`long-context
|
|
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,
|
|
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
|
|
301
|
-
|
|
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 |
|