@octocodeai/freellama-mcp-server 0.5.0 → 0.6.1
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 +106 -0
- package/DEVELOPING.md +112 -0
- package/LICENSE-MIT +1 -1
- package/README.md +133 -284
- package/dist/index.js +1514 -1409
- package/dist/research/bash-agent.js +2435 -0
- package/dist/research/octocode-agent.js +1639 -0
- package/docs/ARCHITECTURE.md +176 -101
- package/docs/CLI.md +94 -63
- package/docs/CPU_GPU_ROUTING.md +15 -14
- package/docs/MODEL_SELECTION.md +116 -5
- package/docs/MONITORING.md +26 -16
- package/docs/PRODUCTION.md +33 -26
- package/docs/README.md +9 -17
- package/docs/SCOPES_AND_WARMING.md +8 -9
- package/docs/TESTING.md +22 -35
- package/native/index.d.ts +179 -174
- package/native/index.js +3 -3
- package/package.json +12 -10
- package/adapters/agent_context.py +0 -850
- package/adapters/agent_transport.py +0 -215
- package/adapters/bash_agent.py +0 -345
- package/adapters/octocode_agent.py +0 -404
- package/adapters/shell_sandbox.py +0 -487
- package/docs/ECONOMICS.md +0 -110
- package/docs/INDEX.md +0 -21
- package/docs/MODEL_METADATA.md +0 -106
- package/docs/OLLAMA_SIDECAR.md +0 -78
- package/docs/PRODUCT_POSITIONING.md +0 -121
- package/docs/dev/ADR_RESOURCE_AWARE_BACKEND_ROUTING.md +0 -55
- package/docs/dev/FINDINGS_AND_POSITIONING.md +0 -290
- package/docs/dev/LOCAL_PERFORMANCE_TESTING.md +0 -298
- package/docs/dev/OLLAMA_SYSTEM_OPTIMIZATION.md +0 -255
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -11,10 +11,14 @@ the operating system and drivers schedule physical work.
|
|
|
11
11
|
|
|
12
12
|
## Responsibility boundary
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
This is the canonical ownership rule; other guides link here.
|
|
15
15
|
|
|
16
16
|
| Responsibility | Owner |
|
|
17
17
|
|---|---|
|
|
18
|
+
| Goal decomposition, prompts, system messages, task dependencies, and answer verification | Calling agent |
|
|
19
|
+
| Ollama processes and endpoints, exact `--cpu-model` assignments, runtime settings, and approval of pulls, stops, and deletes | Operator |
|
|
20
|
+
| Prompt templates and model defaults | Ollama model metadata or a `Modelfile` |
|
|
21
|
+
| Physical CPU/GPU scheduling and memory | OS, drivers, and accelerator runtime |
|
|
18
22
|
| Model files, runners, Metal, MLX, llama.cpp, and token generation | Ollama |
|
|
19
23
|
| Native `/api/*` and OpenAI-compatible `/v1/*` semantics | Ollama |
|
|
20
24
|
| Installed-model discovery and capability normalization | FreeLlama |
|
|
@@ -23,9 +27,33 @@ The following table defines the ownership boundary:
|
|
|
23
27
|
| Forecast memory reservations, host-pressure holds, and requests to unload idle unpinned models | FreeLlama managed execution |
|
|
24
28
|
| Explicit per-model CPU/GPU backend assignment | FreeLlama and separate Ollama processes |
|
|
25
29
|
| Grounded research adapters, citations, and verification verdicts | FreeLlama MCP server |
|
|
30
|
+
| Bearer authentication of every route except the static `/_freellama/ui` shell | FreeLlama; external ingress owns TLS and tenant authorization |
|
|
26
31
|
|
|
27
|
-
FreeLlama
|
|
28
|
-
|
|
32
|
+
FreeLlama is a governed router, not an unrestricted hardware scheduler: a CPU preference cannot move
|
|
33
|
+
an arbitrary tag, and one managed call contains one task. Only the calling agent knows which tasks
|
|
34
|
+
are independent.
|
|
35
|
+
|
|
36
|
+
### Ollama sidecar
|
|
37
|
+
|
|
38
|
+
FreeLlama is a loopback sidecar, not an Ollama fork. It talks to Ollama over its documented HTTP
|
|
39
|
+
contract, which also makes a second, process-isolated CPU backend possible without changing client
|
|
40
|
+
payloads:
|
|
41
|
+
|
|
42
|
+
```mermaid
|
|
43
|
+
flowchart LR
|
|
44
|
+
C["Ollama-compatible client"] --> F["FreeLlama loopback sidecar"]
|
|
45
|
+
F -->|"raw /api and /v1"| G["Primary Ollama"]
|
|
46
|
+
F -->|"managed primary task"| G
|
|
47
|
+
F -->|"managed assigned task"| U["Optional CPU Ollama"]
|
|
48
|
+
G --> E["Metal, MLX, CUDA, ROCm, Vulkan, or CPU runner"]
|
|
49
|
+
U --> E2["CPU runner"]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
FreeLlama does not make an individual model decode faster. It can improve workload completion by
|
|
53
|
+
avoiding unsuitable routes, reducing avoidable transitions, refusing unsupported work early, and
|
|
54
|
+
overlapping independent CPU and GPU model requests when the machine benefits from that layout.
|
|
55
|
+
Sources: [Ollama API](https://docs.ollama.com/api/introduction),
|
|
56
|
+
[streaming](https://docs.ollama.com/api/streaming), [usage](https://docs.ollama.com/api/usage).
|
|
29
57
|
|
|
30
58
|
## System topology
|
|
31
59
|
|
|
@@ -64,9 +92,10 @@ flowchart TB
|
|
|
64
92
|
ADAPTER --> GPU
|
|
65
93
|
```
|
|
66
94
|
|
|
67
|
-
The CLI and MCP server do not reimplement routing.
|
|
68
|
-
|
|
69
|
-
|
|
95
|
+
The CLI and MCP server do not reimplement routing. CLI commands other than `serve`, `doctor`,
|
|
96
|
+
`init`, and benchmarking are HTTP clients of a running `freellama serve`. The MCP server reaches the
|
|
97
|
+
same control API through the feature-gated NAPI boundary, and unless `FREELLAMA_MCP_AUTOSTART_SERVE=0`
|
|
98
|
+
it starts the bundled `serve` in the background at launch when nothing answers on the default endpoint.
|
|
70
99
|
|
|
71
100
|
## Request classification
|
|
72
101
|
|
|
@@ -79,8 +108,7 @@ flowchart TD
|
|
|
79
108
|
RAW --> PRIMARY["Primary Ollama"]
|
|
80
109
|
P -->|"Yes"| C{"Control endpoint"}
|
|
81
110
|
C -->|"health, machine, models"| READ["Read local and Ollama state"]
|
|
82
|
-
C -->|"
|
|
83
|
-
C -->|"natural-routes"| INTENT["Run intent model, then route"]
|
|
111
|
+
C -->|"routes"| DECIDE["Decide without inference"]
|
|
84
112
|
C -->|"tasks"| RUN["Route, admit, and execute"]
|
|
85
113
|
C -->|"sessions"| SESSION["Create in-memory affinity id"]
|
|
86
114
|
C -->|"scopes"| SCOPES["Retain revision-protected messages"]
|
|
@@ -94,30 +122,72 @@ The control API exposes these endpoints:
|
|
|
94
122
|
| `GET /_freellama/v1/health` | Health, admission capacity, and configured backends | No |
|
|
95
123
|
| `GET /_freellama/v1/machine` | Machine profile | No |
|
|
96
124
|
| `GET /_freellama/v1/models` | Normalized installed catalog, evidence, residency, and execution backend | No |
|
|
97
|
-
| `POST /_freellama/v1/recommendations` | Installed route or reviewed installation plan | No |
|
|
98
125
|
| `POST /_freellama/v1/routes` | Deterministic route decision | No |
|
|
99
|
-
| `POST /_freellama/v1/natural-routes` | Schema-bound intent interpretation followed by deterministic routing | Intent model only |
|
|
100
126
|
| `POST /_freellama/v1/sessions` | New model-affinity session | No |
|
|
101
127
|
| `DELETE /_freellama/v1/sessions/:session_id` | Release model-affinity session | No |
|
|
128
|
+
| `POST /_freellama/v1/sessions/:session_id/kill` | Release a session and cancel its in-flight tasks; the runner stays loaded | No new inference |
|
|
102
129
|
| `POST /_freellama/v1/scopes` | Create bounded message history | No |
|
|
103
130
|
| `GET /_freellama/v1/scopes/:id` | Scope metadata; optional explicit history inclusion | No |
|
|
104
131
|
| `POST /_freellama/v1/scopes/:id/fork` | Fork history at an expected revision | No |
|
|
105
132
|
| `DELETE /_freellama/v1/scopes/:id` | Delete retained history | No |
|
|
106
133
|
| `POST /_freellama/v1/warm` | Admitted load of an exact installed runner profile | Load only |
|
|
107
|
-
| `POST /_freellama/v1/tasks` | Managed chat, vision, tools, or embedding task | Yes |
|
|
134
|
+
| `POST /_freellama/v1/tasks` | Managed chat, vision, tools, or embedding task (body shape under [Request shapes](#request-shapes)) | Yes |
|
|
135
|
+
| `POST /_freellama/v1/task-batches` | Caller-declared independent tasks with ordered per-item receipts | Yes |
|
|
108
136
|
| `GET /_freellama/v1/jobs` | Deferred job metadata without prompts or results | No |
|
|
109
137
|
| `GET /_freellama/v1/jobs/:id` | One deferred receipt and retained result | No |
|
|
110
138
|
| `POST /_freellama/v1/jobs/:id/cancel` | Cancel one deferred task and release its local permits | No new inference |
|
|
111
139
|
| `DELETE /_freellama/v1/jobs/:id` | Cancel active work, release local permits, and remove its retained record | No new inference |
|
|
112
140
|
|
|
141
|
+
Status, usage, metrics, and runtime-config endpoints are listed in [monitoring](MONITORING.md).
|
|
142
|
+
|
|
143
|
+
### Request shapes
|
|
144
|
+
|
|
145
|
+
The control API uses snake_case JSON. A managed task body is the routing fields plus the payload:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"task": "vision", "objective": "fastest", "model": "my-vision-model",
|
|
150
|
+
"session_id": "<uuid from POST /sessions>", "required_capabilities": ["vision"],
|
|
151
|
+
"prompt": "Text Recognition:", "images": ["<base64, no data-URI prefix>"],
|
|
152
|
+
"priority": "background", "keep_alive": "5m", "max_wait_seconds": 30, "timeout_seconds": 120,
|
|
153
|
+
"stop_on_repetition": true,
|
|
154
|
+
"request_options": { "options": { "temperature": 0, "num_predict": 600 }, "format": "json", "think": false }
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`model` is read the way Ollama reads it: an exact installed tag, else `name:latest`, else the one
|
|
159
|
+
installed tag of that family; an unknown or ambiguous name is refused with the installed candidates.
|
|
160
|
+
`messages` (Ollama message objects, images included) replaces `prompt`; `input` serves embeddings;
|
|
161
|
+
`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
|
|
163
|
+
as the output repeats its unit twice after the first copy over 96+ characters: the connection
|
|
164
|
+
closes, which cancels generation upstream, and everything generated is kept with
|
|
165
|
+
`done_reason: "repetition"`. An Ollama "token repeat limit" abort is kept the same way
|
|
166
|
+
(`done_reason: "repeat_limit"`). A stopped stream has no Ollama timing fields, so it trains no
|
|
167
|
+
throughput feedback. The receipt holds `route`, `admission` (`queue_wait_ms`,
|
|
168
|
+
`resource_wait_ms`, `transition_wait_ms`, `total_wait_ms`), `execution` (placement, observation,
|
|
169
|
+
`resource_admission`, `context_sizing`, `memory_reservation`), `metrics`, `feedback` and
|
|
170
|
+
`response`, which is Ollama's own answer (`message.content`, `eval_count`, durations).
|
|
171
|
+
|
|
172
|
+
A batch nests whole task bodies:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{ "max_parallelism": 2, "tasks": [ { "id": "item-1", "independent": true, "task": { "task": "vision", "model": "my-vision-model", "prompt": "...", "images": ["..."] } } ] }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`POST /sessions` with an empty object returns `session_id`; `POST /warm` takes `{"model": "<tag>"}`.
|
|
179
|
+
|
|
113
180
|
The machine profile is host-derived rather than model-name-derived. It reports total physical
|
|
114
|
-
memory on macOS (`sysctl`), Linux (`/proc`), and Windows (
|
|
181
|
+
memory on macOS (`sysctl`), Linux (`/proc`), and Windows (CIM through PowerShell), along with CPU, OS,
|
|
115
182
|
architecture, and disk. `memory_bytes` is portable host RAM. `unified_memory_bytes` is populated
|
|
116
183
|
only when host and accelerator memory are known to share a pool; it must not be treated as discrete
|
|
117
184
|
GPU VRAM. Ollama's `/api/ps` remains the placement authority.
|
|
118
185
|
|
|
119
186
|
All other paths use the compatibility proxy. It buffers request bodies up to 64 MB so a retry can
|
|
120
|
-
resend identical bytes, then streams the upstream response. It
|
|
187
|
+
resend identical bytes, then streams the upstream response. It removes hop-by-hop headers, adds
|
|
188
|
+
`x-freellama-proxy: 1`, always targets the primary upstream, and does not apply CPU model
|
|
189
|
+
assignment. Existing Ollama clients keep predictable compatibility; clients that opt into the
|
|
190
|
+
managed API receive routing and policy behavior.
|
|
121
191
|
|
|
122
192
|
## Model discovery
|
|
123
193
|
|
|
@@ -131,12 +201,26 @@ flowchart LR
|
|
|
131
201
|
BENCH["Local benchmark report"] --> CAT
|
|
132
202
|
POLICY["Task policy"] --> CAT
|
|
133
203
|
ASSIGN["CPU model assignments"] --> CAT
|
|
134
|
-
|
|
204
|
+
LOOPS["Observed repetition loops"] --> CAT
|
|
205
|
+
CAT --> MODEL["CatalogModel<br/>capabilities · context · residency<br/>benchmark · policy rank · backend · loops"]
|
|
135
206
|
```
|
|
136
207
|
|
|
137
208
|
FreeLlama fetches tags and residency from each configured backend. Models assigned to CPU are
|
|
138
209
|
removed from the primary catalog view and included only from the secondary backend. Static catalog
|
|
139
|
-
metadata is cached for 30 seconds; residency
|
|
210
|
+
metadata is cached for 30 seconds; residency and observed loops are refreshed before routing.
|
|
211
|
+
|
|
212
|
+
Every field comes from the model's own metadata and from what FreeLlama observed, never from its
|
|
213
|
+
name. `GET /_freellama/v1/models` adds, per model:
|
|
214
|
+
|
|
215
|
+
- `context_default`: the context Ollama gives a request that leaves `num_ctx` unset (`source` is
|
|
216
|
+
`modelfile`, `config`, `ollama_context_length`, `vram_tier`, `resident_runner`, or `unknown`). A managed
|
|
217
|
+
task that sends a different `num_ctx` reloads the runner.
|
|
218
|
+
- `resident_context`: the `context_length` of the loaded runner from `/api/ps`.
|
|
219
|
+
- `observed_loops`: per task kind, managed runs since start and how many ended in a repetition
|
|
220
|
+
loop (`{runs, loops}`); process-local, not persisted.
|
|
221
|
+
- `kv_cache_bytes_per_token_f16`: estimated from the declared architecture's block count, KV head
|
|
222
|
+
count and key/value lengths, for any architecture; `null` when the metadata declares recurrent,
|
|
223
|
+
state-space, sliding-window, shared or cross-attention caches, which the dense formula misstates.
|
|
140
224
|
|
|
141
225
|
A failed `/api/show` response skips that model instead of failing the entire catalog. An unreachable
|
|
142
226
|
configured backend fails catalog discovery closed because FreeLlama cannot prove the assigned model's
|
|
@@ -150,7 +234,7 @@ matches exact tags and digest prefixes, preserves local supported features, and
|
|
|
150
234
|
source excerpts and usage notes. Its bounded in-memory cache expires successes after one hour and
|
|
151
235
|
failures after one minute. Failed or unmatched lookups leave local inventory and evidence intact.
|
|
152
236
|
Public claims do not enter the Rust catalog, task policy, quality confidence, or routing decisions.
|
|
153
|
-
See [
|
|
237
|
+
See [Model metadata](MODEL_SELECTION.md#model-metadata) for the public contract and request bounds.
|
|
154
238
|
|
|
155
239
|
## Deterministic routing
|
|
156
240
|
|
|
@@ -164,7 +248,7 @@ flowchart TD
|
|
|
164
248
|
F --> X{"Explicit model?"}
|
|
165
249
|
X -->|"Yes"| E["Use exact model or refuse"]
|
|
166
250
|
X -->|"No"| O{"Objective"}
|
|
167
|
-
O -->|"fastest"| S["Rank local
|
|
251
|
+
O -->|"fastest"| S["Rank local benchmark,<br/>smaller size, and deterministic ties"]
|
|
168
252
|
O -->|"balanced"| B["Prefer policy-qualified candidates;<br/>otherwise use eligible models with low confidence"]
|
|
169
253
|
O -->|"quality"| Q["Require policy-qualified candidates,<br/>then prefer policy order"]
|
|
170
254
|
E --> H["Grade advertised-context compatibility and evidence"]
|
|
@@ -176,8 +260,18 @@ flowchart TD
|
|
|
176
260
|
G -->|"Yes or unset"| D["RouteDecision"]
|
|
177
261
|
```
|
|
178
262
|
|
|
179
|
-
Explicit model selection never substitutes another model.
|
|
180
|
-
|
|
263
|
+
Explicit model selection never substitutes another model. An explicit `model` is read the way
|
|
264
|
+
Ollama reads it: the exact tag, else `name:latest`, else the one installed tag of that family; an
|
|
265
|
+
unknown or ambiguous name is refused with the installed candidates. Every rejected candidate
|
|
266
|
+
includes a reason so callers can distinguish capability, context, policy, and installation failures.
|
|
267
|
+
|
|
268
|
+
Routing never ranks by a model's name or architecture. Automatic selection (fallback ranking and
|
|
269
|
+
session affinity) ranks a model last for a task kind when it has looped on that kind at least twice
|
|
270
|
+
in at most twice as many managed runs; the decision then carries
|
|
271
|
+
`repeats_on_this_task_last_resort`. An operator policy naming the model for the task, or an explicit
|
|
272
|
+
`model`, always wins. No request option is hard-coded for a model: apart from an operator policy's
|
|
273
|
+
per-model `model_options` (`think`, `num_predict`), stop sequences and decoding settings belong to
|
|
274
|
+
the caller.
|
|
181
275
|
|
|
182
276
|
`context_window_fit` means only that requested `num_ctx` fits the model's advertised context
|
|
183
277
|
window. It is not a live-memory, free-VRAM, K/V-cache, thermal, or runner-admission verdict.
|
|
@@ -198,37 +292,12 @@ Confidence is a grade derived from independent evidence dimensions:
|
|
|
198
292
|
`minConfidence: "medium"` refuses before generation when either input is missing. The gate lives in
|
|
199
293
|
`select_route`, so the HTTP API, CLI, MCP server, and embedded core share the same behavior.
|
|
200
294
|
|
|
201
|
-
## Natural-language routing
|
|
202
|
-
|
|
203
|
-
Natural-language routing cannot select a model directly:
|
|
204
|
-
|
|
205
|
-
```mermaid
|
|
206
|
-
sequenceDiagram
|
|
207
|
-
participant C as Client
|
|
208
|
-
participant F as FreeLlama
|
|
209
|
-
participant I as Intent model
|
|
210
|
-
participant R as Deterministic router
|
|
211
|
-
|
|
212
|
-
C->>F: Natural-language request
|
|
213
|
-
F->>I: Strict JSON schema and bounded profile
|
|
214
|
-
I-->>F: Task, objective, context, tool, and vision fields
|
|
215
|
-
F->>F: Apply deterministic guards
|
|
216
|
-
F->>R: Normalized RouteInput
|
|
217
|
-
R-->>F: RouteDecision
|
|
218
|
-
F-->>C: Intent, adjustments, and route
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Word-boundary guards prevent incidental substrings such as `photosynthesis` from creating a vision
|
|
222
|
-
requirement. Explicit requirements in the original text override weak or unsupported inferred fields.
|
|
223
|
-
|
|
224
|
-
The natural route endpoint does not run the selected task. Submit a managed task to execute it.
|
|
225
|
-
|
|
226
295
|
## Managed task execution
|
|
227
296
|
|
|
228
|
-
The implementation separates [admission scheduling](
|
|
229
|
-
[execution coordination](
|
|
230
|
-
[read-only readiness](
|
|
231
|
-
[typed errors](
|
|
297
|
+
The implementation separates [admission scheduling](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/admission.rs),
|
|
298
|
+
[execution coordination](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/execution/mod.rs),
|
|
299
|
+
[read-only readiness](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/readiness.rs), and
|
|
300
|
+
[typed errors](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/error.rs). The platform module wires HTTP endpoints,
|
|
232
301
|
configuration, discovery, and shared state; extracting these owners preserves the public routing API.
|
|
233
302
|
|
|
234
303
|
Managed tasks combine routing with admission and backend-aware transition coordination. With
|
|
@@ -243,7 +312,7 @@ flowchart TD
|
|
|
243
312
|
T["POST /tasks or /task-batches"] --> R["Select eligible model + backend"]
|
|
244
313
|
R --> K["Estimate metadata-backed F16 K/V<br/>(or report unknown)"]
|
|
245
314
|
K --> A{"Acquire selected backend's<br/>3:2:1 fair weighted budget"}
|
|
246
|
-
A -->|"No"| E503["503
|
|
315
|
+
A -->|"No"| E503["429 queue full or<br/>503 wait timeout"]
|
|
247
316
|
A -->|"Yes"| B{"Execution backend"}
|
|
248
317
|
B -->|"Primary"| GL{"GPU model resident<br/>under transition lock?"}
|
|
249
318
|
B -->|"CPU assignment"| CO["Add num_gpu: 0"]
|
|
@@ -271,8 +340,8 @@ falling back to two. The optional CPU pool defaults to one. A saturated GPU burs
|
|
|
271
340
|
consume the permit held for a small CPU helper. With one parallel slot, those values represent one
|
|
272
341
|
ordinary chat and one embedding. Operators can tune both budgets from queue-wait and resident-memory evidence.
|
|
273
342
|
After FreeLlama admission, each Ollama process applies its own `OLLAMA_MAX_QUEUE`, scheduler,
|
|
274
|
-
`OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy requests bypass weighted
|
|
275
|
-
still pass the raw concurrency cap, backend exclusion, and host-pressure gate.
|
|
343
|
+
`OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy generation requests bypass weighted
|
|
344
|
+
admission but still pass the raw concurrency cap, backend exclusion, and host-pressure gate.
|
|
276
345
|
|
|
277
346
|
| Task | Default cost |
|
|
278
347
|
|---|---:|
|
|
@@ -312,9 +381,10 @@ stable ID and `independent:true`; dependent workflow execution is rejected befor
|
|
|
312
381
|
It bounds local dispatch and returns ordered per-item success/error receipts. Every child still
|
|
313
382
|
passes the same global admission and transition path above.
|
|
314
383
|
|
|
315
|
-
The K/V preflight derives single-sequence F16 K+V bytes-per-token
|
|
316
|
-
|
|
317
|
-
|
|
384
|
+
The K/V preflight derives single-sequence F16 K+V bytes-per-token for any architecture whose
|
|
385
|
+
metadata declares the block count and head counts. Explicit key/value dimensions take precedence
|
|
386
|
+
over embedding length divided by heads; sliding-window, shared-KV, recurrent, cross-attention, and
|
|
387
|
+
unknown layouts report unknown. F16 is advisory because quantized KV can
|
|
318
388
|
use less memory. The coarse model-file budget refuses files over 80% of local CPU/unified RAM only
|
|
319
389
|
for loopback backends. Forecasts use each backend's independently attributed parallelism and KV
|
|
320
390
|
cache settings where process inspection is available, with explicit fallback sources otherwise.
|
|
@@ -322,32 +392,45 @@ Process observations refresh on requests after five seconds; measurements made u
|
|
|
322
392
|
settings cannot supply an exact-fit estimate. Ollama remains the live loader and final memory
|
|
323
393
|
authority. Configured settings alone do not prove physical processor placement.
|
|
324
394
|
|
|
325
|
-
The shared [resource governor](
|
|
395
|
+
The shared [resource governor](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/resources/mod.rs) samples local
|
|
326
396
|
available-memory estimates, load per logical CPU, OS memory pressure, active swap-out growth, and
|
|
327
|
-
thermal throttling when reported.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
397
|
+
thermal throttling when reported. OS memory pressure holds every local demand; active swap-out
|
|
398
|
+
growth holds new loads; CPU-load and thermal holds apply to CPU-assigned work and to cold loads
|
|
399
|
+
(decoding weights is CPU work and a new heat source) but not to a bounded request on a runner
|
|
400
|
+
already resident on the GPU, which neither waits for the cores nor cools the package by waiting (`ResourceDemand::is_held_by`; the assessment's
|
|
401
|
+
`held_by` lists the holds that applied). Low available RAM (below the larger of 15% or 1 GiB, clearing after two
|
|
402
|
+
healthy samples above the larger of 20% or 2 GiB) depends on the platform (`ResourcePolicy::low_memory`).
|
|
403
|
+
A new load must leave that reserve free, but never more than its own size above the 1 GiB floor.
|
|
404
|
+
On macOS (`advisory`) the estimate undercounts what the OS can reclaim, so a load that does not cover
|
|
405
|
+
it first unloads idle runners and then dispatches with a `memory_advisory`. On Linux and other
|
|
406
|
+
platforms (`refuse`) a new load that does not fit first unloads idle runners and then waits
|
|
407
|
+
with `held_insufficient_capacity` until a permit release or a fresh sample frees capacity, or refuses
|
|
408
|
+
with 503 at its deadline: Linux `MemAvailable` already includes reclaimable cache and is the hard cap
|
|
409
|
+
in a container, so over-admitting invites the OOM killer. Already-resident runners add no memory and
|
|
410
|
+
are admitted in both modes, also during an active-swapping hold, so a large load never locks out
|
|
411
|
+
reuse of its own runner; swapping still holds new loads. Historical swap occupancy alone is not pressure. Samples are cached for two
|
|
412
|
+
seconds; unavailable signals stay unknown. Remote backends bypass this host gate.
|
|
331
413
|
|
|
332
414
|
Each pressure signal recovers independently after two fresh samples that meet its recovery
|
|
333
|
-
threshold. A recovered CPU-load signal therefore clears even while RAM remains low.
|
|
334
|
-
unchanged resident runners eligible under a memory-only hold while cold loads continue waiting.
|
|
415
|
+
threshold. A recovered CPU-load signal therefore clears even while RAM remains low.
|
|
335
416
|
|
|
336
|
-
|
|
337
|
-
even when a raw request has no model-footprint estimate.
|
|
417
|
+
On Linux and macOS the default `require_memory` telemetry policy holds local inference when
|
|
418
|
+
available RAM is missing, even when a raw request has no model-footprint estimate. Windows and other
|
|
419
|
+
platforms have no host collector, so they default to `best_effort` and admit with
|
|
420
|
+
`admitted_telemetry_unknown`; elsewhere `best_effort` is an explicit opt-in;
|
|
338
421
|
`require_all` additionally requires the collector's applicable load, OS-pressure, and thermal signals.
|
|
339
422
|
Unsupported Linux collector signals remain explicitly unavailable rather than being fabricated.
|
|
340
423
|
The pure `ResourceSnapshot::assess_capacity` calculation is shared by admission and previews.
|
|
341
424
|
Previews use the same footprint lookup, subtract existing reservations, and expose both queue and
|
|
342
|
-
resource readiness.
|
|
343
|
-
|
|
425
|
+
resource readiness. Insufficient model headroom reports `runnable_low_memory_estimate` and admits
|
|
426
|
+
no siblings on macOS; elsewhere it reports `held_insufficient_capacity`. Preview capacity is advisory and conservative, never a reservation.
|
|
344
427
|
|
|
345
428
|
Managed CPU/unified-memory tasks reserve estimated additional model-file and known F16 KV bytes
|
|
346
429
|
across both backends. Verified runner footprints replace that estimate only for the same backend,
|
|
347
430
|
immutable model digest, and equal-or-larger observed context. Exact resident digest/context matches
|
|
348
|
-
avoid double-counting existing allocations. The reservation is rechecked after transition locking
|
|
349
|
-
an increased requirement that no longer fits
|
|
350
|
-
|
|
431
|
+
avoid double-counting existing allocations. The reservation is rechecked after transition locking
|
|
432
|
+
without waiting while holding the backend lock; an increased requirement that no longer fits the
|
|
433
|
+
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
|
|
351
434
|
VRAM discounts the estimated host-memory requirement by the bytes the GPU can hold. Missing VRAM
|
|
352
435
|
telemetry reserves the full estimated footprint in host RAM. The governor cannot constrain external
|
|
353
436
|
allocations or preempt an already-running model.
|
|
@@ -369,11 +452,11 @@ Resident tasks share a backend lock. Nonresident tasks take that backend's exclu
|
|
|
369
452
|
load cannot race another managed task on the same server. CPU and GPU backends have separate locks
|
|
370
453
|
and admission pools, so they can progress independently.
|
|
371
454
|
|
|
372
|
-
In `serve`,
|
|
373
|
-
returning 503 when busy. The response body holds this guard through
|
|
374
|
-
GET/HEAD
|
|
375
|
-
|
|
376
|
-
pressure
|
|
455
|
+
In `serve`, raw generation requests also take the primary backend's exclusive transition lock,
|
|
456
|
+
returning 503 when it stays busy past the raw queue wait. The response body holds this guard through
|
|
457
|
+
EOF or disconnect. Metadata GET/HEAD, `/api/show`, model-store calls (pull, push, copy, delete, and
|
|
458
|
+
blob uploads), and empty `keep_alive:0` unload requests take no lock and bypass pressure. Raw
|
|
459
|
+
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
|
|
377
460
|
returns HTTP 408 and releases its guards without contacting Ollama. Admission receipts report
|
|
378
461
|
`queue_wait_ms`, `resource_wait_ms`, `transition_wait_ms`, and
|
|
379
462
|
their sum as `total_wait_ms`. Health and route receipts expose sampled pressure and reservations.
|
|
@@ -418,35 +501,18 @@ cold-load cost do not poison the comparison. It requires more than a 10% advanta
|
|
|
418
501
|
break a tie before the sample threshold. Quality routing, explicit model selection, and session
|
|
419
502
|
affinity do not follow the latency loop.
|
|
420
503
|
|
|
421
|
-
The same selection function supplies preview
|
|
422
|
-
managed task execution. This keeps advertised placement and actual request routing aligned.
|
|
504
|
+
The same selection function supplies preview receipts and managed task execution. This keeps advertised placement and actual request routing aligned.
|
|
423
505
|
|
|
424
506
|
Raw proxy traffic always uses the primary upstream. For setup and runtime verification, see
|
|
425
507
|
[Run models on CPU and GPU](CPU_GPU_ROUTING.md).
|
|
426
508
|
|
|
427
509
|
## MCP tool flow
|
|
428
510
|
|
|
429
|
-
The MCP server keeps general model work
|
|
430
|
-
operations
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
MC["MCP client"] --> TOOL{"Tool"}
|
|
435
|
-
TOOL -->|"doctor"| NAPI["Native core diagnostics"]
|
|
436
|
-
TOOL -->|"models"| MIX["Control API or direct Ollama/library HTTP"]
|
|
437
|
-
TOOL -->|"run_task"| TASK["Control API route or task"]
|
|
438
|
-
TOOL -->|"task_jobs"| JOB["Deferred receipts and per-task cancellation"]
|
|
439
|
-
TOOL -->|"ollama_manage"| LIFE["Direct Ollama pull or unload"]
|
|
440
|
-
TOOL -->|"ollama_delete"| DELETE["Explicit destructive Ollama delete"]
|
|
441
|
-
TOOL -->|"delegate_research"| AD["Confined adapter subprocess"]
|
|
442
|
-
AD --> FS["Allowlisted workspace"]
|
|
443
|
-
AD --> TASK
|
|
444
|
-
AD --> VER["Answer, citations, usage,<br/>and verification verdict"]
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
The tool split prevents `run_task` from acquiring file access and prevents lifecycle operations from
|
|
448
|
-
being hidden inside a generic action. For schemas and security annotations, see the
|
|
449
|
-
[MCP server reference](../packages/mcp/README.md).
|
|
511
|
+
The MCP server keeps general model work (`run_task`), file-backed research (`delegate_research`, a
|
|
512
|
+
confined adapter subprocess), and destructive lifecycle operations (`ollama_manage`,
|
|
513
|
+
`ollama_delete`) in separate tools, so `run_task` never acquires file access and lifecycle
|
|
514
|
+
operations are not hidden inside a generic action. The canonical tool table, schemas, and security
|
|
515
|
+
annotations are in the [MCP server reference](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md#tools).
|
|
450
516
|
|
|
451
517
|
## Grounded research flow
|
|
452
518
|
|
|
@@ -499,8 +565,8 @@ Adapter cache totals remain null if any turn is unreported, with partial-report
|
|
|
499
565
|
See [Ollama usage](https://docs.ollama.com/api/usage) and
|
|
500
566
|
[the pinned prefix-cache implementation](https://github.com/ggml-org/llama.cpp/blob/74a7c897f049c17e7080423aa2111776eff6ebbf/tools/server/server-context.cpp#L3201).
|
|
501
567
|
|
|
502
|
-
|
|
503
|
-
[
|
|
568
|
+
Adapter implementations and defaults live in
|
|
569
|
+
[`packages/mcp/src/research/`](../packages/mcp/src/research/).
|
|
504
570
|
|
|
505
571
|
## Context and token boundary
|
|
506
572
|
|
|
@@ -519,7 +585,7 @@ flowchart LR
|
|
|
519
585
|
|
|
520
586
|
Raw source, images, and embedding vectors do not need to enter the frontier model's context.
|
|
521
587
|
Embedding vectors are withheld by default because they are large and not human-readable. For the
|
|
522
|
-
measured accounting, see [Token economics](
|
|
588
|
+
measured accounting, see [Token economics](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md#token-economics).
|
|
523
589
|
|
|
524
590
|
## State and failure behavior
|
|
525
591
|
|
|
@@ -543,6 +609,14 @@ expected revision. Warming uses ordinary admission and fit checks; omitted resid
|
|
|
543
609
|
a finite measured policy. See [Scope history and model warming](SCOPES_AND_WARMING.md) for the
|
|
544
610
|
public contract and configured limits.
|
|
545
611
|
|
|
612
|
+
In `serve`, raw generation traffic shares the primary transition lock and receives
|
|
613
|
+
host-pressure gating, while managed tasks also reserve forecast memory. Direct Ollama callers bypass
|
|
614
|
+
these controls. FreeLlama cannot preempt inference or constrain other processes. A configured but
|
|
615
|
+
unreachable CPU backend makes managed catalog discovery fail closed; raw primary passthrough remains
|
|
616
|
+
usable. Runtime feedback is task-specific, token-normalized, and warm-only: it can steer unpinned
|
|
617
|
+
`fastest` or `balanced` work after three samples per backend and a greater-than-10% advantage, but
|
|
618
|
+
cannot change CPU eligibility or steer quality routing.
|
|
619
|
+
|
|
546
620
|
The design fails closed when it cannot establish a required contract. Examples include an
|
|
547
621
|
unreachable configured backend, an unknown confidence grade, a policy without qualified models, an
|
|
548
622
|
explicit model that is not installed, or a task that exceeds the advertised context window.
|
|
@@ -550,8 +624,9 @@ explicit model that is not installed, or a task that exceeds the advertised cont
|
|
|
550
624
|
## Product boundary
|
|
551
625
|
|
|
552
626
|
FreeLlama is not an inference engine, remote provider marketplace, billing layer, hosted model
|
|
553
|
-
registry, general agent runtime, or A2A coordinator. It can authenticate every route
|
|
554
|
-
operator-managed bearer token and refuses unauthenticated nonloopback
|
|
627
|
+
registry, general agent runtime, or A2A coordinator. It can authenticate every route except the
|
|
628
|
+
static UI shell with one operator-managed bearer token and refuses unauthenticated nonloopback
|
|
629
|
+
listeners. An external layer
|
|
555
630
|
must still provide TLS, tenant authorization, tenant-specific limits, and isolation.
|
|
556
631
|
|
|
557
632
|
Use Ollama directly when a caller only needs raw speed from one exact model. Use FreeLlama when the
|