@cleocode/core 2026.6.14 → 2026.6.15
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/dist/check/pr-gate.d.ts +6 -2
- package/dist/check/pr-gate.d.ts.map +1 -1
- package/dist/check/pr-gate.js +14 -32
- package/dist/check/pr-gate.js.map +1 -1
- package/dist/config/config-repair.d.ts +70 -0
- package/dist/config/config-repair.d.ts.map +1 -0
- package/dist/config/config-repair.js +248 -0
- package/dist/config/config-repair.js.map +1 -0
- package/dist/config/registry.d.ts.map +1 -1
- package/dist/config/registry.js +4 -4
- package/dist/config/registry.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -10
- package/dist/config.js.map +1 -1
- package/dist/docs/export-document.js +17 -16
- package/dist/docs/export-document.js.map +2 -2
- package/dist/gc/cleanup.d.ts +23 -8
- package/dist/gc/cleanup.d.ts.map +1 -1
- package/dist/gc/cleanup.js +287 -22
- package/dist/gc/cleanup.js.map +1 -1
- package/dist/llm/cli-ops.d.ts +16 -0
- package/dist/llm/cli-ops.d.ts.map +1 -1
- package/dist/llm/cli-ops.js +32 -5
- package/dist/llm/cli-ops.js.map +1 -1
- package/dist/llm/cross-provider-selector.d.ts +249 -0
- package/dist/llm/cross-provider-selector.d.ts.map +1 -0
- package/dist/llm/cross-provider-selector.js +572 -0
- package/dist/llm/cross-provider-selector.js.map +1 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +1 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/local-model-fit.d.ts +260 -0
- package/dist/llm/local-model-fit.d.ts.map +1 -0
- package/dist/llm/local-model-fit.js +563 -0
- package/dist/llm/local-model-fit.js.map +1 -0
- package/dist/llm/plugin-facade.js +761 -230
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/llm/provider-registry/builtin/ollama.d.ts +18 -0
- package/dist/llm/provider-registry/builtin/ollama.d.ts.map +1 -1
- package/dist/llm/provider-registry/builtin/ollama.js +20 -2
- package/dist/llm/provider-registry/builtin/ollama.js.map +1 -1
- package/dist/llm/role-resolver.d.ts.map +1 -1
- package/dist/llm/role-resolver.js +45 -10
- package/dist/llm/role-resolver.js.map +1 -1
- package/dist/llm/transports/codex-responses.d.ts +111 -88
- package/dist/llm/transports/codex-responses.d.ts.map +1 -1
- package/dist/llm/transports/codex-responses.js +456 -258
- package/dist/llm/transports/codex-responses.js.map +1 -1
- package/dist/release/index.d.ts +2 -2
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +1 -1
- package/dist/release/index.js.map +1 -1
- package/dist/release/reconcile.d.ts +97 -0
- package/dist/release/reconcile.d.ts.map +1 -1
- package/dist/release/reconcile.js +412 -5
- package/dist/release/reconcile.js.map +1 -1
- package/dist/resources/backend.d.ts +155 -0
- package/dist/resources/backend.d.ts.map +1 -0
- package/dist/resources/backend.js +23 -0
- package/dist/resources/backend.js.map +1 -0
- package/dist/resources/linux-backend.d.ts +167 -0
- package/dist/resources/linux-backend.d.ts.map +1 -0
- package/dist/resources/linux-backend.js +259 -0
- package/dist/resources/linux-backend.js.map +1 -0
- package/dist/resources/monitor.d.ts +230 -0
- package/dist/resources/monitor.d.ts.map +1 -0
- package/dist/resources/monitor.js +255 -0
- package/dist/resources/monitor.js.map +1 -0
- package/dist/resources/spawn-wrapper.d.ts +265 -0
- package/dist/resources/spawn-wrapper.d.ts.map +1 -0
- package/dist/resources/spawn-wrapper.js +304 -0
- package/dist/resources/spawn-wrapper.js.map +1 -0
- package/dist/selfimprove/budget.d.ts +11 -2
- package/dist/selfimprove/budget.d.ts.map +1 -1
- package/dist/selfimprove/budget.js +11 -2
- package/dist/selfimprove/budget.js.map +1 -1
- package/dist/selfimprove/fix-gen.d.ts +229 -0
- package/dist/selfimprove/fix-gen.d.ts.map +1 -0
- package/dist/selfimprove/fix-gen.js +266 -0
- package/dist/selfimprove/fix-gen.js.map +1 -0
- package/dist/selfimprove/run-loop.d.ts +44 -4
- package/dist/selfimprove/run-loop.d.ts.map +1 -1
- package/dist/selfimprove/run-loop.js +73 -6
- package/dist/selfimprove/run-loop.js.map +1 -1
- package/dist/sentient/cross-project-hygiene.d.ts +11 -2
- package/dist/sentient/cross-project-hygiene.d.ts.map +1 -1
- package/dist/sentient/cross-project-hygiene.js +73 -10
- package/dist/sentient/cross-project-hygiene.js.map +1 -1
- package/dist/sentient/daemon.d.ts.map +1 -1
- package/dist/sentient/daemon.js +7 -2
- package/dist/sentient/daemon.js.map +1 -1
- package/dist/sentient/worktree-dispatch.d.ts.map +1 -1
- package/dist/sentient/worktree-dispatch.js +8 -1
- package/dist/sentient/worktree-dispatch.js.map +1 -1
- package/dist/spawn/branch-lock.d.ts.map +1 -1
- package/dist/spawn/branch-lock.js +7 -1
- package/dist/spawn/branch-lock.js.map +1 -1
- package/dist/store/attachment-repair.d.ts +110 -0
- package/dist/store/attachment-repair.d.ts.map +1 -0
- package/dist/store/attachment-repair.js +326 -0
- package/dist/store/attachment-repair.js.map +1 -0
- package/dist/store/attachment-store.d.ts.map +1 -1
- package/dist/store/attachment-store.js +24 -7
- package/dist/store/attachment-store.js.map +1 -1
- package/dist/tasks/evidence.d.ts +34 -2
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +293 -35
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/verification/verify-tools.d.ts.map +1 -1
- package/dist/verification/verify-tools.js +5 -6
- package/dist/verification/verify-tools.js.map +1 -1
- package/package.json +22 -12
- package/templates/workflows/release-prepare.yml.tmpl +48 -2
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-model fit ranking for Ollama-backed open-weight models (T11982).
|
|
3
|
+
*
|
|
4
|
+
* ## Purpose
|
|
5
|
+
*
|
|
6
|
+
* Given the current machine's hardware (RAM, VRAM), rank 2–3 curated
|
|
7
|
+
* open-weight models that are actually likely to run well. This is the
|
|
8
|
+
* `cleo llm fit` wizard building block — it powers T11983's one-keypress
|
|
9
|
+
* pull+connect flow.
|
|
10
|
+
*
|
|
11
|
+
* ## Vendor-vs-inspire decision (llmfit)
|
|
12
|
+
*
|
|
13
|
+
* Evaluated `github.com/AlexsJones/llmfit` (MIT, Go, ~400 LOC). The library
|
|
14
|
+
* is a standalone Go binary — not a TS/ESM package. Its key contribution is
|
|
15
|
+
* the IDEA: use RAM thresholds to gate model selection, prefer VRAM when
|
|
16
|
+
* available, and produce a ranked list with human-readable reasons. We
|
|
17
|
+
* **vendor the IDEA** (TS-native re-implementation) rather than shelling out
|
|
18
|
+
* to a Go binary or adding a cross-language dependency. The model thresholds
|
|
19
|
+
* below are independently calibrated from Ollama's documentation and the
|
|
20
|
+
* Hugging Face model cards, not copy-pasted from llmfit.
|
|
21
|
+
*
|
|
22
|
+
* ## Gate-13 compliance
|
|
23
|
+
*
|
|
24
|
+
* This module:
|
|
25
|
+
* - MUST NOT construct any transport or SDK client.
|
|
26
|
+
* - MUST NOT read `process.env.*_API_KEY` directly.
|
|
27
|
+
* - MUST NOT define a new `resolveLLMFor*` function.
|
|
28
|
+
* - MUST NOT hardcode model-id literals in logic — all model IDs come from
|
|
29
|
+
* the {@link LOCAL_MODEL_CANDIDATES} data table.
|
|
30
|
+
*
|
|
31
|
+
* The hardware detection (`os.totalmem`, `os.freemem`, `/proc/meminfo`,
|
|
32
|
+
* `nvidia-smi`) is plain system inspection — not covered by Gate-13 (which
|
|
33
|
+
* governs LLM resolution / transport construction).
|
|
34
|
+
*
|
|
35
|
+
* Ollama liveness re-uses {@link probeOllamaAlive} from
|
|
36
|
+
* `cross-provider-selector.ts` (no duplication). The `/api/tags` fetch is
|
|
37
|
+
* a vanilla HTTP call to localhost, not a transport construction.
|
|
38
|
+
*
|
|
39
|
+
* @module llm/local-model-fit
|
|
40
|
+
* @task T11982
|
|
41
|
+
* @epic T11671
|
|
42
|
+
*/
|
|
43
|
+
import { execFile } from 'node:child_process';
|
|
44
|
+
import { readFile } from 'node:fs/promises';
|
|
45
|
+
import { freemem, totalmem } from 'node:os';
|
|
46
|
+
import { promisify } from 'node:util';
|
|
47
|
+
import { getLogger } from '../logger.js';
|
|
48
|
+
import { probeOllamaAlive } from './cross-provider-selector.js';
|
|
49
|
+
const execFileAsync = promisify(execFile);
|
|
50
|
+
const logger = getLogger('llm-local-model-fit');
|
|
51
|
+
/**
|
|
52
|
+
* Curated candidate table. These are the ONLY models `rankLocalModelFit`
|
|
53
|
+
* can recommend — expand this table to add new models.
|
|
54
|
+
*
|
|
55
|
+
* Exclusions:
|
|
56
|
+
* - `qwen2:0.5b` — proof-of-life only; deliberately absent from this list.
|
|
57
|
+
*
|
|
58
|
+
* @task T11982
|
|
59
|
+
*/
|
|
60
|
+
export const LOCAL_MODEL_CANDIDATES = [
|
|
61
|
+
{
|
|
62
|
+
// Live-verified 2026-06-11: ollama.com/library/gemma4 · gemma4:e2b = 7.2 GB (Q4_K_M)
|
|
63
|
+
// Edge 2B effective parameters, 128k context, multimodal (text + image).
|
|
64
|
+
modelTag: 'gemma4:e2b',
|
|
65
|
+
displayName: 'Gemma 4 E2B (edge)',
|
|
66
|
+
family: 'gemma4',
|
|
67
|
+
minRamGb: 4,
|
|
68
|
+
recommendedRamGb: 6,
|
|
69
|
+
minVramGb: 3,
|
|
70
|
+
recommendedVramGb: 5,
|
|
71
|
+
diskSizeGb: 7.2,
|
|
72
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
73
|
+
codeSpecialist: false,
|
|
74
|
+
contextLengthK: 128,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
// Live-verified 2026-06-11: ollama.com/library/gemma4 · gemma4:e4b = 9.6 GB (Q4_K_M)
|
|
78
|
+
// Edge 4B effective parameters, 128k context, multimodal (text + image).
|
|
79
|
+
modelTag: 'gemma4:e4b',
|
|
80
|
+
displayName: 'Gemma 4 E4B (edge)',
|
|
81
|
+
family: 'gemma4',
|
|
82
|
+
minRamGb: 6,
|
|
83
|
+
recommendedRamGb: 8,
|
|
84
|
+
minVramGb: 6,
|
|
85
|
+
recommendedVramGb: 8,
|
|
86
|
+
diskSizeGb: 9.6,
|
|
87
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
88
|
+
codeSpecialist: false,
|
|
89
|
+
contextLengthK: 128,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
// Live-verified 2026-06-11: ollama.com/library/gemma4 · gemma4:12b = 7.6 GB (QAT)
|
|
93
|
+
// 12B parameters, 256k context, multimodal (text + image).
|
|
94
|
+
modelTag: 'gemma4:12b',
|
|
95
|
+
displayName: 'Gemma 4 12B',
|
|
96
|
+
family: 'gemma4',
|
|
97
|
+
minRamGb: 10,
|
|
98
|
+
recommendedRamGb: 16,
|
|
99
|
+
minVramGb: 8,
|
|
100
|
+
recommendedVramGb: 12,
|
|
101
|
+
diskSizeGb: 7.6,
|
|
102
|
+
quantNote: 'QAT (default pull)',
|
|
103
|
+
codeSpecialist: false,
|
|
104
|
+
contextLengthK: 256,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
modelTag: 'qwen3:1.7b',
|
|
108
|
+
displayName: 'Qwen 3 1.7B',
|
|
109
|
+
family: 'qwen3',
|
|
110
|
+
minRamGb: 3,
|
|
111
|
+
recommendedRamGb: 4,
|
|
112
|
+
minVramGb: 2,
|
|
113
|
+
recommendedVramGb: 3,
|
|
114
|
+
diskSizeGb: 1.4,
|
|
115
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
116
|
+
codeSpecialist: false,
|
|
117
|
+
contextLengthK: 32,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
modelTag: 'qwen3:4b',
|
|
121
|
+
displayName: 'Qwen 3 4B',
|
|
122
|
+
family: 'qwen3',
|
|
123
|
+
minRamGb: 6,
|
|
124
|
+
recommendedRamGb: 8,
|
|
125
|
+
minVramGb: 4,
|
|
126
|
+
recommendedVramGb: 6,
|
|
127
|
+
diskSizeGb: 2.6,
|
|
128
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
129
|
+
codeSpecialist: false,
|
|
130
|
+
contextLengthK: 32,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
modelTag: 'qwen3:8b',
|
|
134
|
+
displayName: 'Qwen 3 8B',
|
|
135
|
+
family: 'qwen3',
|
|
136
|
+
minRamGb: 8,
|
|
137
|
+
recommendedRamGb: 12,
|
|
138
|
+
minVramGb: 7,
|
|
139
|
+
recommendedVramGb: 10,
|
|
140
|
+
diskSizeGb: 5.2,
|
|
141
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
142
|
+
codeSpecialist: false,
|
|
143
|
+
contextLengthK: 32,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
modelTag: 'llama3.2:3b',
|
|
147
|
+
displayName: 'Llama 3.2 3B',
|
|
148
|
+
family: 'llama3.2',
|
|
149
|
+
minRamGb: 4,
|
|
150
|
+
recommendedRamGb: 6,
|
|
151
|
+
minVramGb: 3,
|
|
152
|
+
recommendedVramGb: 4,
|
|
153
|
+
diskSizeGb: 2.0,
|
|
154
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
155
|
+
codeSpecialist: false,
|
|
156
|
+
contextLengthK: 128,
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
modelTag: 'qwen2.5-coder:3b',
|
|
160
|
+
displayName: 'Qwen 2.5 Coder 3B',
|
|
161
|
+
family: 'qwen2.5-coder',
|
|
162
|
+
minRamGb: 4,
|
|
163
|
+
recommendedRamGb: 6,
|
|
164
|
+
minVramGb: 3,
|
|
165
|
+
recommendedVramGb: 4,
|
|
166
|
+
diskSizeGb: 1.9,
|
|
167
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
168
|
+
codeSpecialist: true,
|
|
169
|
+
contextLengthK: 128,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
modelTag: 'phi4-mini:3.8b',
|
|
173
|
+
displayName: 'Phi 4 Mini 3.8B',
|
|
174
|
+
family: 'phi4',
|
|
175
|
+
minRamGb: 4,
|
|
176
|
+
recommendedRamGb: 6,
|
|
177
|
+
minVramGb: 3,
|
|
178
|
+
recommendedVramGb: 4,
|
|
179
|
+
diskSizeGb: 2.5,
|
|
180
|
+
quantNote: 'Q4_K_M (default pull)',
|
|
181
|
+
codeSpecialist: false,
|
|
182
|
+
contextLengthK: 128,
|
|
183
|
+
},
|
|
184
|
+
];
|
|
185
|
+
/**
|
|
186
|
+
* Read `MemAvailable` from `/proc/meminfo` on Linux.
|
|
187
|
+
*
|
|
188
|
+
* Returns `null` on non-Linux or if the file is unreadable.
|
|
189
|
+
*
|
|
190
|
+
* @internal
|
|
191
|
+
*/
|
|
192
|
+
async function readLinuxMemAvailableBytes() {
|
|
193
|
+
try {
|
|
194
|
+
const content = await readFile('/proc/meminfo', 'utf-8');
|
|
195
|
+
for (const line of content.split('\n')) {
|
|
196
|
+
if (line.startsWith('MemAvailable:')) {
|
|
197
|
+
// Format: "MemAvailable: 42233788 kB"
|
|
198
|
+
const match = /MemAvailable:\s+(\d+)\s+kB/.exec(line);
|
|
199
|
+
if (match?.[1]) {
|
|
200
|
+
return parseInt(match[1], 10) * 1024;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Detect NVIDIA VRAM via `nvidia-smi`.
|
|
212
|
+
*
|
|
213
|
+
* Returns `null` fields if the binary is absent or errors.
|
|
214
|
+
*
|
|
215
|
+
* @internal
|
|
216
|
+
*/
|
|
217
|
+
async function detectNvidiaVram() {
|
|
218
|
+
try {
|
|
219
|
+
const { stdout } = await execFileAsync('nvidia-smi', [
|
|
220
|
+
'--query-gpu=memory.total,memory.free',
|
|
221
|
+
'--format=csv,noheader,nounits',
|
|
222
|
+
]);
|
|
223
|
+
// Output: "10240, 7995" (in MiB)
|
|
224
|
+
const parts = stdout.trim().split(',');
|
|
225
|
+
if (parts.length >= 2) {
|
|
226
|
+
const totalMib = parseInt(parts[0].trim(), 10);
|
|
227
|
+
const freeMib = parseInt(parts[1].trim(), 10);
|
|
228
|
+
if (!Number.isNaN(totalMib) && !Number.isNaN(freeMib)) {
|
|
229
|
+
return {
|
|
230
|
+
totalBytes: totalMib * 1024 * 1024,
|
|
231
|
+
freeBytes: freeMib * 1024 * 1024,
|
|
232
|
+
method: 'nvidia-smi',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Detect AMD VRAM via `rocm-smi`.
|
|
244
|
+
*
|
|
245
|
+
* Returns `null` fields if the binary is absent or errors.
|
|
246
|
+
*
|
|
247
|
+
* @internal
|
|
248
|
+
*/
|
|
249
|
+
async function detectRocmVram() {
|
|
250
|
+
try {
|
|
251
|
+
const { stdout } = await execFileAsync('rocm-smi', ['--showmeminfo', 'vram', '--csv']);
|
|
252
|
+
// rocm-smi CSV: variable format, heuristically parse for Total/Used lines
|
|
253
|
+
const totalMatch = /Total\s+VRAM.*?(\d+)/i.exec(stdout);
|
|
254
|
+
const usedMatch = /Used\s+VRAM.*?(\d+)/i.exec(stdout);
|
|
255
|
+
if (totalMatch?.[1]) {
|
|
256
|
+
const totalBytes = parseInt(totalMatch[1], 10) * 1024 * 1024;
|
|
257
|
+
const usedBytes = usedMatch?.[1] ? parseInt(usedMatch[1], 10) * 1024 * 1024 : null;
|
|
258
|
+
return {
|
|
259
|
+
totalBytes,
|
|
260
|
+
freeBytes: usedBytes !== null ? totalBytes - usedBytes : null,
|
|
261
|
+
method: 'rocm-smi',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Capture a full hardware snapshot.
|
|
272
|
+
*
|
|
273
|
+
* Never throws — VRAM detection failures degrade gracefully to `null`.
|
|
274
|
+
*
|
|
275
|
+
* @param overrides - Override fields for testing (avoids OS calls in unit tests).
|
|
276
|
+
* @returns A hardware snapshot describing this machine's capabilities.
|
|
277
|
+
*
|
|
278
|
+
* @task T11982
|
|
279
|
+
*/
|
|
280
|
+
export async function captureHardwareSnapshot(overrides) {
|
|
281
|
+
const platform = overrides?.platform ?? process.platform;
|
|
282
|
+
const totalRamBytes = overrides?.totalRamBytes ?? totalmem();
|
|
283
|
+
let availableRamBytes;
|
|
284
|
+
if (overrides?.availableRamBytes !== undefined) {
|
|
285
|
+
availableRamBytes = overrides.availableRamBytes;
|
|
286
|
+
}
|
|
287
|
+
else if (platform === 'linux') {
|
|
288
|
+
availableRamBytes = (await readLinuxMemAvailableBytes()) ?? freemem();
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
availableRamBytes = freemem();
|
|
292
|
+
}
|
|
293
|
+
let vram;
|
|
294
|
+
if (overrides?.vram !== undefined) {
|
|
295
|
+
vram = overrides.vram;
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
// Try NVIDIA first, then AMD, then Apple-Silicon heuristic.
|
|
299
|
+
const nvidia = await detectNvidiaVram();
|
|
300
|
+
if (nvidia) {
|
|
301
|
+
vram = nvidia;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
const amd = await detectRocmVram();
|
|
305
|
+
if (amd) {
|
|
306
|
+
vram = amd;
|
|
307
|
+
}
|
|
308
|
+
else if (platform === 'darwin') {
|
|
309
|
+
// Apple Silicon unified memory: VRAM = total RAM (conservative floor).
|
|
310
|
+
vram = {
|
|
311
|
+
totalBytes: totalRamBytes,
|
|
312
|
+
freeBytes: availableRamBytes,
|
|
313
|
+
method: 'apple-unified',
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
vram = { totalBytes: null, freeBytes: null, method: 'none' };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return { totalRamBytes, availableRamBytes, vram, platform };
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Parse raw `/api/tags` response from Ollama.
|
|
325
|
+
*
|
|
326
|
+
* @internal
|
|
327
|
+
*/
|
|
328
|
+
function parseOllamaTagsResponse(body) {
|
|
329
|
+
if (typeof body !== 'object' ||
|
|
330
|
+
body === null ||
|
|
331
|
+
!Array.isArray(body['models'])) {
|
|
332
|
+
return [];
|
|
333
|
+
}
|
|
334
|
+
const models = body.models;
|
|
335
|
+
const result = [];
|
|
336
|
+
for (const m of models) {
|
|
337
|
+
if (typeof m !== 'object' || m === null)
|
|
338
|
+
continue;
|
|
339
|
+
const model = m;
|
|
340
|
+
const name = typeof model['name'] === 'string' ? model['name'] : '';
|
|
341
|
+
const details = typeof model['details'] === 'object' && model['details'] !== null
|
|
342
|
+
? model['details']
|
|
343
|
+
: {};
|
|
344
|
+
const parameterSize = typeof details['parameter_size'] === 'string' ? details['parameter_size'] : '';
|
|
345
|
+
const quantizationLevel = typeof details['quantization_level'] === 'string' ? details['quantization_level'] : '';
|
|
346
|
+
const family = typeof details['family'] === 'string' ? details['family'] : '';
|
|
347
|
+
if (name) {
|
|
348
|
+
result.push({ name, parameterSize, quantizationLevel, family });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Fetch the list of locally-pulled models from Ollama's `/api/tags` endpoint.
|
|
355
|
+
*
|
|
356
|
+
* Uses the HTTP API (not shelling out) — Gate-13 does not govern plain `fetch`
|
|
357
|
+
* calls to localhost.
|
|
358
|
+
*
|
|
359
|
+
* @param baseUrl - Ollama base URL (default `http://localhost:11434`).
|
|
360
|
+
* @param fetchFn - Injectable `fetch` for testing (defaults to global `fetch`).
|
|
361
|
+
* @returns Array of pulled models, or empty array if Ollama is not reachable.
|
|
362
|
+
*
|
|
363
|
+
* @task T11982
|
|
364
|
+
*/
|
|
365
|
+
export async function listOllamaPulledModels(baseUrl = 'http://localhost:11434', fetchFn = fetch) {
|
|
366
|
+
try {
|
|
367
|
+
const controller = new AbortController();
|
|
368
|
+
const timeout = setTimeout(() => controller.abort(), 1000);
|
|
369
|
+
let response;
|
|
370
|
+
try {
|
|
371
|
+
response = await fetchFn(`${baseUrl}/api/tags`, { signal: controller.signal });
|
|
372
|
+
}
|
|
373
|
+
finally {
|
|
374
|
+
clearTimeout(timeout);
|
|
375
|
+
}
|
|
376
|
+
if (!response.ok)
|
|
377
|
+
return [];
|
|
378
|
+
const body = await response.json();
|
|
379
|
+
return parseOllamaTagsResponse(body);
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
return [];
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
// Ranking algorithm
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
/**
|
|
389
|
+
* Hard RAM floor below which no local model recommendation is made.
|
|
390
|
+
* Machines under this threshold are steered toward cloud providers instead.
|
|
391
|
+
*/
|
|
392
|
+
export const LOCAL_FIT_FLOOR_GB = 4;
|
|
393
|
+
/** Score bonus when the model is already pulled locally. */
|
|
394
|
+
const ALREADY_PULLED_BONUS = 50;
|
|
395
|
+
/** Score bonus when VRAM is sufficient for full GPU offload. */
|
|
396
|
+
const VRAM_FULL_OFFLOAD_BONUS = 40;
|
|
397
|
+
/** Score bonus when VRAM meets the minimum (partial offload). */
|
|
398
|
+
const VRAM_PARTIAL_BONUS = 15;
|
|
399
|
+
/** Score bonus when available RAM exceeds the recommended threshold. */
|
|
400
|
+
const RAM_COMFORTABLE_BONUS = 20;
|
|
401
|
+
/** Score bonus when RAM is sufficient but below recommended. */
|
|
402
|
+
const RAM_SUFFICIENT_BONUS = 5;
|
|
403
|
+
/** Penalty when the model barely meets the minimum RAM. */
|
|
404
|
+
const RAM_TIGHT_PENALTY = -10;
|
|
405
|
+
/** Score bonus for models with longer context windows. */
|
|
406
|
+
const LONG_CONTEXT_BONUS = 8;
|
|
407
|
+
/**
|
|
408
|
+
* Compute the fit score for a candidate given this machine's hardware.
|
|
409
|
+
*
|
|
410
|
+
* @internal
|
|
411
|
+
*/
|
|
412
|
+
function scoreCandidate(candidate, snapshot, alreadyPulled) {
|
|
413
|
+
const ramGb = snapshot.totalRamBytes / 1024 ** 3;
|
|
414
|
+
const availableRamGb = snapshot.availableRamBytes / 1024 ** 3;
|
|
415
|
+
const vramTotalGb = snapshot.vram.totalBytes !== null ? snapshot.vram.totalBytes / 1024 ** 3 : null;
|
|
416
|
+
const vramFreeGb = snapshot.vram.freeBytes !== null ? snapshot.vram.freeBytes / 1024 ** 3 : null;
|
|
417
|
+
let score = 0;
|
|
418
|
+
const reasons = [];
|
|
419
|
+
let fitTier = 'marginal';
|
|
420
|
+
// VRAM scoring
|
|
421
|
+
if (vramTotalGb !== null && vramFreeGb !== null && candidate.minVramGb > 0) {
|
|
422
|
+
if (vramFreeGb >= candidate.recommendedVramGb) {
|
|
423
|
+
score += VRAM_FULL_OFFLOAD_BONUS;
|
|
424
|
+
reasons.push(`fits in ${vramFreeGb.toFixed(1)} GB free VRAM (recommended: ${candidate.recommendedVramGb} GB)`);
|
|
425
|
+
fitTier = 'excellent';
|
|
426
|
+
}
|
|
427
|
+
else if (vramFreeGb >= candidate.minVramGb) {
|
|
428
|
+
score += VRAM_PARTIAL_BONUS;
|
|
429
|
+
reasons.push(`partial GPU offload possible (${vramFreeGb.toFixed(1)} GB free VRAM, min: ${candidate.minVramGb} GB)`);
|
|
430
|
+
// fitTier is 'marginal' here (VRAM partial offload: 'good' is appropriate)
|
|
431
|
+
fitTier = 'good';
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
reasons.push(`CPU inference only — VRAM ${vramFreeGb.toFixed(1)} GB below model minimum ${candidate.minVramGb} GB`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else if (candidate.minVramGb === 0) {
|
|
438
|
+
reasons.push('optimised for CPU inference');
|
|
439
|
+
}
|
|
440
|
+
else if (snapshot.vram.method === 'none') {
|
|
441
|
+
reasons.push('no GPU detected — will use CPU inference');
|
|
442
|
+
}
|
|
443
|
+
// RAM scoring
|
|
444
|
+
if (availableRamGb >= candidate.recommendedRamGb) {
|
|
445
|
+
score += RAM_COMFORTABLE_BONUS;
|
|
446
|
+
reasons.push(`${availableRamGb.toFixed(1)} GB available RAM (recommended: ${candidate.recommendedRamGb} GB)`);
|
|
447
|
+
if (fitTier !== 'excellent')
|
|
448
|
+
fitTier = 'good';
|
|
449
|
+
}
|
|
450
|
+
else if (ramGb >= candidate.recommendedRamGb) {
|
|
451
|
+
score += RAM_SUFFICIENT_BONUS;
|
|
452
|
+
reasons.push(`${ramGb.toFixed(1)} GB total RAM meets recommendation (some swap may be used)`);
|
|
453
|
+
if (fitTier !== 'excellent')
|
|
454
|
+
fitTier = 'good';
|
|
455
|
+
}
|
|
456
|
+
else if (ramGb >= candidate.minRamGb) {
|
|
457
|
+
score += RAM_TIGHT_PENALTY;
|
|
458
|
+
reasons.push(`${ramGb.toFixed(1)} GB RAM meets minimum — expect slower inference (recommended: ${candidate.recommendedRamGb} GB)`);
|
|
459
|
+
// fitTier stays 'marginal'
|
|
460
|
+
}
|
|
461
|
+
// Context window bonus
|
|
462
|
+
if (candidate.contextLengthK >= 128) {
|
|
463
|
+
score += LONG_CONTEXT_BONUS;
|
|
464
|
+
reasons.push(`${candidate.contextLengthK}k context window`);
|
|
465
|
+
}
|
|
466
|
+
// Already-pulled bonus
|
|
467
|
+
if (alreadyPulled) {
|
|
468
|
+
score += ALREADY_PULLED_BONUS;
|
|
469
|
+
reasons.push('already pulled — no download needed');
|
|
470
|
+
}
|
|
471
|
+
return { score, reasons, fitTier };
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Rank the 2–3 best-fit local open-weight models for this machine.
|
|
475
|
+
*
|
|
476
|
+
* ## Floor
|
|
477
|
+
*
|
|
478
|
+
* Machines with total RAM below {@link LOCAL_FIT_FLOOR_GB} (4 GB) receive no
|
|
479
|
+
* recommendations. This deliberately excludes `qwen2:0.5b` (proof-of-life
|
|
480
|
+
* only) — it is absent from {@link LOCAL_MODEL_CANDIDATES}.
|
|
481
|
+
*
|
|
482
|
+
* ## Algorithm
|
|
483
|
+
*
|
|
484
|
+
* 1. Capture hardware snapshot.
|
|
485
|
+
* 2. Probe Ollama liveness and fetch the pulled-models list.
|
|
486
|
+
* 3. Filter candidates to those whose `minRamGb` ≤ total RAM.
|
|
487
|
+
* 4. Score remaining candidates (VRAM fit + RAM comfort + already-pulled bonus).
|
|
488
|
+
* 5. Return the top 3 (or fewer), ordered by score descending.
|
|
489
|
+
*
|
|
490
|
+
* @param opts - Override hardware snapshot for testing.
|
|
491
|
+
* @returns Fit envelope with ranked candidates.
|
|
492
|
+
*
|
|
493
|
+
* @task T11982
|
|
494
|
+
*/
|
|
495
|
+
export async function rankLocalModelFit(opts) {
|
|
496
|
+
const ollamaBaseUrl = opts?.ollamaBaseUrl ?? 'http://localhost:11434';
|
|
497
|
+
const [snapshot, ollamaRunning] = await Promise.all([
|
|
498
|
+
captureHardwareSnapshot(opts?.hardwareOverride),
|
|
499
|
+
probeOllamaAlive(ollamaBaseUrl),
|
|
500
|
+
]);
|
|
501
|
+
const pulledModels = opts?.pulledModelsOverride ??
|
|
502
|
+
(ollamaRunning ? await listOllamaPulledModels(ollamaBaseUrl, opts?.fetchFn) : []);
|
|
503
|
+
const pulledTags = new Set(pulledModels.map((m) => m.name));
|
|
504
|
+
const ramGb = snapshot.totalRamBytes / 1024 ** 3;
|
|
505
|
+
const hardware = {
|
|
506
|
+
totalRamGb: ramGb,
|
|
507
|
+
availableRamGb: snapshot.availableRamBytes / 1024 ** 3,
|
|
508
|
+
vramTotalGb: snapshot.vram.totalBytes !== null ? snapshot.vram.totalBytes / 1024 ** 3 : null,
|
|
509
|
+
vramFreeGb: snapshot.vram.freeBytes !== null ? snapshot.vram.freeBytes / 1024 ** 3 : null,
|
|
510
|
+
vramMethod: snapshot.vram.method,
|
|
511
|
+
};
|
|
512
|
+
// Hard floor: machines under LOCAL_FIT_FLOOR_GB get no recommendation.
|
|
513
|
+
if (ramGb < LOCAL_FIT_FLOOR_GB) {
|
|
514
|
+
logger.warn({ ramGb: ramGb.toFixed(1), floorGb: LOCAL_FIT_FLOOR_GB }, 'local-model-fit: RAM below floor — no local model recommended');
|
|
515
|
+
return {
|
|
516
|
+
hardware,
|
|
517
|
+
ollamaRunning,
|
|
518
|
+
pulledModels,
|
|
519
|
+
recommendations: [],
|
|
520
|
+
noRecommendationReason: `This machine has only ${ramGb.toFixed(1)} GB RAM. Local LLM inference requires at least ${LOCAL_FIT_FLOOR_GB} GB. Use a cloud provider instead.`,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
// Filter to candidates that at minimum fit in RAM.
|
|
524
|
+
const eligible = LOCAL_MODEL_CANDIDATES.filter((c) => ramGb >= c.minRamGb);
|
|
525
|
+
if (eligible.length === 0) {
|
|
526
|
+
return {
|
|
527
|
+
hardware,
|
|
528
|
+
ollamaRunning,
|
|
529
|
+
pulledModels,
|
|
530
|
+
recommendations: [],
|
|
531
|
+
noRecommendationReason: `No curated model fits the available RAM (${ramGb.toFixed(1)} GB).`,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
// Score every eligible candidate.
|
|
535
|
+
const scored = eligible.map((candidate) => {
|
|
536
|
+
const alreadyPulled = pulledTags.has(candidate.modelTag);
|
|
537
|
+
const { score, reasons, fitTier } = scoreCandidate(candidate, snapshot, alreadyPulled);
|
|
538
|
+
return {
|
|
539
|
+
candidate,
|
|
540
|
+
score,
|
|
541
|
+
alreadyPulled,
|
|
542
|
+
reasons,
|
|
543
|
+
fitTier,
|
|
544
|
+
pullCommand: `ollama pull ${candidate.modelTag}`,
|
|
545
|
+
};
|
|
546
|
+
});
|
|
547
|
+
// Sort by score descending, then by displayName ascending for stable tie-break.
|
|
548
|
+
scored.sort((a, b) => {
|
|
549
|
+
if (b.score !== a.score)
|
|
550
|
+
return b.score - a.score;
|
|
551
|
+
return a.candidate.modelTag.localeCompare(b.candidate.modelTag);
|
|
552
|
+
});
|
|
553
|
+
// Return top 3.
|
|
554
|
+
const recommendations = scored.slice(0, 3);
|
|
555
|
+
return {
|
|
556
|
+
hardware,
|
|
557
|
+
ollamaRunning,
|
|
558
|
+
pulledModels,
|
|
559
|
+
recommendations,
|
|
560
|
+
noRecommendationReason: null,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
//# sourceMappingURL=local-model-fit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-model-fit.js","sourceRoot":"","sources":["../../src/llm/local-model-fit.ts"],"names":[],"mappings":"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"}
|