@octocodeai/freellama-mcp-server 0.1.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/README.md +426 -0
- package/adapters/agent_context.py +663 -0
- package/adapters/agent_transport.py +84 -0
- package/adapters/bash_agent.py +370 -0
- package/adapters/octocode_agent.py +394 -0
- package/dist/index.js +22971 -0
- package/docs/ADR_RESOURCE_AWARE_BACKEND_ROUTING.md +55 -0
- package/docs/ARCHITECTURE.md +416 -0
- package/docs/CLI.md +219 -0
- package/docs/CPU_GPU_ROUTING.md +352 -0
- package/docs/ECONOMICS.md +105 -0
- package/docs/INDEX.md +15 -0
- package/docs/MODEL_SELECTION.md +65 -0
- package/docs/OLLAMA_SIDECAR.md +76 -0
- package/docs/OLLAMA_SYSTEM_OPTIMIZATION.md +254 -0
- package/docs/PRODUCTION.md +195 -0
- package/docs/PRODUCT_POSITIONING.md +291 -0
- package/docs/TESTING.md +125 -0
- package/native/index.d.ts +158 -0
- package/native/index.js +45 -0
- package/native/package.json +7 -0
- package/package.json +69 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ADR: Resource-aware backend routing
|
|
2
|
+
|
|
3
|
+
Status: Accepted and implemented
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
FreeLlama can isolate explicitly assigned helper models in a second Ollama process. Live trials
|
|
8
|
+
proved that CPU and GPU requests can overlap, but exact operator assignment alone was too rigid for
|
|
9
|
+
agents, and one global admission pool can let GPU work starve a CPU helper. Unconstrained
|
|
10
|
+
automatic migration is unsafe because capability, quality, session, and memory constraints
|
|
11
|
+
must continue to win.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
Keep backend eligibility operator-owned and add three bounded controls:
|
|
16
|
+
|
|
17
|
+
1. Callers may send `auto`, `prefer_cpu`, or `prefer_gpu`; this is a fallback-capable hint.
|
|
18
|
+
2. GPU and CPU backends own independent weighted admission pools and transition locks.
|
|
19
|
+
3. `auto` can use task-specific normalized warm latency after three samples exist on both backends
|
|
20
|
+
and one backend has more than a 10% advantage.
|
|
21
|
+
|
|
22
|
+
Explicit models and session affinity override the hint. Quality routing never follows latency.
|
|
23
|
+
Generation normalizes by output tokens; embeddings normalize by input tokens. Cold-load duration
|
|
24
|
+
does not enter feedback. Raw Ollama passthrough remains byte-transparent to the primary upstream.
|
|
25
|
+
Verified feedback is stored in a bounded, versioned snapshot and replaced atomically. A corrupt or
|
|
26
|
+
unsupported snapshot fails startup instead of silently changing learned routing behavior.
|
|
27
|
+
|
|
28
|
+
The contract is hardware-neutral: the operator supplies loopback endpoints and exact CPU-eligible
|
|
29
|
+
tags, while FreeLlama discovers host capacity and observes Ollama placement. The measured 48 GB Mac
|
|
30
|
+
result is validation evidence, not a routing constant. Device-visibility variables remain
|
|
31
|
+
Ollama-process configuration because NVIDIA, ROCm, Vulkan, Metal, and CPU-only hosts differ.
|
|
32
|
+
|
|
33
|
+
## Alternatives
|
|
34
|
+
|
|
35
|
+
| Alternative | Decision | Reason |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| Exact assignment only | Rejected | Safe but too rigid for agents and blind to runtime capacity |
|
|
38
|
+
| Let agents supply an upstream or `num_gpu` | Rejected | Bypasses operator eligibility and compatibility boundaries |
|
|
39
|
+
| Rewrite every raw Ollama request | Rejected | Breaks passthrough semantics and surprises existing clients |
|
|
40
|
+
| Persist every raw runtime event | Rejected | Unbounded traces add privacy and storage risk; persist only aggregate prompt-free feedback |
|
|
41
|
+
| Live memory/thermal controller | Deferred | No stable sensor and held-out workload yet justify it |
|
|
42
|
+
|
|
43
|
+
## Verification contract
|
|
44
|
+
|
|
45
|
+
- Preview returns placement, upstream, preference satisfaction, reason, and admission capacity.
|
|
46
|
+
- Two samples per backend do not change `auto`; the third makes the comparison decision-ready.
|
|
47
|
+
- Explicit model and session pins do not move.
|
|
48
|
+
- A one-unit GPU pool and one-unit CPU pool admit one task on each concurrently.
|
|
49
|
+
- CPU-managed requests contain `options.num_gpu: 0`; raw `/api/*` bodies are unchanged.
|
|
50
|
+
- Health exposes both pools and per-task feedback counts.
|
|
51
|
+
- Restart reloads the same bounded feedback counts; an unsupported schema refuses startup.
|
|
52
|
+
|
|
53
|
+
Rollback is configuration-only for callers: omit the preference or use `auto`. Operators can remove
|
|
54
|
+
the CPU backend to restore a single-primary layout. Reverting independent pools requires a code
|
|
55
|
+
rollback and is not recommended because it reintroduces cross-backend starvation.
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# FreeLlama architecture
|
|
2
|
+
|
|
3
|
+
FreeLlama is a localhost control plane in front of Ollama. Ollama owns model storage, model loading,
|
|
4
|
+
scheduling, inference engines, and token generation. FreeLlama owns model-aware policy around those
|
|
5
|
+
operations.
|
|
6
|
+
|
|
7
|
+
## Responsibility boundary
|
|
8
|
+
|
|
9
|
+
The following table defines the ownership boundary:
|
|
10
|
+
|
|
11
|
+
| Responsibility | Owner |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Model files, runners, Metal, MLX, llama.cpp, and token generation | Ollama |
|
|
14
|
+
| Native `/api/*` and OpenAI-compatible `/v1/*` semantics | Ollama |
|
|
15
|
+
| Installed-model discovery and capability normalization | FreeLlama |
|
|
16
|
+
| Task routing, advertised-context compatibility, K/V lower-bound preflight, evidence policy, and model rejection reasons | FreeLlama |
|
|
17
|
+
| Managed admission, session affinity, and model-transition coordination | FreeLlama |
|
|
18
|
+
| Explicit per-model CPU/GPU backend assignment | FreeLlama and separate Ollama processes |
|
|
19
|
+
| Grounded research adapters, citations, and verification verdicts | FreeLlama MCP server |
|
|
20
|
+
|
|
21
|
+
FreeLlama remains a sidecar rather than an Ollama fork. For the compatibility rationale, see the
|
|
22
|
+
[Ollama sidecar boundary](OLLAMA_SIDECAR.md).
|
|
23
|
+
|
|
24
|
+
## System topology
|
|
25
|
+
|
|
26
|
+
Every public surface uses the Rust core or calls a narrow local dependency:
|
|
27
|
+
|
|
28
|
+
```mermaid
|
|
29
|
+
flowchart TB
|
|
30
|
+
subgraph clients["Clients"]
|
|
31
|
+
CLI["freellama CLI"]
|
|
32
|
+
MCP["MCP client"]
|
|
33
|
+
RUST["Embedded Rust application"]
|
|
34
|
+
RAW["Ollama-compatible client"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph freellama["FreeLlama"]
|
|
38
|
+
CORE["freellama-core"]
|
|
39
|
+
API["Control API<br/>/_freellama/v1/*"]
|
|
40
|
+
PX["Compatibility proxy"]
|
|
41
|
+
ADAPTER["Research adapter process"]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
GPU["Primary Ollama<br/>GPU-capable"]
|
|
45
|
+
CPU["Optional Ollama<br/>CPU-assigned models"]
|
|
46
|
+
FILES["Allowlisted workspace files"]
|
|
47
|
+
|
|
48
|
+
CLI --> CORE
|
|
49
|
+
MCP --> CORE
|
|
50
|
+
RUST --> CORE
|
|
51
|
+
CORE --> API
|
|
52
|
+
RAW --> PX
|
|
53
|
+
API --> GPU
|
|
54
|
+
API --> CPU
|
|
55
|
+
PX --> GPU
|
|
56
|
+
MCP --> ADAPTER
|
|
57
|
+
ADAPTER --> FILES
|
|
58
|
+
ADAPTER --> GPU
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The CLI and MCP server do not reimplement routing. The CLI calls the core directly. The MCP server
|
|
62
|
+
uses the feature-gated NAPI boundary for native operations and the control API for serve-backed
|
|
63
|
+
operations.
|
|
64
|
+
|
|
65
|
+
## Request classification
|
|
66
|
+
|
|
67
|
+
The listener classifies requests by path before any model-aware work occurs:
|
|
68
|
+
|
|
69
|
+
```mermaid
|
|
70
|
+
flowchart TD
|
|
71
|
+
IN["Request to FreeLlama"] --> P{"Path starts with<br/>/_freellama/v1/?"}
|
|
72
|
+
P -->|"No"| RAW["Stream through compatibility proxy"]
|
|
73
|
+
RAW --> PRIMARY["Primary Ollama"]
|
|
74
|
+
P -->|"Yes"| C{"Control endpoint"}
|
|
75
|
+
C -->|"health, machine, models"| READ["Read local and Ollama state"]
|
|
76
|
+
C -->|"recommendations, routes"| DECIDE["Decide without inference"]
|
|
77
|
+
C -->|"natural-routes"| INTENT["Run intent model, then route"]
|
|
78
|
+
C -->|"tasks"| RUN["Route, admit, and execute"]
|
|
79
|
+
C -->|"sessions"| SESSION["Create in-memory affinity id"]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The control API exposes these endpoints:
|
|
83
|
+
|
|
84
|
+
| Method and path | Purpose | Inference |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `GET /_freellama/v1/health` | Health, admission capacity, and configured backends | No |
|
|
87
|
+
| `GET /_freellama/v1/machine` | Machine profile | No |
|
|
88
|
+
| `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
|
+
| `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
|
+
| `POST /_freellama/v1/sessions` | New model-affinity session | No |
|
|
93
|
+
| `DELETE /_freellama/v1/sessions/:session_id` | Release model-affinity session | No |
|
|
94
|
+
| `POST /_freellama/v1/tasks` | Managed chat, vision, tools, or embedding task | Yes |
|
|
95
|
+
|
|
96
|
+
The machine profile is host-derived rather than model-name-derived. It reports total physical
|
|
97
|
+
memory on macOS (`sysctl`), Linux (`/proc`), and Windows (system APIs), along with CPU, OS,
|
|
98
|
+
architecture, and disk. `memory_bytes` is portable host RAM. `unified_memory_bytes` is populated
|
|
99
|
+
only when host and accelerator memory are known to share a pool; it must not be treated as discrete
|
|
100
|
+
GPU VRAM. Ollama's `/api/ps` remains the placement authority.
|
|
101
|
+
|
|
102
|
+
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 does not apply CPU model assignment.
|
|
104
|
+
|
|
105
|
+
## Model discovery
|
|
106
|
+
|
|
107
|
+
The catalog joins data from Ollama and FreeLlama's optional evidence files:
|
|
108
|
+
|
|
109
|
+
```mermaid
|
|
110
|
+
flowchart LR
|
|
111
|
+
TAGS["Ollama /api/tags"] --> CAT["Catalog builder"]
|
|
112
|
+
SHOW["Ollama /api/show"] --> CAT
|
|
113
|
+
PS["Ollama /api/ps"] --> CAT
|
|
114
|
+
BENCH["Local benchmark report"] --> CAT
|
|
115
|
+
POLICY["Task policy"] --> CAT
|
|
116
|
+
ASSIGN["CPU model assignments"] --> CAT
|
|
117
|
+
CAT --> MODEL["CatalogModel<br/>capabilities · context · residency<br/>benchmark · policy rank · backend"]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
FreeLlama fetches tags and residency from each configured backend. Models assigned to CPU are
|
|
121
|
+
removed from the primary catalog view and included only from the secondary backend. Static catalog
|
|
122
|
+
metadata is cached for 30 seconds; residency is refreshed before routing.
|
|
123
|
+
|
|
124
|
+
A failed `/api/show` response skips that model instead of failing the entire catalog. An unreachable
|
|
125
|
+
configured backend fails catalog discovery closed because FreeLlama cannot prove the assigned model's
|
|
126
|
+
state.
|
|
127
|
+
|
|
128
|
+
## Deterministic routing
|
|
129
|
+
|
|
130
|
+
Routing starts with an explicit task contract. Supported task kinds are `completion`, `coding`,
|
|
131
|
+
`code_repair`, `tools`, `browser`, `vision`, `embedding`, and `long_context`.
|
|
132
|
+
|
|
133
|
+
```mermaid
|
|
134
|
+
flowchart TD
|
|
135
|
+
I["Route input"] --> C["Derive required capabilities<br/>and requested context"]
|
|
136
|
+
C --> F["Filter installed candidates"]
|
|
137
|
+
F --> X{"Explicit model?"}
|
|
138
|
+
X -->|"Yes"| E["Use exact model or refuse"]
|
|
139
|
+
X -->|"No"| O{"Objective"}
|
|
140
|
+
O -->|"fastest"| S["Rank local performance,<br/>residency, and deterministic ties"]
|
|
141
|
+
O -->|"balanced"| B["Require policy-qualified candidates,<br/>then balance score and residency"]
|
|
142
|
+
O -->|"quality"| Q["Require policy-qualified candidates,<br/>then prefer policy order"]
|
|
143
|
+
E --> H["Grade advertised-context compatibility and evidence"]
|
|
144
|
+
S --> H
|
|
145
|
+
B --> H
|
|
146
|
+
Q --> H
|
|
147
|
+
H --> G{"Minimum confidence met?"}
|
|
148
|
+
G -->|"No"| R["Refuse with missing evidence"]
|
|
149
|
+
G -->|"Yes or unset"| D["RouteDecision"]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Explicit model selection never substitutes another model. Every rejected candidate includes a
|
|
153
|
+
reason so callers can distinguish capability, context, policy, and installation failures.
|
|
154
|
+
|
|
155
|
+
`context_window_fit` means only that requested `num_ctx` fits the model's advertised context
|
|
156
|
+
window. It is not a live-memory, free-VRAM, K/V-cache, thermal, or runner-admission verdict.
|
|
157
|
+
Execution separately reports a conservative model-metadata K/V lower-bound preflight and post-run
|
|
158
|
+
placement evidence; Ollama remains authoritative for actual runner allocation and admission.
|
|
159
|
+
|
|
160
|
+
### Confidence evidence
|
|
161
|
+
|
|
162
|
+
Confidence is a grade derived from independent evidence dimensions:
|
|
163
|
+
|
|
164
|
+
| Policy evidence | Benchmark evidence | Confidence | Meaning |
|
|
165
|
+
|---|---|---|---|
|
|
166
|
+
| Present | Present | `medium` | The task policy qualifies the model and the machine has measured it |
|
|
167
|
+
| Present | Missing | `low` | Quality evidence exists without local functional measurement |
|
|
168
|
+
| Missing | Present | `low` | Local throughput exists without a quality contract |
|
|
169
|
+
| Missing | Missing | `low` | Routing relies on capability metadata only |
|
|
170
|
+
|
|
171
|
+
`minConfidence: "medium"` refuses before generation when either input is missing. The gate lives in
|
|
172
|
+
`select_route`, so the HTTP API, CLI, MCP server, and embedded core share the same behavior.
|
|
173
|
+
|
|
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
|
+
## Managed task execution
|
|
200
|
+
|
|
201
|
+
Managed tasks combine routing with admission and backend-aware transition coordination:
|
|
202
|
+
|
|
203
|
+
```mermaid
|
|
204
|
+
flowchart TD
|
|
205
|
+
T["POST /tasks or /task-batches"] --> R["Select eligible model + backend"]
|
|
206
|
+
R --> K["Calculate metadata-backed K/V lower bound<br/>(or report unknown)"]
|
|
207
|
+
K --> A{"Acquire selected backend's<br/>3:2:1 fair weighted budget"}
|
|
208
|
+
A -->|"No"| E503["503 server busy"]
|
|
209
|
+
A -->|"Yes"| B{"Execution backend"}
|
|
210
|
+
B -->|"Primary"| GL{"GPU model resident<br/>under transition lock?"}
|
|
211
|
+
B -->|"CPU assignment"| CO["Add num_gpu: 0"]
|
|
212
|
+
CO --> CL{"CPU model resident<br/>under transition lock?"}
|
|
213
|
+
GL -->|"Yes"| GR["GPU shared lock"]
|
|
214
|
+
GL -->|"No"| GX["GPU exclusive transition lock"]
|
|
215
|
+
CL -->|"Yes"| CR["CPU shared lock"]
|
|
216
|
+
CL -->|"No"| CX["CPU exclusive transition lock"]
|
|
217
|
+
GR --> OQ["Send to selected Ollama queue"]
|
|
218
|
+
GX --> OQ
|
|
219
|
+
CR --> OQ
|
|
220
|
+
CX --> OQ
|
|
221
|
+
OQ --> SEND["Ollama schedules bounded request"]
|
|
222
|
+
SEND --> RETRY{"500, 502, 504,<br/>or connection failure?"}
|
|
223
|
+
RETRY -->|"Retryable"| OQ
|
|
224
|
+
RETRY -->|"Success"| OBS["Query selected Ollama /api/ps"]
|
|
225
|
+
OBS --> OUT["Route, assignment, physical observation,<br/>admission, metrics, and response"]
|
|
226
|
+
RETRY -->|"503, timeout, or final failure"| ERR["Return upstream error"]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Admission is independent per backend and uses weighted fair round robin: interactive gets three
|
|
230
|
+
turns, normal two, and background one, preserving FIFO inside each class and preventing starvation.
|
|
231
|
+
The primary/GPU pool defaults to two weighted units and the
|
|
232
|
+
optional CPU pool defaults to one. A saturated GPU burst therefore cannot consume the permit held
|
|
233
|
+
for a small CPU helper. Those values represent one ordinary chat and one embedding, not detected
|
|
234
|
+
hardware capacity; operators can tune both budgets from queue-wait and resident-memory evidence.
|
|
235
|
+
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 FreeLlama admission and
|
|
237
|
+
enter the primary Ollama queue directly.
|
|
238
|
+
|
|
239
|
+
| Task | Cost |
|
|
240
|
+
|---|---:|
|
|
241
|
+
| Embedding | `ceil(input_items / 4)` |
|
|
242
|
+
| Chat, coding, tools, browser, or long context | 2 |
|
|
243
|
+
| Vision | 4 |
|
|
244
|
+
|
|
245
|
+
FreeLlama acquires the admission slot before a transition lock to avoid deadlock. A model marked
|
|
246
|
+
resident during discovery is checked again while the shared transition lock is held; stale or
|
|
247
|
+
unavailable residency falls back to the exclusive transition path. Session affinity is bound only
|
|
248
|
+
after successful upstream execution, so refused and failed tasks cannot change later routing.
|
|
249
|
+
|
|
250
|
+
`POST /_freellama/v1/task-batches` is for caller-declared independent work only: every item has a
|
|
251
|
+
stable ID and `independent:true`; dependent workflow execution is rejected before any upstream call.
|
|
252
|
+
It bounds local dispatch and returns ordered per-item success/error receipts. Every child still
|
|
253
|
+
passes the same global admission and transition path above.
|
|
254
|
+
|
|
255
|
+
The K/V preflight derives F16 K+V bytes-per-token only when `/api/show` exposes the required model
|
|
256
|
+
shape. It blocks only a known weights-plus-cache lower bound above 80% of total CPU/unified memory.
|
|
257
|
+
It deliberately reports unknown rather than guessing for other architectures or discrete GPU free
|
|
258
|
+
VRAM; Ollama remains the live loader and final memory authority.
|
|
259
|
+
|
|
260
|
+
Resident tasks share a backend lock. Nonresident tasks take that backend's exclusive lock so a cold
|
|
261
|
+
load cannot race another managed task on the same server. CPU and GPU backends have separate locks
|
|
262
|
+
and admission pools, so they can progress independently.
|
|
263
|
+
|
|
264
|
+
HTTP `500`, `502`, and `504` status codes and connection failures use bounded retry with backoff.
|
|
265
|
+
FreeLlama does not retry `503 Service Unavailable` because retrying while holding admission can
|
|
266
|
+
amplify overload. It does not retry a generation timeout because the first request might still run.
|
|
267
|
+
|
|
268
|
+
## CPU and GPU backend selection
|
|
269
|
+
|
|
270
|
+
CPU eligibility is an explicit operator assignment. Within that safe set, a caller may provide a
|
|
271
|
+
guarded preference and `auto` may learn from warm runtime receipts:
|
|
272
|
+
|
|
273
|
+
```mermaid
|
|
274
|
+
flowchart TD
|
|
275
|
+
I["Task, objective, capabilities,<br/>model/session pins, preference"] --> E["Filter eligible installed models"]
|
|
276
|
+
E --> P{"Explicit model or<br/>session affinity?"}
|
|
277
|
+
P -->|"Yes"| M["Preserve pinned model"]
|
|
278
|
+
P -->|"No"| H{"Preference or auto hint<br/>has eligible candidates?"}
|
|
279
|
+
H -->|"No"| D["Deterministic router default"]
|
|
280
|
+
H -->|"Yes"| S["Select within hinted backend"]
|
|
281
|
+
M --> A{"In cpu_models set?"}
|
|
282
|
+
D --> A
|
|
283
|
+
S --> A
|
|
284
|
+
A -->|"No"| G["Primary upstream<br/>GPU requested"]
|
|
285
|
+
A -->|"Yes"| C["Secondary upstream<br/>CPU requested"]
|
|
286
|
+
C --> N["Pin options.num_gpu = 0"]
|
|
287
|
+
G --> O["Observe /api/ps processor"]
|
|
288
|
+
N --> O
|
|
289
|
+
O -->|"verified"| F["Record normalized warm<br/>work-unit latency"]
|
|
290
|
+
O -->|"unknown, mixed, mismatch"| W["Report; withhold feedback"]
|
|
291
|
+
F -.->|"After 3 samples/backend/task;<br/>not quality routing"| H
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The hint never changes the operator's CPU assignment. If the preferred backend has no eligible
|
|
295
|
+
model, routing falls back and returns `preference_satisfied: false`. Feedback is task-specific,
|
|
296
|
+
bounded, versioned, and prompt-free. The CLI persists it with atomic file replacement by default;
|
|
297
|
+
restart-reset behavior is available only through the explicit `--ephemeral-feedback` mode. It
|
|
298
|
+
records normalized latency only for already-resident, physically verified tasks so prompt size and
|
|
299
|
+
cold-load cost do not poison the comparison. It requires more than a 10% advantage; capacity can
|
|
300
|
+
break a tie before the sample threshold. Quality routing, explicit model selection, and session
|
|
301
|
+
affinity do not follow the latency loop.
|
|
302
|
+
|
|
303
|
+
The same selection function supplies preview/recommendation receipts, intent-model execution, and
|
|
304
|
+
managed task execution. This keeps advertised placement and actual request routing aligned.
|
|
305
|
+
|
|
306
|
+
Raw proxy traffic always uses the primary upstream. For setup and runtime verification, see
|
|
307
|
+
[Run models on CPU and GPU](CPU_GPU_ROUTING.md).
|
|
308
|
+
|
|
309
|
+
## MCP tool flow
|
|
310
|
+
|
|
311
|
+
The MCP server keeps general model work separate from file-backed research and destructive lifecycle
|
|
312
|
+
operations:
|
|
313
|
+
|
|
314
|
+
```mermaid
|
|
315
|
+
flowchart TD
|
|
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).
|
|
331
|
+
|
|
332
|
+
## Grounded research flow
|
|
333
|
+
|
|
334
|
+
Use `delegate_research` for narrow questions that require reading repository files:
|
|
335
|
+
|
|
336
|
+
```mermaid
|
|
337
|
+
flowchart TD
|
|
338
|
+
Q["Question and workspacePath"] --> P["Resolve path through symlinks"]
|
|
339
|
+
P --> A{"Inside allowed roots?"}
|
|
340
|
+
A -->|"No"| REFUSE["Refuse before model execution"]
|
|
341
|
+
A -->|"Yes"| G{"Model evidence grade usable?"}
|
|
342
|
+
G -->|"No"| REFUSE
|
|
343
|
+
G -->|"Yes or unmeasured"| LOOP["Bounded adapter loop"]
|
|
344
|
+
LOOP --> MANAGED["Each model turn: managed coding task"]
|
|
345
|
+
MANAGED --> RECEIPT["Admission + physical placement receipt"]
|
|
346
|
+
LOOP --> READ["Read and search files"]
|
|
347
|
+
READ --> ANSWER["Local-model answer"]
|
|
348
|
+
ANSWER --> V["Grade observed files, calls,<br/>model, and task envelope"]
|
|
349
|
+
V --> OUT["accept, verify, or escalate<br/>plus citations"]
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
The verdict does not ask the local model whether it researched the answer. It uses the adapter's
|
|
353
|
+
recorded calls, successful file evidence, the selected model's measured grade, and the question's
|
|
354
|
+
shape.
|
|
355
|
+
|
|
356
|
+
The adapter loop preserves its control contract through four mechanisms:
|
|
357
|
+
|
|
358
|
+
- Pagination keeps complete output on disk while showing bounded pages to the model.
|
|
359
|
+
- Context fitting byte-preserves the system prompt and question by default, compacts older
|
|
360
|
+
observations, and fails before Ollama if pinned content cannot fit. The first estimate is
|
|
361
|
+
configurable; real `prompt_eval_count` values calibrate subsequent fits conservatively.
|
|
362
|
+
- JSON repair retains gathered evidence across a bounded format correction.
|
|
363
|
+
- Repeat suppression serves an identical earlier result without rerunning the subprocess command.
|
|
364
|
+
|
|
365
|
+
For the benchmark implementation of this loop, see [`AGENTS.md`](../AGENTS.md).
|
|
366
|
+
|
|
367
|
+
## Context and token boundary
|
|
368
|
+
|
|
369
|
+
The intended orchestration pattern keeps judgment in the strongest model and token-heavy operations
|
|
370
|
+
local:
|
|
371
|
+
|
|
372
|
+
```mermaid
|
|
373
|
+
flowchart LR
|
|
374
|
+
FRONTIER["Frontier model<br/>judgment and review"] --> DISPATCH["Small orchestrator<br/>tool dispatch and verification"]
|
|
375
|
+
DISPATCH --> FREE["FreeLlama"]
|
|
376
|
+
FREE --> LOCAL["Local Ollama models<br/>files, images, and embeddings"]
|
|
377
|
+
LOCAL --> FREE
|
|
378
|
+
FREE -->|"bounded answer, evidence,<br/>metrics, verdict"| DISPATCH
|
|
379
|
+
DISPATCH -->|"verified conclusion"| FRONTIER
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Raw source, images, and embedding vectors do not need to enter the frontier model's context.
|
|
383
|
+
Embedding vectors are withheld by default because they are large and not human-readable. For the
|
|
384
|
+
measured accounting, see [Token economics](ECONOMICS.md).
|
|
385
|
+
|
|
386
|
+
## State and failure behavior
|
|
387
|
+
|
|
388
|
+
FreeLlama keeps these values in memory:
|
|
389
|
+
|
|
390
|
+
- Catalog metadata cache
|
|
391
|
+
- Current residency snapshots
|
|
392
|
+
- Session-to-model affinity
|
|
393
|
+
- Independent GPU and CPU priority-fair weighted admission pools
|
|
394
|
+
- Per-task normalized warm latency and queue feedback for each backend, restored from an optional
|
|
395
|
+
versioned atomic snapshot
|
|
396
|
+
- Separate CPU and GPU transition locks
|
|
397
|
+
|
|
398
|
+
Sessions are bounded (default 1024) and expire after idle time (default one hour); they store only
|
|
399
|
+
model affinity, never prompts or Ollama KV. Restarting `freellama serve` clears sessions and the catalog cache. Persisted placement feedback is
|
|
400
|
+
reloaded; `--ephemeral-feedback` intentionally resets it. Ollama owns model residency, so loaded
|
|
401
|
+
models survive a FreeLlama restart.
|
|
402
|
+
|
|
403
|
+
The design fails closed when it cannot establish a required contract. Examples include an
|
|
404
|
+
unreachable configured backend, an unknown confidence grade, a policy without qualified models, an
|
|
405
|
+
explicit model that is not installed, or a task that exceeds the advertised context window.
|
|
406
|
+
|
|
407
|
+
## Product boundary
|
|
408
|
+
|
|
409
|
+
FreeLlama is not an inference engine, remote provider marketplace, billing layer, hosted model
|
|
410
|
+
registry, general agent runtime, or A2A coordinator. It can authenticate every route with one
|
|
411
|
+
operator-managed bearer token and refuses unauthenticated nonloopback listeners. An external layer
|
|
412
|
+
must still provide TLS, tenant authorization, tenant-specific limits, and isolation.
|
|
413
|
+
|
|
414
|
+
Use Ollama directly when a caller only needs raw speed from one exact model. Use FreeLlama when the
|
|
415
|
+
workload needs routing evidence, bounded admission, backend placement, compatibility proxying,
|
|
416
|
+
grounded research, or inspectable receipts.
|