@octocodeai/freellama-mcp-server 0.1.0 → 0.6.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 +108 -0
- package/DEVELOPING.md +109 -0
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +18 -0
- package/README.md +261 -243
- package/assets/logo.jpg +0 -0
- package/dist/index.js +2305 -1332
- package/dist/research/bash-agent.js +2435 -0
- package/dist/research/octocode-agent.js +1639 -0
- package/docs/ARCHITECTURE.md +294 -87
- package/docs/CLI.md +171 -53
- package/docs/CPU_GPU_ROUTING.md +69 -23
- package/docs/MODEL_SELECTION.md +126 -3
- package/docs/MONITORING.md +293 -0
- package/docs/PRODUCTION.md +53 -25
- package/docs/README.md +36 -0
- package/docs/SCOPES_AND_WARMING.md +225 -0
- package/docs/TESTING.md +113 -8
- package/native/index.d.ts +186 -150
- package/native/index.js +3 -3
- package/package.json +15 -11
- package/adapters/agent_context.py +0 -663
- package/adapters/agent_transport.py +0 -84
- package/adapters/bash_agent.py +0 -370
- package/adapters/octocode_agent.py +0 -394
- package/docs/ADR_RESOURCE_AWARE_BACKEND_ROUTING.md +0 -55
- package/docs/ECONOMICS.md +0 -105
- package/docs/INDEX.md +0 -15
- package/docs/OLLAMA_SIDECAR.md +0 -76
- package/docs/OLLAMA_SYSTEM_OPTIMIZATION.md +0 -254
- package/docs/PRODUCT_POSITIONING.md +0 -291
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,25 +1,59 @@
|
|
|
1
1
|
# FreeLlama architecture
|
|
2
2
|
|
|
3
|
-
FreeLlama is
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
FreeLlama is the management layer between an AI agent and local Ollama models. Agents offload
|
|
4
|
+
bounded tasks through MCP, the CLI, or the control API. FreeLlama selects eligible models,
|
|
5
|
+
coordinates resources, and returns results with execution evidence.
|
|
6
|
+
|
|
7
|
+
The calling agent owns task decomposition, prompts, and answer verification. FreeLlama owns managed
|
|
8
|
+
admission, memory checks, residency coordination, and routing within operator-configured CPU/GPU
|
|
9
|
+
backends. Ollama owns model storage, runner loading, inference engines, and token generation;
|
|
10
|
+
the operating system and drivers schedule physical work.
|
|
6
11
|
|
|
7
12
|
## Responsibility boundary
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
This is the canonical ownership rule; other guides link here.
|
|
10
15
|
|
|
11
16
|
| Responsibility | Owner |
|
|
12
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 |
|
|
13
22
|
| Model files, runners, Metal, MLX, llama.cpp, and token generation | Ollama |
|
|
14
23
|
| Native `/api/*` and OpenAI-compatible `/v1/*` semantics | Ollama |
|
|
15
24
|
| Installed-model discovery and capability normalization | FreeLlama |
|
|
16
|
-
| Task routing, advertised-context compatibility, K/V
|
|
17
|
-
| Managed admission, session affinity, and model-transition coordination | FreeLlama |
|
|
25
|
+
| Task routing, advertised-context compatibility, advisory K/V estimates, evidence policy, and model rejection reasons | FreeLlama |
|
|
26
|
+
| Managed admission, session affinity, bounded scope history, and model-transition coordination | FreeLlama |
|
|
27
|
+
| Forecast memory reservations, host-pressure holds, and requests to unload idle unpinned models | FreeLlama managed execution |
|
|
18
28
|
| Explicit per-model CPU/GPU backend assignment | FreeLlama and separate Ollama processes |
|
|
19
29
|
| Grounded research adapters, citations, and verification verdicts | FreeLlama MCP server |
|
|
30
|
+
| Bearer authentication of every route | FreeLlama; external ingress owns TLS and tenant authorization |
|
|
31
|
+
|
|
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
|
+
```
|
|
20
51
|
|
|
21
|
-
FreeLlama
|
|
22
|
-
|
|
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).
|
|
23
57
|
|
|
24
58
|
## System topology
|
|
25
59
|
|
|
@@ -73,10 +107,11 @@ flowchart TD
|
|
|
73
107
|
RAW --> PRIMARY["Primary Ollama"]
|
|
74
108
|
P -->|"Yes"| C{"Control endpoint"}
|
|
75
109
|
C -->|"health, machine, models"| READ["Read local and Ollama state"]
|
|
76
|
-
C -->|"
|
|
77
|
-
C -->|"natural-routes"| INTENT["Run intent model, then route"]
|
|
110
|
+
C -->|"routes"| DECIDE["Decide without inference"]
|
|
78
111
|
C -->|"tasks"| RUN["Route, admit, and execute"]
|
|
79
112
|
C -->|"sessions"| SESSION["Create in-memory affinity id"]
|
|
113
|
+
C -->|"scopes"| SCOPES["Retain revision-protected messages"]
|
|
114
|
+
C -->|"warm"| RUN
|
|
80
115
|
```
|
|
81
116
|
|
|
82
117
|
The control API exposes these endpoints:
|
|
@@ -86,12 +121,56 @@ The control API exposes these endpoints:
|
|
|
86
121
|
| `GET /_freellama/v1/health` | Health, admission capacity, and configured backends | No |
|
|
87
122
|
| `GET /_freellama/v1/machine` | Machine profile | No |
|
|
88
123
|
| `GET /_freellama/v1/models` | Normalized installed catalog, evidence, residency, and execution backend | No |
|
|
89
|
-
| `POST /_freellama/v1/recommendations` | Installed route or reviewed installation plan | No |
|
|
90
124
|
| `POST /_freellama/v1/routes` | Deterministic route decision | No |
|
|
91
|
-
| `POST /_freellama/v1/natural-routes` | Schema-bound intent interpretation followed by deterministic routing | Intent model only |
|
|
92
125
|
| `POST /_freellama/v1/sessions` | New model-affinity session | No |
|
|
93
126
|
| `DELETE /_freellama/v1/sessions/:session_id` | Release model-affinity session | No |
|
|
94
|
-
| `POST /_freellama/v1/
|
|
127
|
+
| `POST /_freellama/v1/scopes` | Create bounded message history | No |
|
|
128
|
+
| `GET /_freellama/v1/scopes/:id` | Scope metadata; optional explicit history inclusion | No |
|
|
129
|
+
| `POST /_freellama/v1/scopes/:id/fork` | Fork history at an expected revision | No |
|
|
130
|
+
| `DELETE /_freellama/v1/scopes/:id` | Delete retained history | No |
|
|
131
|
+
| `POST /_freellama/v1/warm` | Admitted load of an exact installed runner profile | Load only |
|
|
132
|
+
| `POST /_freellama/v1/tasks` | Managed chat, vision, tools, or embedding task (body shape under [Request shapes](#request-shapes)) | Yes |
|
|
133
|
+
| `GET /_freellama/v1/jobs` | Deferred job metadata without prompts or results | No |
|
|
134
|
+
| `GET /_freellama/v1/jobs/:id` | One deferred receipt and retained result | No |
|
|
135
|
+
| `POST /_freellama/v1/jobs/:id/cancel` | Cancel one deferred task and release its local permits | No new inference |
|
|
136
|
+
| `DELETE /_freellama/v1/jobs/:id` | Cancel active work, release local permits, and remove its retained record | No new inference |
|
|
137
|
+
|
|
138
|
+
### Request shapes
|
|
139
|
+
|
|
140
|
+
The control API uses snake_case JSON. A managed task body is the routing fields plus the payload:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"task": "vision", "objective": "fastest", "model": "my-vision-model",
|
|
145
|
+
"session_id": "<uuid from POST /sessions>", "required_capabilities": ["vision"],
|
|
146
|
+
"prompt": "Text Recognition:", "images": ["<base64, no data-URI prefix>"],
|
|
147
|
+
"priority": "background", "keep_alive": "5m", "max_wait_seconds": 30, "timeout_seconds": 120,
|
|
148
|
+
"stop_on_repetition": true,
|
|
149
|
+
"request_options": { "options": { "temperature": 0, "num_predict": 600 }, "format": "json", "think": false }
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`model` is read the way Ollama reads it: an exact installed tag, else `name:latest`, else the one
|
|
154
|
+
installed tag of that family; an unknown or ambiguous name is refused with the installed candidates.
|
|
155
|
+
`messages` (Ollama message objects, images included) replaces `prompt`; `input` serves embeddings;
|
|
156
|
+
`context_tokens` owns `num_ctx` and placement owns `num_gpu`, so neither is accepted inside
|
|
157
|
+
`request_options.options`. `stop_on_repetition: true` streams the chat from Ollama and stops as soon
|
|
158
|
+
as the output repeats its unit twice after the first copy over 96+ characters: the connection
|
|
159
|
+
closes, which cancels generation upstream, and everything generated is kept with
|
|
160
|
+
`done_reason: "repetition"`. An Ollama "token repeat limit" abort is kept the same way
|
|
161
|
+
(`done_reason: "repeat_limit"`). A stopped stream has no Ollama timing fields, so it trains no
|
|
162
|
+
throughput feedback. The receipt holds `route`, `admission` (`queue_wait_ms`,
|
|
163
|
+
`resource_wait_ms`, `transition_wait_ms`, `total_wait_ms`), `execution` (placement, observation,
|
|
164
|
+
`resource_admission`, `context_sizing`, `memory_reservation`), `metrics`, `feedback` and
|
|
165
|
+
`response`, which is Ollama's own answer (`message.content`, `eval_count`, durations).
|
|
166
|
+
|
|
167
|
+
A batch nests whole task bodies:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{ "max_parallelism": 2, "tasks": [ { "id": "item-1", "independent": true, "task": { "task": "vision", "model": "my-vision-model", "prompt": "...", "images": ["..."] } } ] }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`POST /sessions` with an empty object returns `session_id`; `POST /warm` takes `{"model": "<tag>"}`.
|
|
95
174
|
|
|
96
175
|
The machine profile is host-derived rather than model-name-derived. It reports total physical
|
|
97
176
|
memory on macOS (`sysctl`), Linux (`/proc`), and Windows (system APIs), along with CPU, OS,
|
|
@@ -100,7 +179,10 @@ only when host and accelerator memory are known to share a pool; it must not be
|
|
|
100
179
|
GPU VRAM. Ollama's `/api/ps` remains the placement authority.
|
|
101
180
|
|
|
102
181
|
All other paths use the compatibility proxy. It buffers request bodies up to 64 MB so a retry can
|
|
103
|
-
resend identical bytes, then streams the upstream response. It
|
|
182
|
+
resend identical bytes, then streams the upstream response. It removes hop-by-hop headers, adds
|
|
183
|
+
`x-freellama-proxy: 1`, always targets the primary upstream, and does not apply CPU model
|
|
184
|
+
assignment. Existing Ollama clients keep predictable compatibility; clients that opt into the
|
|
185
|
+
managed API receive routing and policy behavior.
|
|
104
186
|
|
|
105
187
|
## Model discovery
|
|
106
188
|
|
|
@@ -114,20 +196,44 @@ flowchart LR
|
|
|
114
196
|
BENCH["Local benchmark report"] --> CAT
|
|
115
197
|
POLICY["Task policy"] --> CAT
|
|
116
198
|
ASSIGN["CPU model assignments"] --> CAT
|
|
117
|
-
|
|
199
|
+
LOOPS["Observed repetition loops"] --> CAT
|
|
200
|
+
CAT --> MODEL["CatalogModel<br/>capabilities · context · residency<br/>benchmark · policy rank · backend · loops"]
|
|
118
201
|
```
|
|
119
202
|
|
|
120
203
|
FreeLlama fetches tags and residency from each configured backend. Models assigned to CPU are
|
|
121
204
|
removed from the primary catalog view and included only from the secondary backend. Static catalog
|
|
122
|
-
metadata is cached for 30 seconds; residency
|
|
205
|
+
metadata is cached for 30 seconds; residency and observed loops are refreshed before routing.
|
|
206
|
+
|
|
207
|
+
Every field comes from the model's own metadata and from what FreeLlama observed, never from its
|
|
208
|
+
name. `GET /_freellama/v1/models` adds, per model:
|
|
209
|
+
|
|
210
|
+
- `context_default`: the context Ollama gives a request that leaves `num_ctx` unset (`source` is
|
|
211
|
+
`modelfile`, `config`, `ollama_context_length`, `vram_tier`, or `resident_runner`). A managed
|
|
212
|
+
task that sends a different `num_ctx` reloads the runner.
|
|
213
|
+
- `resident_context`: the `context_length` of the loaded runner from `/api/ps`.
|
|
214
|
+
- `observed_loops`: per task kind, managed runs since start and how many ended in a repetition
|
|
215
|
+
loop (`{runs, loops}`); process-local, not persisted.
|
|
216
|
+
- `kv_cache_bytes_per_token_f16`: estimated from the declared architecture's block count, KV head
|
|
217
|
+
count and key/value lengths, for any architecture; `null` when the metadata declares recurrent,
|
|
218
|
+
state-space, sliding-window, shared or cross-attention caches, which the dense formula misstates.
|
|
123
219
|
|
|
124
220
|
A failed `/api/show` response skips that model instead of failing the entire catalog. An unreachable
|
|
125
221
|
configured backend fails catalog discovery closed because FreeLlama cannot prove the assigned model's
|
|
126
222
|
state.
|
|
127
223
|
|
|
224
|
+
### Public model metadata
|
|
225
|
+
|
|
226
|
+
Public model enrichment belongs to the MCP layer's `model-knowledge.ts` and `model-search.ts`.
|
|
227
|
+
Installed/detail requests can opt into GET-only Ollama family and full tag-page lookups. The layer
|
|
228
|
+
matches exact tags and digest prefixes, preserves local supported features, and attaches family-scoped
|
|
229
|
+
source excerpts and usage notes. Its bounded in-memory cache expires successes after one hour and
|
|
230
|
+
failures after one minute. Failed or unmatched lookups leave local inventory and evidence intact.
|
|
231
|
+
Public claims do not enter the Rust catalog, task policy, quality confidence, or routing decisions.
|
|
232
|
+
See [Model metadata](MODEL_SELECTION.md#model-metadata) for the public contract and request bounds.
|
|
233
|
+
|
|
128
234
|
## Deterministic routing
|
|
129
235
|
|
|
130
|
-
Routing
|
|
236
|
+
Routing defaults to ordinary `completion`; explicit profiles refine selection. Supported task kinds are `completion`, `coding`,
|
|
131
237
|
`code_repair`, `tools`, `browser`, `vision`, `embedding`, and `long_context`.
|
|
132
238
|
|
|
133
239
|
```mermaid
|
|
@@ -138,7 +244,7 @@ flowchart TD
|
|
|
138
244
|
X -->|"Yes"| E["Use exact model or refuse"]
|
|
139
245
|
X -->|"No"| O{"Objective"}
|
|
140
246
|
O -->|"fastest"| S["Rank local performance,<br/>residency, and deterministic ties"]
|
|
141
|
-
O -->|"balanced"| B["
|
|
247
|
+
O -->|"balanced"| B["Prefer policy-qualified candidates;<br/>otherwise use eligible models with low confidence"]
|
|
142
248
|
O -->|"quality"| Q["Require policy-qualified candidates,<br/>then prefer policy order"]
|
|
143
249
|
E --> H["Grade advertised-context compatibility and evidence"]
|
|
144
250
|
S --> H
|
|
@@ -149,12 +255,21 @@ flowchart TD
|
|
|
149
255
|
G -->|"Yes or unset"| D["RouteDecision"]
|
|
150
256
|
```
|
|
151
257
|
|
|
152
|
-
Explicit model selection never substitutes another model.
|
|
153
|
-
|
|
258
|
+
Explicit model selection never substitutes another model. An explicit `model` is read the way
|
|
259
|
+
Ollama reads it: the exact tag, else `name:latest`, else the one installed tag of that family; an
|
|
260
|
+
unknown or ambiguous name is refused with the installed candidates. Every rejected candidate
|
|
261
|
+
includes a reason so callers can distinguish capability, context, policy, and installation failures.
|
|
262
|
+
|
|
263
|
+
Routing never ranks by a model's name or architecture. Automatic selection (fallback ranking and
|
|
264
|
+
session affinity) ranks a model last for a task kind when it has looped on that kind at least twice
|
|
265
|
+
in at most twice as many managed runs; the decision then carries
|
|
266
|
+
`repeats_on_this_task_last_resort`. An operator policy naming the model for the task, or an explicit
|
|
267
|
+
`model`, always wins. No request option is added because of which model was chosen: stop sequences
|
|
268
|
+
and decoding settings belong to the caller.
|
|
154
269
|
|
|
155
270
|
`context_window_fit` means only that requested `num_ctx` fits the model's advertised context
|
|
156
271
|
window. It is not a live-memory, free-VRAM, K/V-cache, thermal, or runner-admission verdict.
|
|
157
|
-
Execution separately reports
|
|
272
|
+
Execution separately reports an advisory model-metadata K/V estimate and post-run
|
|
158
273
|
placement evidence; Ollama remains authoritative for actual runner allocation and admission.
|
|
159
274
|
|
|
160
275
|
### Confidence evidence
|
|
@@ -171,39 +286,25 @@ Confidence is a grade derived from independent evidence dimensions:
|
|
|
171
286
|
`minConfidence: "medium"` refuses before generation when either input is missing. The gate lives in
|
|
172
287
|
`select_route`, so the HTTP API, CLI, MCP server, and embedded core share the same behavior.
|
|
173
288
|
|
|
174
|
-
## Natural-language routing
|
|
175
|
-
|
|
176
|
-
Natural-language routing cannot select a model directly:
|
|
177
|
-
|
|
178
|
-
```mermaid
|
|
179
|
-
sequenceDiagram
|
|
180
|
-
participant C as Client
|
|
181
|
-
participant F as FreeLlama
|
|
182
|
-
participant I as Intent model
|
|
183
|
-
participant R as Deterministic router
|
|
184
|
-
|
|
185
|
-
C->>F: Natural-language request
|
|
186
|
-
F->>I: Strict JSON schema and bounded profile
|
|
187
|
-
I-->>F: Task, objective, context, tool, and vision fields
|
|
188
|
-
F->>F: Apply deterministic guards
|
|
189
|
-
F->>R: Normalized RouteInput
|
|
190
|
-
R-->>F: RouteDecision
|
|
191
|
-
F-->>C: Intent, adjustments, and route
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Word-boundary guards prevent incidental substrings such as `photosynthesis` from creating a vision
|
|
195
|
-
requirement. Explicit requirements in the original text override weak or unsupported inferred fields.
|
|
196
|
-
|
|
197
|
-
The natural route endpoint does not run the selected task. Submit a managed task to execute it.
|
|
198
|
-
|
|
199
289
|
## Managed task execution
|
|
200
290
|
|
|
201
|
-
|
|
291
|
+
The implementation separates [admission scheduling](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/admission.rs),
|
|
292
|
+
[execution coordination](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/execution/mod.rs),
|
|
293
|
+
[read-only readiness](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/readiness.rs), and
|
|
294
|
+
[typed errors](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/error.rs). The platform module wires HTTP endpoints,
|
|
295
|
+
configuration, discovery, and shared state; extracting these owners preserves the public routing API.
|
|
296
|
+
|
|
297
|
+
Managed tasks combine routing with admission and backend-aware transition coordination. With
|
|
298
|
+
`defer:true`, the server returns HTTP `202` and a process-local job handle, then runs the same task
|
|
299
|
+
path. The bounded registry owns receipts and cancellation, while the existing governor owns
|
|
300
|
+
admission. Batch items cannot defer. Per-task `timeout_seconds` bounds discovery, waiting, loading,
|
|
301
|
+
and inference together, capped by the server task timeout. See [CLI controls](CLI.md#execute-tasks)
|
|
302
|
+
for retention limits and [monitoring](MONITORING.md) for the observable lifecycle.
|
|
202
303
|
|
|
203
304
|
```mermaid
|
|
204
305
|
flowchart TD
|
|
205
306
|
T["POST /tasks or /task-batches"] --> R["Select eligible model + backend"]
|
|
206
|
-
R --> K["
|
|
307
|
+
R --> K["Estimate metadata-backed F16 K/V<br/>(or report unknown)"]
|
|
207
308
|
K --> A{"Acquire selected backend's<br/>3:2:1 fair weighted budget"}
|
|
208
309
|
A -->|"No"| E503["503 server busy"]
|
|
209
310
|
A -->|"Yes"| B{"Execution backend"}
|
|
@@ -219,7 +320,7 @@ flowchart TD
|
|
|
219
320
|
CR --> OQ
|
|
220
321
|
CX --> OQ
|
|
221
322
|
OQ --> SEND["Ollama schedules bounded request"]
|
|
222
|
-
SEND --> RETRY{"500, 502
|
|
323
|
+
SEND --> RETRY{"500, 502,<br/>or connection-establishment failure?"}
|
|
223
324
|
RETRY -->|"Retryable"| OQ
|
|
224
325
|
RETRY -->|"Success"| OBS["Query selected Ollama /api/ps"]
|
|
225
326
|
OBS --> OUT["Route, assignment, physical observation,<br/>admission, metrics, and response"]
|
|
@@ -228,40 +329,130 @@ flowchart TD
|
|
|
228
329
|
|
|
229
330
|
Admission is independent per backend and uses weighted fair round robin: interactive gets three
|
|
230
331
|
turns, normal two, and background one, preserving FIFO inside each class and preventing starvation.
|
|
231
|
-
The primary/GPU pool defaults to two weighted units
|
|
232
|
-
optional CPU pool defaults to one. A saturated GPU burst therefore cannot
|
|
233
|
-
for a small CPU helper.
|
|
234
|
-
|
|
332
|
+
The primary/GPU pool defaults to two weighted units per observed `OLLAMA_NUM_PARALLEL` slot,
|
|
333
|
+
falling back to two. The optional CPU pool defaults to one. A saturated GPU burst therefore cannot
|
|
334
|
+
consume the permit held for a small CPU helper. With one parallel slot, those values represent one
|
|
335
|
+
ordinary chat and one embedding. Operators can tune both budgets from queue-wait and resident-memory evidence.
|
|
235
336
|
After FreeLlama admission, each Ollama process applies its own `OLLAMA_MAX_QUEUE`, scheduler,
|
|
236
|
-
`OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy requests bypass
|
|
237
|
-
|
|
337
|
+
`OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy requests bypass weighted admission but
|
|
338
|
+
still pass the raw concurrency cap, backend exclusion, and host-pressure gate.
|
|
238
339
|
|
|
239
|
-
| Task |
|
|
340
|
+
| Task | Default cost |
|
|
240
341
|
|---|---:|
|
|
241
342
|
| Embedding | `ceil(input_items / 4)` |
|
|
242
343
|
| Chat, coding, tools, browser, or long context | 2 |
|
|
243
344
|
| Vision | 4 |
|
|
244
345
|
|
|
346
|
+
The runtime file's `[task_costs]` table overrides task-kind base costs. Embeddings multiply their
|
|
347
|
+
base by `ceil(input_items / 4)`; the acquired charge is capped to the backend's current capacity.
|
|
348
|
+
See [monitoring controls](MONITORING.md#adaptive-concurrency) for validation and reload behavior.
|
|
349
|
+
|
|
350
|
+
After six capacity bypasses, the scheduler reserves released capacity for the oldest blocked waiter,
|
|
351
|
+
so a stream of small requests cannot indefinitely starve a larger request. Cancellation releases
|
|
352
|
+
that reservation. Admission, resource waiting, and transition locking share one queue deadline.
|
|
353
|
+
|
|
354
|
+
A task waiting for host resources returns its execution slot after two seconds and continues
|
|
355
|
+
waiting within the same deadline. It remains counted against the backend's bounded queue, so
|
|
356
|
+
memory pressure cannot create an invisible backlog. Health reports slot and resource waiters
|
|
357
|
+
separately. Returning to slot admission transfers the existing queue registration atomically;
|
|
358
|
+
new arrivals and a smaller reloaded queue limit cannot displace accepted work. Expired deadlines
|
|
359
|
+
prevent admission even when an execution slot is free. Cancellation removes either waiter, and releasing a memory reservation wakes resource
|
|
360
|
+
waiters immediately; host telemetry still obeys the sampling and recovery policy below.
|
|
361
|
+
|
|
245
362
|
FreeLlama acquires the admission slot before a transition lock to avoid deadlock. A model marked
|
|
246
363
|
resident during discovery is checked again while the shared transition lock is held; stale or
|
|
247
364
|
unavailable residency falls back to the exclusive transition path. Session affinity is bound only
|
|
248
365
|
after successful upstream execution, so refused and failed tasks cannot change later routing.
|
|
366
|
+
An explicit `done:false` chat response or a reported upstream error is a failure, even with HTTP 200.
|
|
367
|
+
It cannot train runtime feedback or bind affinity. Completed failed exchanges retain the raw response
|
|
368
|
+
and honor a requested immediate unload. Transport failures also attempt that unload and return its
|
|
369
|
+
lifecycle receipt, but a disconnected inference response alone does not prove upstream cancellation.
|
|
370
|
+
Resource failures return a readable `error`, a stable `code`, and structured `resource_admission`
|
|
371
|
+
data, including per-item batch failures.
|
|
249
372
|
|
|
250
373
|
`POST /_freellama/v1/task-batches` is for caller-declared independent work only: every item has a
|
|
251
374
|
stable ID and `independent:true`; dependent workflow execution is rejected before any upstream call.
|
|
252
375
|
It bounds local dispatch and returns ordered per-item success/error receipts. Every child still
|
|
253
376
|
passes the same global admission and transition path above.
|
|
254
377
|
|
|
255
|
-
The K/V preflight derives F16 K+V bytes-per-token only
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
378
|
+
The K/V preflight derives single-sequence F16 K+V bytes-per-token only for supported model
|
|
379
|
+
architectures with sufficient metadata. Explicit key/value dimensions take precedence; sliding-window,
|
|
380
|
+
shared-KV, recurrent, and unknown layouts report unknown. F16 is advisory because quantized KV can
|
|
381
|
+
use less memory. The coarse model-file budget refuses files over 80% of local CPU/unified RAM only
|
|
382
|
+
for loopback backends. Forecasts use each backend's independently attributed parallelism and KV
|
|
383
|
+
cache settings where process inspection is available, with explicit fallback sources otherwise.
|
|
384
|
+
Process observations refresh on requests after five seconds; measurements made under different
|
|
385
|
+
settings cannot supply an exact-fit estimate. Ollama remains the live loader and final memory
|
|
386
|
+
authority. Configured settings alone do not prove physical processor placement.
|
|
387
|
+
|
|
388
|
+
The shared [resource governor](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/resources/mod.rs) samples local
|
|
389
|
+
available-memory estimates, load per logical CPU, OS memory pressure, active swap-out growth, and
|
|
390
|
+
thermal throttling when reported. OS memory pressure holds every local demand; active swap-out
|
|
391
|
+
growth holds new loads; CPU-load and thermal holds apply to CPU-assigned work and to cold loads
|
|
392
|
+
(decoding weights is CPU work and a new heat source) but not to a bounded request on a runner
|
|
393
|
+
already resident on the GPU, which neither waits for the cores nor cools the package by waiting (`ResourceDemand::is_held_by`; the assessment's
|
|
394
|
+
`held_by` lists the holds that applied). Low available RAM (below the larger of 15% or 1 GiB, clearing after two
|
|
395
|
+
healthy samples above the larger of 20% or 2 GiB) depends on the platform (`ResourcePolicy::low_memory`).
|
|
396
|
+
On macOS (`advisory`) the estimate undercounts what the OS can reclaim, so a load that does not cover
|
|
397
|
+
it first unloads idle runners and then dispatches with a `memory_advisory`. On Linux, Windows, and
|
|
398
|
+
other platforms (`refuse`) a new load that does not fit first unloads idle runners and then waits
|
|
399
|
+
with `held_insufficient_capacity` until a permit release or a fresh sample frees capacity, or refuses
|
|
400
|
+
with 503 at its deadline: Linux `MemAvailable` already includes reclaimable cache and is the hard cap
|
|
401
|
+
in a container, so over-admitting invites the OOM killer. Already-resident runners add no memory and
|
|
402
|
+
are admitted in both modes, also during an active-swapping hold, so a large load never locks out
|
|
403
|
+
reuse of its own runner; swapping still holds new loads. Historical swap occupancy alone is not pressure. Samples are cached for two
|
|
404
|
+
seconds; unavailable signals stay unknown. Remote backends bypass this host gate.
|
|
405
|
+
|
|
406
|
+
Each pressure signal recovers independently after two fresh samples that meet its recovery
|
|
407
|
+
threshold. A recovered CPU-load signal therefore clears even while RAM remains low.
|
|
408
|
+
|
|
409
|
+
The default `require_memory` telemetry policy holds local inference when available RAM is missing,
|
|
410
|
+
even when a raw request has no model-footprint estimate. `best_effort` is an explicit opt-in;
|
|
411
|
+
`require_all` additionally requires the collector's applicable load, OS-pressure, and thermal signals.
|
|
412
|
+
Unsupported Linux collector signals remain explicitly unavailable rather than being fabricated.
|
|
413
|
+
The pure `ResourceSnapshot::assess_capacity` calculation is shared by admission and previews.
|
|
414
|
+
Previews use the same footprint lookup, subtract existing reservations, and expose both queue and
|
|
415
|
+
resource readiness. Insufficient model headroom reports `runnable_low_memory_estimate` and admits
|
|
416
|
+
no siblings on macOS; elsewhere it reports `held_insufficient_capacity`. Preview capacity is advisory and conservative, never a reservation.
|
|
417
|
+
|
|
418
|
+
Managed CPU/unified-memory tasks reserve estimated additional model-file and known F16 KV bytes
|
|
419
|
+
across both backends. Verified runner footprints replace that estimate only for the same backend,
|
|
420
|
+
immutable model digest, and equal-or-larger observed context. Exact resident digest/context matches
|
|
421
|
+
avoid double-counting existing allocations. The reservation is rechecked after transition locking
|
|
422
|
+
without waiting while holding the backend lock; an increased requirement that no longer fits the
|
|
423
|
+
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
|
|
424
|
+
VRAM discounts the estimated host-memory requirement by the bytes the GPU can hold. Missing VRAM
|
|
425
|
+
telemetry reserves the full estimated footprint in host RAM. The governor cannot constrain external
|
|
426
|
+
allocations or preempt an already-running model.
|
|
427
|
+
|
|
428
|
+
Managed text tasks without explicit `context_tokens` select the smallest 2K/4K/8K/16K/32K bucket
|
|
429
|
+
that covers the UTF-8 prompt/schema estimate, output reserve, and 512-token template margin, bounded
|
|
430
|
+
by the selected model's advertised limit. Larger requests require explicit context or compaction.
|
|
431
|
+
Multimodal and embedding requests retain their profiles. The execution receipt exposes
|
|
432
|
+
`context_sizing`; previews contain routing fields only and therefore retain task defaults.
|
|
433
|
+
Managed chat sends `truncate:false` and `shift:false` so supporting Ollama backends refuse overflow
|
|
434
|
+
instead of silently losing earlier instructions. Managed embeddings send `truncate:false` too.
|
|
435
|
+
Only the dedicated CPU backend supplies a thread default: half the logical CPU count on other
|
|
436
|
+
platforms, while macOS leaves Ollama to select its performance cores. `FREELLAMA_CPU_NUM_THREAD`
|
|
437
|
+
overrides that default; explicit request options take precedence. The primary backend leaves thread
|
|
438
|
+
selection to Ollama to avoid changing the options of a shared runner. `runtime_options` reports
|
|
439
|
+
forwarded values; runner support is not assumed.
|
|
259
440
|
|
|
260
441
|
Resident tasks share a backend lock. Nonresident tasks take that backend's exclusive lock so a cold
|
|
261
442
|
load cannot race another managed task on the same server. CPU and GPU backends have separate locks
|
|
262
443
|
and admission pools, so they can progress independently.
|
|
263
444
|
|
|
264
|
-
|
|
445
|
+
In `serve`, mutating raw proxy requests also take the primary backend's exclusive transition lock,
|
|
446
|
+
returning 503 when busy. The response body holds this guard through EOF or disconnect. Metadata
|
|
447
|
+
GET/HEAD and `/api/show` requests remain available. Raw inference waits at most 500 ms for host
|
|
448
|
+
pressure to clear, then returns 503 with `Retry-After`; empty `keep_alive:0` unload requests bypass
|
|
449
|
+
pressure. Raw request-body collection has the configured request deadline too; an incomplete upload
|
|
450
|
+
returns HTTP 408 and releases its guards without contacting Ollama. Admission receipts report
|
|
451
|
+
`queue_wait_ms`, `resource_wait_ms`, `transition_wait_ms`, and
|
|
452
|
+
their sum as `total_wait_ms`. Health and route receipts expose sampled pressure and reservations.
|
|
453
|
+
|
|
454
|
+
HTTP `500` and `502` status codes and connection-establishment failures use bounded retry with backoff.
|
|
455
|
+
HTTP `504`, transport timeouts and post-send disconnects are not replayed.
|
|
265
456
|
FreeLlama does not retry `503 Service Unavailable` because retrying while holding admission can
|
|
266
457
|
amplify overload. It does not retry a generation timeout because the first request might still run.
|
|
267
458
|
|
|
@@ -300,34 +491,18 @@ cold-load cost do not poison the comparison. It requires more than a 10% advanta
|
|
|
300
491
|
break a tie before the sample threshold. Quality routing, explicit model selection, and session
|
|
301
492
|
affinity do not follow the latency loop.
|
|
302
493
|
|
|
303
|
-
The same selection function supplies preview
|
|
304
|
-
managed task execution. This keeps advertised placement and actual request routing aligned.
|
|
494
|
+
The same selection function supplies preview receipts and managed task execution. This keeps advertised placement and actual request routing aligned.
|
|
305
495
|
|
|
306
496
|
Raw proxy traffic always uses the primary upstream. For setup and runtime verification, see
|
|
307
497
|
[Run models on CPU and GPU](CPU_GPU_ROUTING.md).
|
|
308
498
|
|
|
309
499
|
## MCP tool flow
|
|
310
500
|
|
|
311
|
-
The MCP server keeps general model work
|
|
312
|
-
operations
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
MC["MCP client"] --> TOOL{"Tool"}
|
|
317
|
-
TOOL -->|"doctor"| NAPI["Native core diagnostics"]
|
|
318
|
-
TOOL -->|"models"| MIX["Control API or direct Ollama/library HTTP"]
|
|
319
|
-
TOOL -->|"run_task"| TASK["Control API route or task"]
|
|
320
|
-
TOOL -->|"ollama_manage"| LIFE["Direct Ollama pull or unload"]
|
|
321
|
-
TOOL -->|"ollama_delete"| DELETE["Explicit destructive Ollama delete"]
|
|
322
|
-
TOOL -->|"delegate_research"| AD["Confined adapter subprocess"]
|
|
323
|
-
AD --> FS["Allowlisted workspace"]
|
|
324
|
-
AD --> TASK
|
|
325
|
-
AD --> VER["Answer, citations, usage,<br/>and verification verdict"]
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
The tool split prevents `run_task` from acquiring file access and prevents lifecycle operations from
|
|
329
|
-
being hidden inside a generic action. For schemas and security annotations, see the
|
|
330
|
-
[MCP server reference](../packages/mcp/README.md).
|
|
501
|
+
The MCP server keeps general model work (`run_task`), file-backed research (`delegate_research`, a
|
|
502
|
+
confined adapter subprocess), and destructive lifecycle operations (`ollama_manage`,
|
|
503
|
+
`ollama_delete`) in separate tools, so `run_task` never acquires file access and lifecycle
|
|
504
|
+
operations are not hidden inside a generic action. The canonical tool table, schemas, and security
|
|
505
|
+
annotations are in the [MCP server reference](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md#tools).
|
|
331
506
|
|
|
332
507
|
## Grounded research flow
|
|
333
508
|
|
|
@@ -355,14 +530,33 @@ shape.
|
|
|
355
530
|
|
|
356
531
|
The adapter loop preserves its control contract through four mechanisms:
|
|
357
532
|
|
|
358
|
-
- Pagination keeps complete output
|
|
533
|
+
- Pagination keeps complete output in adapter memory and writes it into the final result file,
|
|
534
|
+
while showing bounded pages. Oversized lines split losslessly; the final page ends pagination.
|
|
359
535
|
- Context fitting byte-preserves the system prompt and question by default, compacts older
|
|
360
536
|
observations, and fails before Ollama if pinned content cannot fit. The first estimate is
|
|
361
537
|
configurable; real `prompt_eval_count` values calibrate subsequent fits conservatively.
|
|
362
538
|
- JSON repair retains gathered evidence across a bounded format correction.
|
|
363
539
|
- Repeat suppression serves an identical earlier result without rerunning the subprocess command.
|
|
364
540
|
|
|
365
|
-
|
|
541
|
+
Overflow compaction targets 90% of the input budget using older observations, preserving head/tail
|
|
542
|
+
evidence and exact stored-page recovery actions. Already-fitting prefixes and existing breadcrumbs
|
|
543
|
+
remain unchanged. Compact observation ledgers preserve action, status, source, step, page recovery,
|
|
544
|
+
and untrusted-output provenance. Full observations and page offsets are stored in temporary files;
|
|
545
|
+
only the requested page enters the conversation. Final audit JSON streams the full results from disk
|
|
546
|
+
and is atomically replaced. Temporary files are cleaned on normal and exceptional exits.
|
|
547
|
+
Calibration keys include the endpoint, exact model tag and verified manifest digest, system-prompt
|
|
548
|
+
hash, estimator version, and settings. Unknown model identity disables persisted calibration while
|
|
549
|
+
retaining process-local calibration. Adapter retries cover explicit
|
|
550
|
+
503 overload and connection refusal, never uncertain timeouts or post-send disconnects.
|
|
551
|
+
|
|
552
|
+
Ollama's optional `prompt_eval_cached_count` is a subset of total `prompt_eval_count`. Prefill rate
|
|
553
|
+
uses uncached tokens only; unavailable cache counts produce null instead of invented zero hits.
|
|
554
|
+
Adapter cache totals remain null if any turn is unreported, with partial-report coverage in metadata.
|
|
555
|
+
See [Ollama usage](https://docs.ollama.com/api/usage) and
|
|
556
|
+
[the pinned prefix-cache implementation](https://github.com/ggml-org/llama.cpp/blob/74a7c897f049c17e7080423aa2111776eff6ebbf/tools/server/server-context.cpp#L3201).
|
|
557
|
+
|
|
558
|
+
Adapter implementations and defaults live in
|
|
559
|
+
[`packages/mcp/src/research/`](../packages/mcp/src/research/).
|
|
366
560
|
|
|
367
561
|
## Context and token boundary
|
|
368
562
|
|
|
@@ -381,7 +575,7 @@ flowchart LR
|
|
|
381
575
|
|
|
382
576
|
Raw source, images, and embedding vectors do not need to enter the frontier model's context.
|
|
383
577
|
Embedding vectors are withheld by default because they are large and not human-readable. For the
|
|
384
|
-
measured accounting, see [Token economics](
|
|
578
|
+
measured accounting, see [Token economics](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md#token-economics).
|
|
385
579
|
|
|
386
580
|
## State and failure behavior
|
|
387
581
|
|
|
@@ -390,6 +584,7 @@ FreeLlama keeps these values in memory:
|
|
|
390
584
|
- Catalog metadata cache
|
|
391
585
|
- Current residency snapshots
|
|
392
586
|
- Session-to-model affinity
|
|
587
|
+
- Revision-protected scope message history and bounded adaptive residency measurements
|
|
393
588
|
- Independent GPU and CPU priority-fair weighted admission pools
|
|
394
589
|
- Per-task normalized warm latency and queue feedback for each backend, restored from an optional
|
|
395
590
|
versioned atomic snapshot
|
|
@@ -398,7 +593,19 @@ FreeLlama keeps these values in memory:
|
|
|
398
593
|
Sessions are bounded (default 1024) and expire after idle time (default one hour); they store only
|
|
399
594
|
model affinity, never prompts or Ollama KV. Restarting `freellama serve` clears sessions and the catalog cache. Persisted placement feedback is
|
|
400
595
|
reloaded; `--ephemeral-feedback` intentionally resets it. Ollama owns model residency, so loaded
|
|
401
|
-
models survive a FreeLlama restart.
|
|
596
|
+
models survive a FreeLlama restart. Scope histories and their revisions do not survive restart.
|
|
597
|
+
They store messages independently from affinity and append only a successful generation at the
|
|
598
|
+
expected revision. Warming uses ordinary admission and fit checks; omitted residency controls use
|
|
599
|
+
a finite measured policy. See [Scope history and model warming](SCOPES_AND_WARMING.md) for the
|
|
600
|
+
public contract and configured limits.
|
|
601
|
+
|
|
602
|
+
In `serve`, mutating passthrough traffic shares the primary transition lock and receives
|
|
603
|
+
host-pressure gating, while managed tasks also reserve forecast memory. Direct Ollama callers bypass
|
|
604
|
+
these controls. FreeLlama cannot preempt inference or constrain other processes. A configured but
|
|
605
|
+
unreachable CPU backend makes managed catalog discovery fail closed; raw primary passthrough remains
|
|
606
|
+
usable. Runtime feedback is task-specific, token-normalized, and warm-only: it can steer unpinned
|
|
607
|
+
`fastest` or `balanced` work after three samples per backend and a greater-than-10% advantage, but
|
|
608
|
+
cannot change CPU eligibility or steer quality routing.
|
|
402
609
|
|
|
403
610
|
The design fails closed when it cannot establish a required contract. Examples include an
|
|
404
611
|
unreachable configured backend, an unknown confidence grade, a policy without qualified models, an
|